2009-07-22 51 views
95

Estoy usando OleDb para leer de un libro de Excel con muchas hojas.Usando Excel OleDb para obtener nombres de hoja EN ORDEN DE HOJA

Necesito leer los nombres de las hojas, pero las necesito en el orden en que están definidas en la hoja de cálculo; entonces si tengo un archivo que se ve así;

|_____|_____|____|____|____|____|____|____|____| 
|_____|_____|____|____|____|____|____|____|____| 
|_____|_____|____|____|____|____|____|____|____| 
\__GERMANY__/\__UK__/\__IRELAND__/ 

entonces necesito para obtener el diccionario

1="GERMANY", 
2="UK", 
3="IRELAND" 

He intentado usar OleDbConnection.GetOleDbSchemaTable(), y eso me da la lista de nombres, pero los ordena alfabéticamente. La ordenación alfa significa que no sé a qué número de hoja corresponde un nombre particular. Entonces lo entiendo;

GERMANY, IRELAND, UK 

que ha cambiado el orden de UK y IRELAND.

La razón por la que necesito que se ordene es porque tengo que dejar que el usuario elija un rango de datos por nombre o índice; pueden solicitar "todos los datos de ALEMANIA a IRLANDA" o "datos de la hoja 1 a la hoja 3".

Cualquier idea sería muy apreciada.

si pudiera usar las clases de interoperabilidad de oficina, esto sería sencillo. Desafortunadamente, no puedo porque las clases de interoperabilidad no funcionan de manera confiable en entornos no interactivos como los servicios de Windows y los sitios ASP.NET, así que necesitaba usar OLEDB.

+0

¿Qué versión del archivo de Excel está leyendo? – yamen

+24

wow cómo dibujaste eso y cómo tuviste la paciencia para dibujar ese –

+2

@steve puedes por favor responder mi pregunta –

Respuesta

16

No se puede encontrar esto en la documentación de MSDN real, pero un moderador en los foros dicho

Me temo que OLEDB no conserva el orden de las hojas como estaban en Excel

Excel Sheet Names in Sheet Order

Parece que esto sería un requisito bastante común que habría una solución decente.

+0

Sin embargo, esto respondió directamente, ahorró mucho tiempo en intentos innecesarios. –

69

¿No puede simplemente pasar entre las hojas de 0 a Conde de nombres -1? de esa manera, debes obtenerlos en el orden correcto.

Editar

me di cuenta a través de los comentarios que hay un montón de preocupaciones sobre el uso de las clases de interoperabilidad para recuperar los nombres de las hojas. Por lo tanto, aquí hay un ejemplo usando OLEDB para recuperarlos:

/// <summary> 
/// This method retrieves the excel sheet names from 
/// an excel workbook. 
/// </summary> 
/// <param name="excelFile">The excel file.</param> 
/// <returns>String[]</returns> 
private String[] GetExcelSheetNames(string excelFile) 
{ 
    OleDbConnection objConn = null; 
    System.Data.DataTable dt = null; 

    try 
    { 
     // Connection String. Change the excel file to the file you 
     // will search. 
     String connString = "Provider=Microsoft.Jet.OLEDB.4.0;" + 
      "Data Source=" + excelFile + ";Extended Properties=Excel 8.0;"; 
     // Create connection object by using the preceding connection string. 
     objConn = new OleDbConnection(connString); 
     // Open connection with the database. 
     objConn.Open(); 
     // Get the data table containg the schema guid. 
     dt = objConn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null); 

     if(dt == null) 
     { 
      return null; 
     } 

     String[] excelSheets = new String[dt.Rows.Count]; 
     int i = 0; 

     // Add the sheet name to the string array. 
     foreach(DataRow row in dt.Rows) 
     { 
      excelSheets[i] = row["TABLE_NAME"].ToString(); 
      i++; 
     } 

     // Loop through all of the sheets if you want too... 
     for(int j=0; j < excelSheets.Length; j++) 
     { 
      // Query each excel sheet. 
     } 

     return excelSheets; 
    } 
    catch(Exception ex) 
    { 
     return null; 
    } 
    finally 
    { 
     // Clean up. 
     if(objConn != null) 
     { 
      objConn.Close(); 
      objConn.Dispose(); 
     } 
     if(dt != null) 
     { 
      dt.Dispose(); 
     } 
    } 
} 

Extraído de Article en el CodeProject.

+0

¡Ese es el código que me gustaría ver! ¿Cómo se puede consultar la 'hoja enésima' y el número de hojas? –

+0

