2013-07-10 12 views
21

Sto cercando di leggere un file Excel in un elenco di Data.DataTable, anche se con il mio metodo corrente può richiedere molto tempo. Presto essenzialmente Foglio di lavoro per foglio di lavoro, cella per cella, e tende a richiedere molto tempo. C'è un modo più veloce per farlo? Ecco il mio codice:Importazione rapida di un DataTable in Excel

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

Purtroppo. – gustavodidomenico

+0

"C'è un modo più veloce per farlo? Sfortunatamente no." Spazzatura assoluta. Questo codice crea (e erroneamente non riesce a disporre) un oggetto COM per ogni singolo valore di cella Excel in cui viene letto. Questo è il modo più lento possibile di farlo! È MOLTO più veloce leggere nell'intero foglio di lavoro in un array in un colpo solo, quindi scorrere gli elementi in quell'array. –

risposta

12

caling .Value2 è un'operazione costosa perché è una chiamata COM-Interop . Vorrei invece letto l'intera gamma in un array e poi scorrere l'array:

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

Funziona ancora perfettamente. Ho registrato 40k e il tempo di elaborazione è diminuito da circa 2 minuti a circa 2 secondi. –

+1

Sono seriamente confuso circa gli usi variabili nella risposta. Non sembra essere un noob friendly. 1. Non potevo usare 'Range.Value2' in quel luogo, mostra errore come" non può convertire implicitamente oggetto [] in oggetto [*, *] ". 2. Non sono sicuro della variabile 'Convertval'. – parkourkarthik

+0

@parkourkarthik Al momento non posso verificarlo, ma se il tuo intervallo è una singola riga o colonna potresti ottenere un oggetto '1 []' indietro, anche se pensavo che fosse sempre un array 2-d. Sentiti libero di chiederlo come una domanda a parte se non l'hai già fatto. –

3

MS Office Interop è lento e anche Microsoft sconsiglia l'utilizzo di interoperabilità sul lato server e non può essere utilizzato per importare i file di grandi dimensioni di Excel. Per ulteriori dettagli, vedere why not to use OLE Automation dal punto di vista di Microsoft.

Invece, è possibile utilizzare qualsiasi libreria di Excel, ad esempio EasyXLS. Questo è un esempio di codice che mostra come leggere il file di Excel:

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

Se il file di Excel dispone di più fogli o per l'importazione solo intervalli di celle (per le prestazioni migliori) dare un'occhiata a più esempi di codice su how to import Excel to DataTable in C# using EasyXLS.

+5

Ouch. Una libreria $ 195, solo per leggere in un foglio di lavoro di Excel? –

2

Nel caso in cui qualcun altro stia utilizzando EPPlus. Questa implementazione è piuttosto ingenua, ma ci sono commenti che richiamano l'attenzione su questo. Se dovessi sovrapporre un altro metodo, GetWorkbookAsDataSet(), farebbe quello che l'OP sta chiedendo.

/// <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

Questo codice ha aiutato. Risolto il mio problema Molte grazie. – 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() 
non
Problemi correlati