2011-08-30 9 views
20

Quello che voglio fare

Sto cercando di utilizzare il Microsoft.Office.Interop.Excel namespace per aprire un file Excel (XSL o CSV, ma purtroppo non XSLX) e importarlo in un DataSet. Non ho il controllo sul nome del foglio di lavoro o dei nomi delle colonne, quindi devo permetterne le modifiche.Come si importa da Excel a un DataSet utilizzando Microsoft.Office.Interop.Excel?

Quello che ho provato

ho provato la OLEDB method di questo in passato, e aveva un sacco di problemi con esso (buggy, lento, e necessaria preventiva conoscenza dello schema del file Excel), in modo da Voglio evitare di farlo di nuovo. Quello che mi piacerebbe fare è utilizzare Microsoft.Office.Interop.Excel per importare la cartella di lavoro direttamente su un DataSet, o scorrere i fogli di lavoro e caricarli in un DataTable.

Che ci crediate o no, ho avuto problemi a trovare risorse per questo. A few searches on StackOverflow hanno trovato per lo più persone che tentano di eseguire il contrario (DataSet => Excel) o la tecnica OLEDB. Google non è stato molto più utile.

quello che ha finora

public void Load(string filename, Excel.XlFileFormat format = Excel.XlFileFormat.xlCSV) 
    { 
     app = new Excel.Application(); 
     book = app.Workbooks.Open(Filename: filename, Format: format); 

     DataSet ds = new DataSet(); 

     foreach (Excel.Worksheet sheet in book.Sheets) 
     { 
      DataTable dt = new DataTable(sheet.Name); 
      ds.Tables.Add(dt); 

      //??? Fill dt from sheet 
     } 

     this.Data = ds; 
    } 

Io sto bene con entrambi importare l'intero libro in una sola volta, o scorrendo un foglio alla volta. Posso farlo con Interop.Excel?

+0

Aggiunta di taglie perché mi piacerebbe davvero sapere se Interop.Excel dispone di questa funzionalità. Assegnerò la ricompensa se chiunque può ottenere i dati in questo modo, nel modo più automatico possibile, senza una conoscenza preliminare dei contenuti del file Excel. –

+0

Questo è possibile _if_ è possibile garantire qualcosa sui dati in anticipo. Ciò di cui ho paura è che vuoi che qualcosa funzioni per qualsiasi vecchia cartella di lavoro e inserisci i dati tabulari. Quei dati tabulari avrebbero bisogno di essere demarcati da intervalli denominati o avrebbero dovuto seguire una sorta di convenzione. Se seguiva la convenzione che ogni foglio nella cartella di lavoro sembrava esattamente come un set di record con una riga di intestazione nella riga 1, allora sarebbe possibile. Altrimenti, saresti sfortunato ... – adamleerich

risposta

31

E a proposito di usare Excel Data Reader (precedentemente ospitato here) un progetto open source su codeplex? Funziona molto bene per me esportare i dati dai fogli Excel.

Il codice di esempio dato sul link specificato:

FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read); 

//1. Reading from a binary Excel file ('97-2003 format; *.xls) 
IExcelDataReader excelReader = ExcelReaderFactory.CreateBinaryReader(stream); 
//... 
//2. Reading from a OpenXml Excel file (2007 format; *.xlsx) 
IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream); 
//... 
//3. DataSet - The result of each spreadsheet will be created in the result.Tables 
DataSet result = excelReader.AsDataSet(); 
//... 
//4. DataSet - Create column names from first row 
excelReader.IsFirstRowAsColumnNames = true; 
DataSet result = excelReader.AsDataSet(); 

//5. Data Reader methods 
while (excelReader.Read()) 
{ 
//excelReader.GetInt32(0); 
} 

//6. Free resources (IExcelDataReader is IDisposable) 
excelReader.Close(); 

UPDATE