Usted dice que está recuperando la lista de nombres de la tabla de pruebas GetOledb ... por lo tanto, tiene el recuento de hojas en el libro de trabajo. Así que es solo un caso de pasar de 0 a contar los nombres de las hojas-1. Personalmente, ni siquiera leía los nombres de las hojas, simplemente recorría las hojas y obtenía la propiedad Nombre del objeto Hoja de trabajo ... – James

+0

Esto no funcionará a menos que sepa el nombre de la primera hoja. – eviljack

1

Esto funcionó para mí. Robado de aquí: How do you get the name of the first page of an excel workbook?

 object opt = System.Reflection.Missing.Value; 
     Excel.Application app = new Microsoft.Office.Interop.Excel.Application(); 
     Excel.Workbook workbook = app.Workbooks.Open(WorkBookToOpen, 
               opt, opt, opt, opt, opt, opt, opt, 
               opt, opt, opt, opt, opt, opt, opt); 
     Excel.Worksheet worksheet = workbook.Worksheets[1] as Microsoft.Office.Interop.Excel.Worksheet; 
     string firstSheetName = worksheet.Name; 
+2

Hola. Me alegro de que tenga código de trabajo, pero eso usa las clases Interop, y no funcionan de manera confiable en un servidor; no puede ejecutar este código en, por ejemplo, Windows Server 2008. Por lo tanto, no puede usarlo en una aplicación web o en un código del lado del servidor. Es por eso que estaba buscando Oledb, en lugar de Interop. –

0

probar esto. Aquí está el código para ordenar los nombres de las hojas.

private Dictionary<int,string> GetExcelSheetNames(string fileName) 
    { 
    Excel.Application _excel = null; 
    Excel.Workbook _workBook = null; 
    Dictionary<int,string> excelSheets = new Dictionary<int,string>(); 
    try 
    { 
     object missing = Type.Missing; 
     object readOnly = true; 

    Excel.XlFileFormat.xlWorkbookNormal 
    _excel = new Excel.ApplicationClass(); 
    _excel.Visible = false; 
    _workBook = _excel.Workbooks.Open(fileName, 0, readOnly, 5, missing, 
    missing, true, Excel.XlPlatform.xlWindows, "\\t", false, false, 0, true, true, missing); 
    if (_workBook != null) 
    { 

        int index = 0; 

    foreach (Excel.Worksheet sheet in _workBook.Sheets) 
    { 
    // Can get sheet names in order they are in workbook 
    excelSheets.Add(++index, sheet.Name); 
    } 
    } 
} 
catch (Exception e) 
{ 
    return null; 
} 
finally 
{ 
    if (_excel != null) 
    { 

    if (_workBook != null) 
    { 
    _workBook.Close(false, Type.Missing, Type.Missing); 
    } 
    _excel.Application.Quit(); 
    } 
    _excel = null; 
    _workBook = null; 
} 
    return excelSheets; 
    } 
20

Dado que el código de seguridad no cubren los procedimientos para la lista de nombre de la hoja de extracción para Excel 2007, a raíz de código será aplicable tanto para Excel (97-2003) y Excel 2007 también:

public List<string> ListSheetInExcel(string filePath) 
{ 
    OleDbConnectionStringBuilder sbConnection = new OleDbConnectionStringBuilder(); 
    String strExtendedProperties = String.Empty; 
    sbConnection.DataSource = filePath; 
    if (Path.GetExtension(filePath).Equals(".xls"))//for 97-03 Excel file 
    { 
     sbConnection.Provider = "Microsoft.Jet.OLEDB.4.0"; 
     strExtendedProperties = "Excel 8.0;HDR=Yes;IMEX=1";//HDR=ColumnHeader,IMEX=InterMixed 
    } 
    else if (Path.GetExtension(filePath).Equals(".xlsx")) //for 2007 Excel file 
    { 
     sbConnection.Provider = "Microsoft.ACE.OLEDB.12.0"; 
     strExtendedProperties = "Excel 12.0;HDR=Yes;IMEX=1"; 
    } 
    sbConnection.Add("Extended Properties",strExtendedProperties); 
    List<string> listSheet = new List<string>(); 
    using (OleDbConnection conn = new OleDbConnection(sbConnection.ToString())) 
    { 
    conn.Open(); 
    DataTable dtSheet = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);   
    foreach (DataRow drSheet in dtSheet.Rows) 
    { 
     if (drSheet["TABLE_NAME"].ToString().Contains("$"))//checks whether row contains '_xlnm#_FilterDatabase' or sheet name(i.e. sheet name always ends with $ sign) 
     { 
      listSheet.Add(drSheet["TABLE_NAME"].ToString()); 
     } 
    } 
    } 
return listSheet; 
} 

Por encima la función devuelve la lista de hojas en particular el archivo de Excel para ambos tipos de Excel (97,2003,2007).

+10

Este código no devuelve las hojas en el orden en que aparecen en Excel –

