2009-02-20 21 views
5

He estado utilizando una conexión OleDb para leer archivos de Excel con éxito desde hace bastante tiempo, pero me he encontrado con un problema. Tengo a alguien que intenta subir una hoja de cálculo de Excel sin nada en la primera columna y cuando intento leer el archivo, no reconoce esa columna.¿Cómo leo un archivo de Excel en C# sin perder ninguna columna?

Actualmente estoy usando la siguiente cadena de conexión OLEDB:

Provider = Microsoft.Jet.OleDb.4.0;
Fuente de datos = c: \ test.xls;
Propiedades extendidas = "Excel 8.0; IMEX = 1;"

Por lo tanto, si hay 13 columnas en el archivo de Excel, el OleDbDataReader que obtengo solo tiene 12 columnas/campos.

Cualquier apreciación sería apreciada.

+0

Si no hay nada en la primera columna, ¿cuál es el problema? – StingyJack

Respuesta

3

SpreadsheetGear for .NET le da una API para trabajar con xls y xlsx libros de trabajo de .NET. Es más fácil de usar y más rápido que OleDB o el modelo de objetos COM de Excel. Puede ver el live samples o probarlo con el free trial.

exención de responsabilidad: Tengo SpreadsheetGear LLC

EDIT:

StingyJack comentó " Más rápido que OleDb mejor atrás, que reclamar hasta?".

Esta es una solicitud razonable. Veo todo el tiempo afirmaciones que sé que son falsas, así que no puedo culpar a nadie por ser escéptico.

A continuación se muestra el código para crear un libro de trabajo de 50,000 filas por 10 columnas con SpreadsheetGear, guárdelo en el disco, y luego sume los números usando OleDb y SpreadsheetGear.SpreadsheetGear lee las 500K celdas en 0.31 segundos en comparación con 0.63 segundos con OleDB, algo más del doble de rápido. SpreadsheetGear realmente crea y lee el libro de trabajo en menos tiempo de lo que lleva leer el libro de trabajo con OleDB.

El código está debajo. Puede probarlo usted mismo con la versión de prueba gratuita de SpreadsheetGear.

using System; 
using System.Data; 
using System.Data.OleDb; 
using SpreadsheetGear; 
using SpreadsheetGear.Advanced.Cells; 
using System.Diagnostics; 

namespace SpreadsheetGearAndOleDBBenchmark 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      // Warm up (get the code JITed). 
      BM(10, 10); 

      // Do it for real. 
      BM(50000, 10); 
     } 

     static void BM(int rows, int cols) 
     { 
      // Compare the performance of OleDB to SpreadsheetGear for reading 
      // workbooks. We sum numbers just to have something to do. 
      // 
      // Run on Windows Vista 32 bit, Visual Studio 2008, Release Build, 
      // Run Without Debugger: 
      // Create time: 0.25 seconds 
      // OleDb Time: 0.63 seconds 
      // SpreadsheetGear Time: 0.31 seconds 
      // 
      // SpreadsheetGear is more than twice as fast at reading. Furthermore, 
      // SpreadsheetGear can create the file and read it faster than OleDB 
      // can just read it. 
      string filename = @"C:\tmp\SpreadsheetGearOleDbBenchmark.xls"; 
      Console.WriteLine("\nCreating {0} rows x {1} columns", rows, cols); 
      Stopwatch timer = Stopwatch.StartNew(); 
      double createSum = CreateWorkbook(filename, rows, cols); 
      double createTime = timer.Elapsed.TotalSeconds; 
      Console.WriteLine("Create sum of {0} took {1} seconds.", createSum, createTime); 
      timer = Stopwatch.StartNew(); 
      double oleDbSum = ReadWithOleDB(filename); 
      double oleDbTime = timer.Elapsed.TotalSeconds; 
      Console.WriteLine("OleDb sum of {0} took {1} seconds.", oleDbSum, oleDbTime); 
      timer = Stopwatch.StartNew(); 
      double spreadsheetGearSum = ReadWithSpreadsheetGear(filename); 
      double spreadsheetGearTime = timer.Elapsed.TotalSeconds; 
      Console.WriteLine("SpreadsheetGear sum of {0} took {1} seconds.", spreadsheetGearSum, spreadsheetGearTime); 
     } 

     static double CreateWorkbook(string filename, int rows, int cols) 
     { 
      IWorkbook workbook = Factory.GetWorkbook(); 
      IWorksheet worksheet = workbook.Worksheets[0]; 
      IValues values = (IValues)worksheet; 
      double sum = 0.0; 
      Random rand = new Random(); 
      // Put labels in the first row. 
      foreach (IRange cell in worksheet.Cells[0, 0, 0, cols - 1]) 
       cell.Value = "Cell-" + cell.Address; 
      // Using IRange and foreach be less code, 
      // but we'll do it the fast way. 
      for (int row = 1; row <= rows; row++) 
      { 
       for (int col = 0; col < cols; col++) 
       { 
        double number = rand.NextDouble(); 
        sum += number; 
        values.SetNumber(row, col, number); 
       } 
      } 
      workbook.SaveAs(filename, FileFormat.Excel8); 
      return sum; 
     } 

     static double ReadWithSpreadsheetGear(string filename) 
     { 
      IWorkbook workbook = Factory.GetWorkbook(filename); 
      IWorksheet worksheet = workbook.Worksheets[0]; 
      IValues values = (IValues)worksheet; 
      IRange usedRahge = worksheet.UsedRange; 
      int rowCount = usedRahge.RowCount; 
      int colCount = usedRahge.ColumnCount; 
      double sum = 0.0; 
      // We could use foreach (IRange cell in usedRange) for cleaner 
      // code, but this is faster. 
      for (int row = 1; row <= rowCount; row++) 
      { 
       for (int col = 0; col < colCount; col++) 
       { 
        IValue value = values[row, col]; 
        if (value != null && value.Type == SpreadsheetGear.Advanced.Cells.ValueType.Number) 
         sum += value.Number; 
       } 
      } 
      return sum; 
     } 

     static double ReadWithOleDB(string filename) 
     { 
      String connectionString = 
       "Provider=Microsoft.Jet.OLEDB.4.0;" + 
       "Data Source=" + filename + ";" + 
       "Extended Properties=Excel 8.0;"; 
      OleDbConnection connection = new OleDbConnection(connectionString); 
      connection.Open(); 
      OleDbCommand selectCommand =new OleDbCommand("SELECT * FROM [Sheet1$]", connection); 
      OleDbDataAdapter dataAdapter = new OleDbDataAdapter(); 
      dataAdapter.SelectCommand = selectCommand; 
      DataSet dataSet = new DataSet(); 
      dataAdapter.Fill(dataSet); 
      connection.Close(); 
      double sum = 0.0; 
      // We'll make some assumptions for brevity of the code. 
      DataTable dataTable = dataSet.Tables[0]; 
      int cols = dataTable.Columns.Count; 
      foreach (DataRow row in dataTable.Rows) 
      { 
       for (int i = 0; i < cols; i++) 
       { 
        object val = row[i]; 
        if (val is double) 
         sum += (double)val; 
       } 
      } 
      return sum; 
     } 
    } 
} 
+0