Dopo un po 'di ricerca in giro, mi sono imbattuto in questo articolo: Faster MS Excel Reading using Office Interop Assemblies. L'articolo utilizza solo Office Interop Assemblies per leggere i dati da un determinato foglio di Excel. C'è anche il codice sorgente del progetto. Immagino che questo articolo possa essere un punto di partenza su ciò che stai cercando di ottenere. Vedere se questo aiuta

UPDATE 2

Il codice qui sotto prende un excel workbook e legge tutti i valori trovati, per ogni excel worksheet all'interno della excel workbook.

private static void TestExcel() 
    { 
     ApplicationClass app = new ApplicationClass(); 
     Workbook book = null; 
     Range range = null; 

     try 
     { 
      app.Visible = false; 
      app.ScreenUpdating = false; 
      app.DisplayAlerts = false; 

      string execPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase); 

      book = app.Workbooks.Open(@"C:\data.xls", Missing.Value, Missing.Value, Missing.Value 
               , Missing.Value, Missing.Value, Missing.Value, Missing.Value 
              , Missing.Value, Missing.Value, Missing.Value, Missing.Value 
              , Missing.Value, Missing.Value, Missing.Value); 
      foreach (Worksheet sheet in book.Worksheets) 
      { 

       Console.WriteLine(@"Values for Sheet "+sheet.Index); 

       // get a range to work with 
       range = sheet.get_Range("A1", Missing.Value); 
       // get the end of values to the right (will stop at the first empty cell) 
       range = range.get_End(XlDirection.xlToRight); 
       // get the end of values toward the bottom, looking in the last column (will stop at first empty cell) 
       range = range.get_End(XlDirection.xlDown); 

       // get the address of the bottom, right cell 
       string downAddress = range.get_Address(
        false, false, XlReferenceStyle.xlA1, 
        Type.Missing, Type.Missing); 

       // Get the range, then values from a1 
       range = sheet.get_Range("A1", downAddress); 
       object[,] values = (object[,]) range.Value2; 

       // View the values 
       Console.Write("\t"); 
       Console.WriteLine(); 
       for (int i = 1; i <= values.GetLength(0); i++) 
       { 
        for (int j = 1; j <= values.GetLength(1); j++) 
        { 
         Console.Write("{0}\t", values[i, j]); 
        } 
        Console.WriteLine(); 
       } 
      } 

     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e); 
     } 
     finally 
     { 
      range = null; 
      if (book != null) 
       book.Close(false, Missing.Value, Missing.Value); 
      book = null; 
      if (app != null) 
       app.Quit(); 
      app = null; 
     } 
    } 

Nel codice precedente, values[i, j] è il valore che è necessario aggiungere al dataset. i indica la riga, mentre j indica la colonna.

+0

Preferirei comunque utilizzare Interop.Excel se possibile, ma questo è un ottimo piano di backup. Grazie. –

+1

Se trovi qualcosa relativo a Interop.Excel, per favore pubblicalo qui. Sarebbe bello fare qualcosa basato anche su questo. – reggie

+0

@Justin Morgan Ho aggiornato la risposta con un collegamento a un articolo su CodeProject che utilizza solo Office Interop Assemblies. Fammi sapere se questo aiuta. – reggie

4

Hai visto questo? Da http://www.aspspider.com/resources/Resource510.aspx:

public DataTable Import(String path) 
{ 
    Microsoft.Office.Interop.Excel.ApplicationClass app = new Microsoft.Office.Interop.Excel.ApplicationClass(); 
    Microsoft.Office.Interop.Excel.Workbook workBook = app.Workbooks.Open(path, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0); 

    Microsoft.Office.Interop.Excel.Worksheet workSheet = (Microsoft.Office.Interop.Excel.Worksheet)workBook.ActiveSheet; 

    int index = 0; 
    object rowIndex = 2; 

    DataTable dt = new DataTable(); 
    dt.Columns.Add("FirstName"); 
    dt.Columns.Add("LastName"); 
    dt.Columns.Add("Mobile"); 
    dt.Columns.Add("Landline"); 
    dt.Columns.Add("Email"); 
    dt.Columns.Add("ID"); 

    DataRow row; 

    while (((Microsoft.Office.Interop.Excel.Range)workSheet.Cells[rowIndex, 1]).Value2 != null) 
    { 
     rowIndex = 2 + index; 
     row = dt.NewRow(); 
     row[0] = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)workSheet.Cells[rowIndex, 1]).Value2); 
     row[1] = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)workSheet.Cells[rowIndex, 2]).Value2); 
     row[2] = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)workSheet.Cells[rowIndex, 3]).Value2); 
     row[3] = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)workSheet.Cells[rowIndex, 4]).Value2); 
     row[4] = Convert.ToString(((Microsoft.Office.Interop.Excel.Range)workSheet.Cells[rowIndex, 5]).Value2); 
     index++; 
     dt.Rows.Add(row); 
    } 
    app.Workbooks.Close(); 
    return dt; 
} 
+0

Probabilmente potrei adattarlo. Non conosco il conteggio delle colonne o i tipi di dati in anticipo, quindi dovrei trovare una soluzione alternativa. Speravo che ci fosse qualcosa in Interop.Excel che avrebbe permesso la conversione diretta ad alcuni oggetti dati, ma prenderò ciò che posso ottenere. –

+0

Interop.Excel non consente la conversione diretta da un tipo di dati a un altro. Vi darà accesso all'oggetto Fogli in modo da poter accedere a ciascun foglio e quindi scorrere su ogni intervallo in quel foglio, ma questo è quanto. Una volta lì, dovrai scrivere il codice di conversione. – adamleerich

+0

Questo codice sembra piuttosto strutturato. Abbiamo un'estensione per aggiornare Excel nello stesso? – Shalem

4
object[,] valueArray = (object[,])excelRange.get_Value(XlRangeValueDataType.xlRangeValueDefault); 

//Get the column names 
for (int k = 0; k < valueArray.GetLength(1);) 
{ 
    //add columns to the data table. 
    dt.Columns.Add((string)valueArray[1,++k]); 
} 

//Load data into data table 
object[] singleDValue = new object[valueArray.GetLength(1)]; 
//value array first row contains column names. so loop starts from 1 instead of 0 
for (int i = 1; i < valueArray.GetLength(0); i++) 
{ 
    Console.WriteLine(valueArray.GetLength(0) + ":" + valueArray.GetLength(1)); 
    for (int k = 0; k < valueArray.GetLength(1);) 
    { 
     singleDValue[k] = valueArray[i+1, ++k]; 
    } 
    dt.LoadDataRow(singleDValue, System.Data.LoadOption.PreserveChanges); 
} 
+0

Bello e veloce. Grazie. –

1
using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Data; 
using System.Reflection; 
using Microsoft.Office.Interop.Excel; 

namespace trg.satmap.portal.ParseAgentSkillMapping 
{ 
    class ConvertXLStoDT 
    { 
     private StringBuilder errorMessages; 

     public StringBuilder ErrorMessages 
     { 
      get { return errorMessages; } 
      set { errorMessages = value; } 
     } 

     public ConvertXLStoDT() 
     { 
      ErrorMessages = new StringBuilder(); 
     } 