0

Según MSDN, en el caso de las hojas de cálculo dentro de Excel, es posible que no funcione porque los archivos de Excel no son bases de datos reales. Por lo tanto, no podrá obtener el nombre de las hojas en orden de su visualización en el libro de trabajo.

código para obtener el nombre de hojas como por su aspecto visual utiliza la interoperabilidad:

añadir una referencia a Microsoft Excel 12.0 Object Library.

El siguiente código dará el nombre de las hojas en el orden real almacenado en el libro, no el nombre ordenado.

Código de ejemplo:

using Microsoft.Office.Interop.Excel; 

string filename = "C:\\romil.xlsx"; 

object missing = System.Reflection.Missing.Value; 

Microsoft.Office.Interop.Excel.Application excel = new Microsoft.Office.Interop.Excel.Application(); 

Microsoft.Office.Interop.Excel.Workbook wb =excel.Workbooks.Open(filename, missing, missing, missing, missing,missing, missing, missing, missing, missing, missing, missing, missing, missing, missing); 

ArrayList sheetname = new ArrayList(); 

foreach (Microsoft.Office.Interop.Excel.Worksheet sheet in wb.Sheets) 
{ 
    sheetname.Add(sheet.Name); 
} 
2

me gusta la idea de @deathApril para nombrar las hojas como 1_Germany, 2_UK, 3_IRELAND. También recibí tu problema para hacer este cambio de nombre para cientos de hojas. Si no tiene un problema para cambiar el nombre de la hoja, puede usar esta macro para hacerlo por usted. Tomará menos de unos segundos cambiar el nombre de todos los nombres de las hojas. lamentablemente ODBC, OLEDB devuelve el orden del nombre de la hoja por asc. No hay reemplazo para eso. Tienes que usar COM o renombrar tu nombre para estar en el orden.

Sub Macro1() 
' 
' Macro1 Macro 
' 

' 
Dim i As Integer 
For i = 1 To Sheets.Count 
Dim prefix As String 
prefix = i 
If Len(prefix) < 4 Then 
    prefix = "000" 
ElseIf Len(prefix) < 3 Then 
    prefix = "00" 
ElseIf Len(prefix) < 2 Then 
    prefix = "0" 
End If 
Dim sheetName As String 
sheetName = Sheets(i).Name 
Dim names 
names = Split(sheetName, "-") 
If (UBound(names) > 0) And IsNumeric(names(0)) Then 
    'do nothing 
Else 
    Sheets(i).Name = prefix & i & "-" & Sheets(i).Name 
End If 
Next 

End Sub 

ACTUALIZACIÓN: Después de leer comentario @SidHoland respecto BIFF. una idea cruzó. Los siguientes pasos se pueden hacer a través del código. No sé si realmente desea hacer eso para obtener los nombres de las hojas en el mismo orden. Avíseme si necesita ayuda para hacerlo a través del código.

1. Consider XLSX as a zip file. Rename *.xlsx into *.zip 
2. Unzip 
3. Go to unzipped folder root and open /docprops/app.xml 
4. This xml contains the sheet name in the same order of what you see. 
5. Parse the xml and get the sheet names 

ACTUALIZACIÓN: Otra solución - NPOI podría ser útil aquí http://npoi.codeplex.com/

FileStream file = new FileStream(@"yourexcelfilename", FileMode.Open, FileAccess.Read); 

     HSSFWorkbook hssfworkbook = new HSSFWorkbook(file); 
     for (int i = 0; i < hssfworkbook.NumberOfSheets; i++) 
     { 
      Console.WriteLine(hssfworkbook.GetSheetName(i)); 
     } 
     file.Close(); 

Esta solución funciona para xls. No intenté xlsx.

Gracias,

Esen

+1

