Utilizzo di Excel OleDb per ottenere i nomi dei fogli IN SHEET ORDER


103

Sto usando OleDb per leggere da una cartella di lavoro Excel con molti fogli.

Ho bisogno di leggere i nomi dei fogli, ma ne ho bisogno nell'ordine in cui sono definiti nel foglio di calcolo; quindi se ho un file che assomiglia a questo;

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

Allora devo prendere il dizionario

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

Ho provato a usare OleDbConnection.GetOleDbSchemaTable()e questo mi dà l'elenco dei nomi, ma li ordina alfabeticamente. L'ordinamento alfa significa che non so a quale numero di foglio corrisponde un particolare nome. Quindi ottengo;

GERMANY, IRELAND, UK

che ha cambiato l'ordine di UKe IRELAND.

Il motivo per cui ho bisogno che sia ordinato è che devo lasciare che l'utente scelga un intervallo di dati per nome o indice; possono chiedere "tutti i dati dalla GERMANIA all'IRLANDA" o "dati dal foglio 1 al foglio 3".

Tutte le idee notevolmente sarebbero apprezzate.

se potessi usare le classi di interoperabilità dell'ufficio, sarebbe semplice. Sfortunatamente, non posso perché le classi di interoperabilità non funzionano in modo affidabile in ambienti non interattivi come servizi Windows e siti ASP.NET, quindi dovevo usare OLEDB.


Quale versione del file Excel stai leggendo?
yamen

30
wow, come l'hai disegnato e come hai avuto la pazienza di disegnarlo
l --''''''--------- '' '' '' '' '' '

4
@ АртёмЦарионов - sono righe di barre verticali (|) e trattini bassi (_) per la tabella, e barre avanti e indietro (\ /) per le tabulazioni. Copialo in un editor di testo e vedrai.
Sid Holland

Risposte:


17

Non riesco a trovare questo nella documentazione reale di MSDN, ma ha detto un moderatore nei forum

Temo che OLEDB non conservi l'ordine dei fogli come erano in Excel

Nomi dei fogli Excel nell'ordine dei fogli

Sembra che questo sarebbe un requisito abbastanza comune da prevedere una soluzione alternativa decente.


Tuttavia questo ha risposto direttamente, fa risparmiare molto tempo su tentativi non necessari.
Shihe Zhang

75

Non puoi semplicemente scorrere i fogli da 0 a Count of names -1? in questo modo dovresti inserirli nell'ordine corretto.

modificare

Ho notato attraverso i commenti che ci sono molte preoccupazioni sull'utilizzo delle classi Interop per recuperare i nomi dei fogli. Quindi ecco un esempio che utilizza OLEDB per recuperarli:

/// <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();
      }
   }
}

Estratto dall'articolo su CodeProject.


Questo è il codice che vorrei vedere! Come puoi interrogare "l'ennesimo foglio" e il numero di fogli?
Steve Cooper,

13
Ciao James. Questo è praticamente il mio problema originale: mentre il metodo GetOleDbSchemaTable () ottiene i nomi, il numero di riga non corrisponde al numero del foglio della cartella di lavoro. Quindi il foglio 4 sarebbe la riga 0, se venisse prima dell'alfabeto.
Steve Cooper,

23
Non risponde alla domanda dei poster (lo vuole in ordine di apparizione in Excel)
Andrew White

7
@Samuel Non penso che abbia risolto direttamente il problema dell'OP, tuttavia, è sembrato aiutare molti altri con un problema simile.
James

1
Non risolve la domanda dell'OP, che è quello che cercavo. (Metto sempre il motivo di un voto negativo.)
Phil Nicholas

23

Poiché il codice sopra non copre le procedure per l'estrazione dell'elenco del nome del foglio per Excel 2007, il seguente codice sarà applicabile sia per Excel (97-2003) che per Excel 2007:

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;
}

La funzione sopra restituisce l'elenco del foglio in un file Excel particolare per entrambi i tipi di Excel (97,2003,2007).


11
Questo codice non restituisce i fogli nell'ordine in cui appaiono in Excel
Andrew White

10

Questo è breve, veloce, sicuro e utilizzabile ...

public static List<string> ToExcelsSheetList(string excelFilePath)
{
    List<string> sheets = new List<string>();
    using (OleDbConnection connection = 
            new OleDbConnection((excelFilePath.TrimEnd().ToLower().EndsWith("x")) 
            ? "Provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + excelFilePath + "';" + "Extended Properties='Excel 12.0 Xml;HDR=YES;'"
            : "provider=Microsoft.Jet.OLEDB.4.0;Data Source='" + excelFilePath + "';Extended Properties=Excel 8.0;"))
    {
        connection.Open();
        DataTable dt = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
        foreach (DataRow drSheet in dt.Rows)
            if (drSheet["TABLE_NAME"].ToString().Contains("$"))
            {
                string s = drSheet["TABLE_NAME"].ToString();
                sheets.Add(s.StartsWith("'")?s.Substring(1, s.Length - 3): s.Substring(0, s.Length - 1));
            }
        connection.Close();
    }
    return sheets;
}