     public System.Data.DataTable XLStoDTusingInterOp(string FilePath) 
     { 
      #region Excel important Note. 
      /* 
      * Excel creates XLS and XLSX files. These files are hard to read in C# programs. 
      * They are handled with the Microsoft.Office.Interop.Excel assembly. 
      * This assembly sometimes creates performance issues. Step-by-step instructions are helpful. 
      * 
      * Add the Microsoft.Office.Interop.Excel assembly by going to Project -> Add Reference. 
      */ 
      #endregion 

      Microsoft.Office.Interop.Excel.Application excelApp = null; 
      Microsoft.Office.Interop.Excel.Workbook workbook = null; 


      System.Data.DataTable dt = new System.Data.DataTable(); //Creating datatable to read the content of the Sheet in File. 

      try 
      { 

       excelApp = new Microsoft.Office.Interop.Excel.Application(); // Initialize a new Excel reader. Must be integrated with an Excel interface object. 

       //Opening Excel file(myData.xlsx) 
       workbook = excelApp.Workbooks.Open(FilePath, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value, Missing.Value); 

       Microsoft.Office.Interop.Excel.Worksheet ws = (Microsoft.Office.Interop.Excel.Worksheet)workbook.Sheets.get_Item(1); 

       Microsoft.Office.Interop.Excel.Range excelRange = ws.UsedRange; //gives the used cells in sheet 

       ws = null; // now No need of this so should expire. 

       //Reading Excel file.    
       object[,] valueArray = (object[,])excelRange.get_Value(Microsoft.Office.Interop.Excel.XlRangeValueDataType.xlRangeValueDefault); 

       excelRange = null; // you don't need to do any more Interop. Now No need of this so should expire. 

       dt = ProcessObjects(valueArray);     

      } 
      catch (Exception ex) 
      { 
       ErrorMessages.Append(ex.Message); 
      } 
      finally 
      { 
       #region Clean Up     
       if (workbook != null) 
       { 
        #region Clean Up Close the workbook and release all the memory. 
        workbook.Close(false, FilePath, Missing.Value);      
        System.Runtime.InteropServices.Marshal.ReleaseComObject(workbook); 
        #endregion 
       } 
       workbook = null; 

       if (excelApp != null) 
       { 
        excelApp.Quit(); 
       } 
       excelApp = null;     

       #endregion 
      } 
      return (dt); 
     } 

     /// <summary> 
     /// Scan the selected Excel workbook and store the information in the cells 
     /// for this workbook in an object[,] array. Then, call another method 
     /// to process the data. 
     /// </summary> 
     private void ExcelScanIntenal(Microsoft.Office.Interop.Excel.Workbook workBookIn) 
     { 
      // 
      // Get sheet Count and store the number of sheets. 
      // 
      int numSheets = workBookIn.Sheets.Count; 

      // 
      // Iterate through the sheets. They are indexed starting at 1. 
      // 
      for (int sheetNum = 1; sheetNum < numSheets + 1; sheetNum++) 
      { 
       Worksheet sheet = (Worksheet)workBookIn.Sheets[sheetNum]; 

       // 
       // Take the used range of the sheet. Finally, get an object array of all 
       // of the cells in the sheet (their values). You can do things with those 
       // values. See notes about compatibility. 
       // 
       Range excelRange = sheet.UsedRange; 
       object[,] valueArray = (object[,])excelRange.get_Value(XlRangeValueDataType.xlRangeValueDefault); 

       // 
       // Do something with the data in the array with a custom method. 
       // 
       ProcessObjects(valueArray); 
      } 
     } 
     private System.Data.DataTable ProcessObjects(object[,] valueArray) 
     { 
      System.Data.DataTable dt = new System.Data.DataTable(); 

      #region Get the COLUMN names 

      for (int k = 1; k <= valueArray.GetLength(1); k++) 
      { 
       dt.Columns.Add((string)valueArray[1, k]); //add columns to the data table. 
      } 
      #endregion 

      #region Load Excel SHEET DATA into data table 

      object[] singleDValue = new object[valueArray.GetLength(1)]; 
      //value array first row contains column names. so loop starts from 2 instead of 1 
      for (int i = 2; i <= valueArray.GetLength(0); i++) 
      { 
       for (int j = 0; j < valueArray.GetLength(1); j++) 
       { 
        if (valueArray[i, j + 1] != null) 
        { 
         singleDValue[j] = valueArray[i, j + 1].ToString(); 
        } 
        else 
        { 
         singleDValue[j] = valueArray[i, j + 1]; 
        } 
       } 
       dt.LoadDataRow(singleDValue, System.Data.LoadOption.PreserveChanges); 
      } 
      #endregion 


      return (dt); 
     } 
    } 
}