2013-07-10 93 views
21

Tôi đang cố đọc một tệp Excel vào danh sách Data.DataTable, mặc dù với phương pháp hiện tại của tôi, có thể mất một thời gian rất dài. Tôi thường xuyên đi đến Worksheet by Worksheet, di động bằng ô, và nó có xu hướng mất một thời gian rất dài. Có cách nào nhanh hơn để làm việc này không? Đây là mã của tôi:Nhập Excel vào một DataTable nhanh chóng

List<DataTable> List = new List<DataTable>(); 

    // Counting sheets 
    for (int count = 1; count < WB.Worksheets.Count; ++count) 
    { 
     // Create a new DataTable for every Worksheet 
     DATA.DataTable DT = new DataTable(); 

     WS = (EXCEL.Worksheet)WB.Worksheets.get_Item(count); 

     textBox1.Text = count.ToString(); 

     // Get range of the worksheet 
     Range = WS.UsedRange; 


     // Create new Column in DataTable 
     for (cCnt = 1; cCnt <= Range.Columns.Count; cCnt++) 
     { 
      textBox3.Text = cCnt.ToString(); 


       Column = new DataColumn(); 
       Column.DataType = System.Type.GetType("System.String"); 
       Column.ColumnName = cCnt.ToString(); 
       DT.Columns.Add(Column); 

      // Create row for Data Table 
      for (rCnt = 0; rCnt <= Range.Rows.Count; rCnt++) 
      { 
       textBox2.Text = rCnt.ToString(); 

       try 
       { 
        cellVal = (string)(Range.Cells[rCnt, cCnt] as EXCEL.Range).Value2; 
       } 
       catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) 
       { 
        ConvertVal = (double)(Range.Cells[rCnt, cCnt] as EXCEL.Range).Value2; 
        cellVal = ConvertVal.ToString(); 
       } 

       // Add to the DataTable 
       if (cCnt == 1) 
       { 

        Row = DT.NewRow(); 
        Row[cCnt.ToString()] = cellVal; 
        DT.Rows.Add(Row); 
       } 
       else 
       { 

        Row = DT.Rows[rCnt]; 
        Row[cCnt.ToString()] = cellVal; 

       } 
      } 
     } 
     // Add DT to the list. Then go to the next sheet in the Excel Workbook 
     List.Add(DT); 
    } 
+0

Đáng tiếc là không. – gustavodidomenico

+0

"Có cách nào nhanh hơn để làm việc này không? Thật không may là không." Rác tuyệt đối. Mã này đang tạo (và nhầm lẫn không vứt bỏ) một đối tượng COM cho mỗi giá trị ô Excel duy nhất mà nó đọc. Đây là cách làm chậm nhất có thể! Đó là MUCH nhanh hơn để đọc trong toàn bộ bảng tính thành một mảng trong một lần, sau đó lặp qua các mục trong mảng đó. –

Trả lời

12

Caling .Value2 là một hoạt động tốn kém bởi vì đó là một cuộc gọi COM-interop . Tôi thay vào đó sẽ đọc toàn bộ phạm vi thành một mảng và sau đó lặp qua mảng:

object[,] data = Range.Value2; 

// Create new Column in DataTable 
for (int cCnt = 1; cCnt <= Range.Columns.Count; cCnt++) 
{ 
    textBox3.Text = cCnt.ToString(); 

    var Column = new DataColumn(); 
    Column.DataType = System.Type.GetType("System.String"); 
    Column.ColumnName = cCnt.ToString(); 
    DT.Columns.Add(Column); 

    // Create row for Data Table 
    for (int rCnt = 0; rCnt <= Range.Rows.Count; rCnt++) 
    { 
     textBox2.Text = rCnt.ToString(); 

     string CellVal = String.Empty; 
     try 
     { 
      cellVal = (string)(data[rCnt, cCnt]); 
     } 
     catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) 
     { 
      ConvertVal = (double)(data[rCnt, cCnt]); 
      cellVal = ConvertVal.ToString(); 
     } 

     DataRow Row; 

     // Add to the DataTable 
     if (cCnt == 1) 
     { 

      Row = DT.NewRow(); 
      Row[cCnt.ToString()] = cellVal; 
      DT.Rows.Add(Row); 
     } 
     else 
     { 

      Row = DT.Rows[rCnt]; 
      Row[cCnt.ToString()] = cellVal; 

     } 
    } 
} 
+0

Điều này vẫn hoạt động hoàn hảo. Tôi đã có 40k hồ sơ và thời gian xử lý giảm từ khoảng 2 phút xuống còn khoảng 2 giây. –

+1

Tôi thực sự bối rối về việc sử dụng biến số trong câu trả lời. Dường như nó không thân thiện. 1. Tôi không thể sử dụng 'Range.Value2'in nơi đó, nó cho thấy lỗi là" không thể chuyển đổi hoàn toàn đối tượng [] thành đối tượng [*, *] ". 2. Tôi không chắc chắn về biến 'Convertval'. – parkourkarthik

+0

@parkourkarthik Tôi không thể xác minh ngay bây giờ nhưng nếu phạm vi của bạn là một hàng hoặc cột, bạn có thể nhận được đối tượng 1-D '[] trở lại, mặc dù tôi nghĩ rằng đó luôn là mảng 2 chiều. Vui lòng hỏi đó là câu hỏi riêng nếu bạn chưa có. –

3