Non funziona "out of the box". exceladdress- cos'è questo?
Michael Hutter

8

Un altro modo:

un file xls (x) è solo una raccolta di file * .xml archiviati in un contenitore * .zip. decomprimere il file "app.xml" nella cartella 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>

Il file è un file tedesco (Arbeitsblätter = fogli di lavoro). I nomi delle tabelle (Tabelle3 ecc.) Sono nell'ordine corretto. Hai solo bisogno di leggere questi tag;)

Saluti


1
Funziona bene per i file xlsx ma non per i file xls. Non hanno la stessa struttura. Sai come si potrebbero estrarre gli stessi dati da un file xls?
rdans

6

Ho creato la seguente funzione utilizzando le informazioni fornite nella risposta da @kraeppy ( https://stackoverflow.com/a/19930386/2617732 ). Ciò richiede l'utilizzo di .net framework v4.5 e un riferimento a System.IO.Compression. Funziona solo per i file xlsx e non per i file xls più vecchi.

    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);
        }
    }

2

Mi piace l'idea di @deathApril di denominare i fogli come 1_Germany, 2_UK, 3_IRELAND. Ho anche chiesto al tuo problema di rinominare centinaia di fogli. Se non hai problemi a rinominare il nome del foglio, puoi utilizzare questa macro per farlo per te. Ci vorranno meno di secondi per rinominare tutti i nomi dei fogli. sfortunatamente ODBC, OLEDB restituiscono l'ordine dei nomi dei fogli per asc. Non c'è sostituto per quello. Devi usare COM o rinominare il tuo nome per essere nell'ordine.

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

AGGIORNAMENTO: Dopo aver letto il commento di @SidHoland riguardo al BIFF, è balenata un'idea. I passaggi seguenti possono essere eseguiti tramite codice. Non so se vuoi davvero farlo per ottenere i nomi dei fogli nello stesso ordine. Fammi sapere se hai bisogno di aiuto per farlo tramite il codice.

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

AGGIORNAMENTO: un'altra soluzione - NPOI potrebbe essere utile qui 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();

Questa soluzione funziona per xls. Non ho provato xlsx.

Grazie,

Esen


1
Non è necessario rinominare i fogli o utilizzare solo COM, poiché la mia risposta dimostra che è possibile utilizzare DAO. Penso che potrebbe esserci anche un modo per recuperarli leggendo il BIFF , ma sto ancora indagando su questo.
Sid Holland

1
@ SidHolland: DAO è un componente COM. L'uso del componente COM in Server 2008 è un problema, quindi Steve è andato con ADO.NET
Esen

Il mio cervello non ha capito che DAO è un componente COM, nonostante debba aggiungerlo come riferimento COM per usarlo. Grazie per la correzione. La tua aggiunta (rinominare in uno zip e leggere l'XML) è geniale. Non avevo idea che avrebbe funzionato. Finora è l'unico metodo che mostrerà i fogli in ordine senza utilizzare COM. +1!
Sid Holland

1

Questo ha funzionato per me. Rubato da qui: come si ottiene il nome della prima pagina di una cartella di lavoro Excel?

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
Ciao. Sono contento che tu abbia un codice funzionante, ma che utilizza le classi Interop e non funzionano in modo affidabile su un server; non è possibile eseguire questo codice su, ad esempio, Windows Server 2008. Quindi non è possibile utilizzarlo in un'app Web o nel codice lato server. Ecco perché stavo scegliendo oledb, piuttosto che Interop.
Steve Cooper

1

Prova questo. Ecco il codice per ottenere i nomi dei fogli in ordine.

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;
}

Ist nicht mal compilierfähig! (Zeile Excel.XlFileFormat.xlWorkbookNormal)
Michael Hutter

0

Come per MSDN, in un caso di fogli di calcolo all'interno di Excel potrebbe non funzionare perché i file Excel non sono database reali. Quindi non sarai in grado di ottenere il nome dei fogli in ordine di visualizzazione nella cartella di lavoro.

Codice per ottenere il nome dei fogli in base al loro aspetto visivo utilizzando l'interoperabilità:

Aggiungere il riferimento alla libreria di oggetti di Microsoft Excel 12.0.

Il codice seguente darà il nome dei fogli nell'ordine effettivo memorizzato nella cartella di lavoro, non il nome ordinato.

Codice di esempio:

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);
}

0

Non vedo alcuna documentazione che dica che l'ordine in app.xml è garantito come l'ordine dei fogli. PROBABILMENTE lo è, ma non è conforme alla specifica OOXML.

Il file workbook.xml, d'altra parte, include l'attributo sheetId, che determina la sequenza, da 1 al numero di fogli. Ciò è conforme alla specifica OOXML. workbook.xml è descritto come il luogo in cui viene conservata la sequenza dei fogli.

Quindi leggere workbook.xml dopo che è stato estratto da XLSX sarebbe la mia raccomandazione. NON app.xml. Invece di docProps / app.xml, usa xl / workbook.xml e guarda l'elemento, come mostrato qui -

`

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

`

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.