No ** tiene ** para cambiar el nombre de las hojas o solo usa COM, ya que mi respuesta demuestra que puede usar DAO. Creo que también podría haber una forma de recuperarlos [leyendo el BIFF] (http://www.powerbasic.com/support/pbforums/showthread.php?t=43827), pero aún estoy investigando eso. –

+1

@SidHolland: DAO es un componente COM. Usar el componente COM en el Servidor 2008 es un problema, por lo tanto Steve se fue con ADO.NET – Esen

+0

Mi cerebro no creía que DAO es un componente COM, a pesar de tener que agregarlo como una referencia COM para usarlo. Gracias por la corrección. Su adición (cambiar el nombre a un archivo comprimido y leer el XML) es genio. No tenía idea de que eso funcionaría. Es, hasta ahora, el único método que mostrará las hojas en orden sin usar COM. +1! –

7

Otra forma: archivo

un xls (x) es una colección de archivos * .xml almacenados en un contenedor * .zip. descomprime el archivo "app.xml" en la carpeta docProps.

<?xml version="1.0" encoding="UTF-8" standalone="true"?> 
-<Properties xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes" xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties"> 
<TotalTime>0</TotalTime> 
<Application>Microsoft Excel</Application> 
<DocSecurity>0</DocSecurity> 
<ScaleCrop>false</ScaleCrop> 
-<HeadingPairs> 
    -<vt:vector baseType="variant" size="2"> 
    -<vt:variant> 
     <vt:lpstr>Arbeitsblätter</vt:lpstr> 
    </vt:variant> 
    -<vt:variant> 
     <vt:i4>4</vt:i4> 
    </vt:variant> 
    </vt:vector> 
</HeadingPairs> 
-<TitlesOfParts> 
    -<vt:vector baseType="lpstr" size="4"> 
    <vt:lpstr>Tabelle3</vt:lpstr> 
    <vt:lpstr>Tabelle4</vt:lpstr> 
    <vt:lpstr>Tabelle1</vt:lpstr> 
    <vt:lpstr>Tabelle2</vt:lpstr> 
    </vt:vector> 
</TitlesOfParts> 
<Company/> 
<LinksUpToDate>false</LinksUpToDate> 
<SharedDoc>false</SharedDoc> 
<HyperlinksChanged>false</HyperlinksChanged> 
<AppVersion>14.0300</AppVersion> 
</Properties> 

El archivo es un archivo alemán (Arbeitsblätter = worksheets). Los nombres de la tabla (Tabelle3, etc.) están en el orden correcto. Sólo tiene que leer estas etiquetas;)

respecto

+0

Muy prometedor - gracias por la contribución. –

+0

Esto funciona bien para archivos xlsx pero no para xls. Ellos no tienen la misma estructura. ¿Sabes cómo se pueden extraer los mismos datos de un archivo xls? – rdans

5

he creado la función a continuación utilizando la información proporcionada en la respuesta de @kraeppy (https://stackoverflow.com/a/19930386/2617732). Esto requiere utilizar .NET Framework v4.5 y requiere una referencia a System.IO.Compression.Esto solo funciona para los archivos xlsx y no para los archivos xls más antiguos.

using System.IO.Compression; 
    using System.Xml; 
    using System.Xml.Linq; 

    static IEnumerable<string> GetWorksheetNamesOrdered(string fileName) 
    { 
     //open the excel file 
     using (FileStream data = new FileStream(fileName, FileMode.Open)) 
     { 
      //unzip 
      ZipArchive archive = new ZipArchive(data); 

      //select the correct file from the archive 
      ZipArchiveEntry appxmlFile = archive.Entries.SingleOrDefault(e => e.FullName == "docProps/app.xml"); 

      //read the xml 
      XDocument xdoc = XDocument.Load(appxmlFile.Open()); 

      //find the titles element 
      XElement titlesElement = xdoc.Descendants().Where(e => e.Name.LocalName == "TitlesOfParts").Single(); 

      //extract the worksheet names 
      return titlesElement 
       .Elements().Where(e => e.Name.LocalName == "vector").Single() 
       .Elements().Where(e => e.Name.LocalName == "lpstr") 
       .Select(e => e.Value); 
     } 
    } 
0

no veo ninguna documentación que dice que el orden en app.xml se garantiza que sea el orden de las hojas. Es PROBABLEMENTE, pero no de acuerdo con la especificación OOXML.

El archivo workbook.xml, por otro lado, incluye el atributo sheetId, que determina la secuencia - de 1 a la cantidad de hojas. Esto está de acuerdo con la especificación OOXML. workbook.xml se describe como el lugar donde se guarda la secuencia de las hojas.

Así que la lectura de workbook.xml después de que se extraiga del XLSX sería mi recomendación. NO aplicación.xml. En lugar de docProps/app.xml, el uso xl/workbook.xml y mirar el elemento, como se muestra aquí -

`

<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"> 
    <fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="9303" /> 
    <workbookPr defaultThemeVersion="124226" /> 
- <bookViews> 
    <workbookView xWindow="120" yWindow="135" windowWidth="19035" windowHeight="8445" /> 
    </bookViews> 
- <sheets> 
    <sheet name="By song" sheetId="1" r:id="rId1" /> 
    <sheet name="By actors" sheetId="2" r:id="rId2" /> 
    <sheet name="By pit" sheetId="3" r:id="rId3" /> 
    </sheets> 
- <definedNames> 
    <definedName name="_xlnm._FilterDatabase" localSheetId="0" hidden="1">'By song'!$A$1:$O$59</definedName> 
    </definedNames> 
    <calcPr calcId="145621" /> 
    </workbook> 

`

Cuestiones relacionadas