Más rápido que OleDb? Es mejor que vuelvas a reclamar. – StingyJack

+0

StingyJack: No te culpo por ser escéptico. He editado mi respuesta con un código que demuestra que SpreadsheetGear es más rápido que OleDb. –

+0

Mis números eran un poco diferentes, pero razonablemente cercanos. Realmente deberías publicar eso en tu sitio. Al comprar un componente xls, esta sería realmente información para ver. – StingyJack

1

Siempre utilizamos Excel Interop para abrir la hoja de cálculo y analizar directamente (por ejemplo, similar a cómo escanearía las celdas en VBA), o creamos plantillas bloqueadas que imponen ciertas columnas para rellenar antes de que el usuario pueda guardar la datos.

+0

Vaya con la biblioteca de interoperabilidad. LL tiene razón. – KevDog

0

Si pudiera requerirse que el formato de la hoja de Excel tenga encabezados de columna, siempre tendrá las 13 columnas. Solo necesitaría omitir la fila de encabezado cuando procese.

Esto también corregiría las situaciones en las que el usuario coloca las columnas en un orden que no está esperando. (detectar índices de columna en la fila de encabezado y leer apropiadamente)

Veo que otros recomiendan la interoperación de Excel, pero por Dios es una opción lenta en comparación con la forma de OleDb. Además, requiere que Excel o OWC estén instalados en el servidor (licencias).

+0

Los archivos actualmente tienen una fila de encabezado. Incluso cuando le digo a OleDb que incluya la fila del encabezado (usando HDR = NO), solo devuelve 12 columnas y omite la primera columna. – Austin

+0

Esa opción HDR suena al revés ... verifique (http://www.connectionstrings.com/excel) como referencia para la cadena de conexión. – StingyJack

+0

Sé que suena al revés, pero estableces HDR = NO para decirle que te dé la fila del encabezado (básicamente, estás diciendo que la fila del encabezado es una fila de datos) – Austin

0

Puede intentar usar Excel y COM. De esa forma, obtendrás tu información directamente de la boca del caballo, por así decirlo.

De D. Anand Más en los foros de MSDN:

crear una referencia en su proyecto a Excel objetos de biblioteca. La biblioteca de objetos de Excel se puede agregar en la pestaña COM para agregar el cuadro de diálogo de referencia.

Aquí hay algo de información sobre el modelo de objetos de Excel en C# http://msdn.microsoft.com/en-us/library/aa168292(office.11).aspx

+0

La boca de los caballos tarda un rato en masticar, por lo que no será tan buena para archivos grandes (> 1000 filas). – StingyJack

+1

También la boca de los caballos no funciona bien en un entorno de servidor si este es un entorno de servidor – JoshBerke

+0

Sí, me gustaría evitar ir por la ruta COM o Interop debido al problema de velocidad; pero eso puede ser lo que tenemos que hacer. ¿Alguna otra idea antes de ir por ese camino? – Austin

0

¡Te recomiendo que pruebes Visual Studio Tools para Office y Excel Interop! Está usando es muy fácil.

1

Probablemente pueda mirar ExcelMapper. Es una herramienta para leer archivos de Excel como objetos fuertemente tipados. Oculta todos los detalles de la lectura de un excel de su código. Sería importante que a su Excel le falte una columna o que falten datos en una columna. Usted lee los datos que le interesan. Puede obtener el código/ejecutable para ExcelMapper en http://code.google.com/p/excelmapper/.

Cuestiones relacionadas