MS Office Interop là chậm và thậm chí Microsoft không khuyên bạn sử dụng Interop trên phía máy chủ và không thể được sử dụng để nhập các tập tin Excel lớn. Để biết thêm chi tiết, xem why not to use OLE Automation từ quan điểm của Microsoft.

Thay vào đó, bạn có thể sử dụng bất kỳ thư viện Excel nào, chẳng hạn như EasyXLS chẳng hạn. Đây là một mẫu mã cho thấy làm thế nào để đọc các file Excel:

ExcelDocument workbook = new ExcelDocument(); 
DataSet ds = workbook.easy_ReadXLSActiveSheet_AsDataSet("excel.xls"); 
DataTable dataTable = ds.Tables[0]; 

Nếu file Excel của bạn có nhiều tờ hoặc nhập khẩu chỉ dao động của các tế bào (các buổi biểu diễn tốt hơn) hãy nhìn đến mẫu mã thêm về how to import Excel to DataTable in C# using EasyXLS.

+5

Ouch. Một thư viện $ 195, chỉ để đọc trong một bảng tính Excel? –

2

Trong trường hợp bất kỳ ai khác đang sử dụng EPPlus. Việc triển khai này khá ngây thơ, nhưng có những nhận xét thu hút sự chú ý đến như vậy. Nếu bạn muốn thêm một phương thức nữa là GetWorkbookAsDataSet() trên đầu nó sẽ làm những gì OP yêu cầu.

/// <summary> 
    /// Assumption: Worksheet is in table format with no weird padding or blank column headers. 
    /// 
    /// Assertion: Duplicate column names will be aliased by appending a sequence number (eg. Column, Column1, Column2) 
    /// </summary> 
    /// <param name="worksheet"></param> 
    /// <returns></returns> 
    public static DataTable GetWorksheetAsDataTable(ExcelWorksheet worksheet) 
    { 
     var dt = new DataTable(worksheet.Name); 
     dt.Columns.AddRange(GetDataColumns(worksheet).ToArray()); 
     var headerOffset = 1; //have to skip header row 
     var width = dt.Columns.Count; 
     var depth = GetTableDepth(worksheet, headerOffset); 
     for (var i = 1; i <= depth; i++) 
     { 
      var row = dt.NewRow(); 
      for (var j = 1; j <= width; j++) 
      { 
       var currentValue = worksheet.Cells[i + headerOffset, j].Value; 

       //have to decrement b/c excel is 1 based and datatable is 0 based. 
       row[j - 1] = currentValue == null ? null : currentValue.ToString(); 
      } 

      dt.Rows.Add(row); 
     } 

     return dt; 
    } 

    /// <summary> 
    /// Assumption: There are no null or empty cells in the first column 
    /// </summary> 
    /// <param name="worksheet"></param> 
    /// <returns></returns> 
    private static int GetTableDepth(ExcelWorksheet worksheet, int headerOffset) 
    { 
     var i = 1; 
     var j = 1; 
     var cellValue = worksheet.Cells[i + headerOffset, j].Value; 
     while (cellValue != null) 
     { 
      i++; 
      cellValue = worksheet.Cells[i + headerOffset, j].Value; 
     } 

     return i - 1; //subtract one because we're going from rownumber (1 based) to depth (0 based) 
    } 

    private static IEnumerable<DataColumn> GetDataColumns(ExcelWorksheet worksheet) 
    { 
     return GatherColumnNames(worksheet).Select(x => new DataColumn(x)); 
    } 

    private static IEnumerable<string> GatherColumnNames(ExcelWorksheet worksheet) 
    { 
     var columns = new List<string>(); 

     var i = 1; 
     var j = 1; 
     var columnName = worksheet.Cells[i, j].Value; 
     while (columnName != null) 
     { 
      columns.Add(GetUniqueColumnName(columns, columnName.ToString())); 
      j++; 
      columnName = worksheet.Cells[i, j].Value; 
     } 

     return columns; 
    } 

    private static string GetUniqueColumnName(IEnumerable<string> columnNames, string columnName) 
    { 
     var colName = columnName; 
     var i = 1; 
     while (columnNames.Contains(colName)) 
     { 
      colName = columnName + i.ToString(); 
      i++; 
     } 

     return colName; 
    } 
+0

Mã này đã giúp. Giải quyết vấn đề của tôi. Cảm ơn rất nhiều. – Aditi

1
Dim sSheetName As String 
Dim sConnection As String 
Dim dtTablesList As DataTable 
Dim oleExcelCommand As OleDbCommand 
Dim oleExcelReader As OleDbDataReader 
Dim oleExcelConnection As OleDbConnection 

sConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Test.xls;Extended Properties=""Excel 12.0;HDR=No;IMEX=1""" 

oleExcelConnection = New OleDbConnection(sConnection) 
oleExcelConnection.Open() 

dtTablesList = oleExcelConnection.GetSchema("Tables") 

If dtTablesList.Rows.Count > 0 Then 
    sSheetName = dtTablesList.Rows(0)("TABLE_NAME").ToString 
End If 

dtTablesList.Clear() 
dtTablesList.Dispose() 

If sSheetName <> "" Then 

    oleExcelCommand = oleExcelConnection.CreateCommand() 
    oleExcelCommand.CommandText = "Select * From [" & sSheetName & "]" 
    oleExcelCommand.CommandType = CommandType.Text 

    oleExcelReader = oleExcelCommand.ExecuteReader 

    nOutputRow = 0 

    While oleExcelReader.Read 

    End While 

    oleExcelReader.Close() 

End If 

oleExcelConnection.Close()