web-dev-qa-db-de.com

Verwenden von Excel OleDb, um Blattnamen in Blattreihenfolge abzurufen

Ich verwende OleDb, um aus einer Excel-Arbeitsmappe mit vielen Blättern zu lesen.

Ich muss die Blattnamen lesen, aber ich benötige sie in der Reihenfolge, in der sie in der Tabelle definiert sind. Also, wenn ich eine Datei habe, die so aussieht;

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

Dann muss ich das Wörterbuch bekommen

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

Ich habe versucht, OleDbConnection.GetOleDbSchemaTable() zu verwenden, und das gibt mir die Liste der Namen, aber es sortiert sie alphabetisch. Die Alpha-Sortierung bedeutet, dass ich nicht weiß, welcher Blattnummer ein bestimmter Name entspricht. Also verstehe ich;

GERMANY, IRELAND, UK

das hat die Reihenfolge von UK und IRELAND geändert.

Der Grund, warum ich es sortieren muss, ist, dass der Benutzer einen Datenbereich nach Name oder Index auswählen muss. Sie können „alle Daten von DEUTSCHLAND nach IRLAND“ oder „Daten von Blatt 1 nach Blatt 3“ anfordern.

Irgendwelche Ideen wären sehr dankbar.

wenn ich die Office-Interop-Klassen verwenden könnte, wäre dies unkompliziert. Leider kann ich das nicht, da die Interop-Klassen in nicht interaktiven Umgebungen wie Windows-Diensten und ASP.NET-Sites nicht zuverlässig funktionieren. Daher musste OLEDB verwendet werden.

101
Steve Cooper

Kann dies nicht in der tatsächlichen MSDN-Dokumentation finden, aber ein Moderator in den Foren sagte

Ich befürchte, dass OLEDB die Blattreihenfolge nicht wie in Excel beibehält

Excel-Blattnamen in Blattreihenfolge

Es scheint, als wäre dies eine hinreichend verbreitete Voraussetzung für eine angemessene Problemumgehung.

17
Jeremy Breece

Können Sie nicht einfach die Blätter von 0 bis Anzahl der Namen -1 durchlaufen? Auf diese Weise sollten Sie sie in der richtigen Reihenfolge erhalten.

Bearbeiten

Ich habe durch die Kommentare bemerkt, dass es eine Menge Bedenken gibt, die Interop-Klassen zum Abrufen der Blattnamen zu verwenden. Daher hier ein Beispiel mit OLEDB, um sie abzurufen:

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

Aus Article im CodeProject extrahiert.

74
James

Da der obige Code keine Verfahren zum Extrahieren der Liste der Blattnamen für Excel 2007 abdeckt, gilt der folgende Code auch für Excel (97-2003) und 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;
}

Die obige Funktion gibt eine Liste der Arbeitsblätter in einer bestimmten Excel-Datei für beide Excel-Typen (97,2003,2007) zurück.

23
user1082916

Ein anderer Weg:

eine xls (x) -Datei ist nur eine Sammlung von * .xml-Dateien, die in einem * .Zip-Container gespeichert sind. Entpacken Sie die Datei "app.xml" im Ordner 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>

Die Datei ist eine deutsche Datei (Arbeitsblätter). Die Tabellennamen (Tabelle3 usw.) sind in der richtigen Reihenfolge. Sie müssen nur diese Tags lesen;)

grüße

8
kraeppy

Ich habe die folgende Funktion anhand der Informationen in der Antwort von @kraeppy ( https://stackoverflow.com/a/19930386/2617732 ) erstellt. Dazu muss das .net-Framework v4.5 verwendet werden, und es muss ein Verweis auf System.IO.Compression vorhanden sein. Dies funktioniert nur für XLSX-Dateien und nicht für ältere XLS-Dateien.

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

Das ist kurz, schnell, sicher und brauchbar ...

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

Ich mag die Idee von @deathApril, die Blätter als 1_Germany, 2_UK, 3_IRELAND zu bezeichnen. Ich habe auch Ihr Problem, diese Umbenennung für Hunderte von Blättern zu tun. Wenn Sie keine Probleme haben, den Blattnamen umzubenennen, können Sie diesen Makro verwenden, um dies für Sie zu tun. Das Umbenennen aller Blattnamen dauert weniger als Sekunden. Leider geben ODBC, OLEDB die Reihenfolge der Blattnamen von asc zurück. Dafür gibt es keinen Ersatz. Sie müssen entweder COM verwenden oder Ihren Namen umbenennen, um in der Reihenfolge zu sein.

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

UPDATE: Nach dem Lesen des @SidHoland-Kommentars zu BIFF blitzte eine Idee auf. Die folgenden Schritte können über Code ausgeführt werden. Ich weiß nicht, ob Sie das wirklich tun möchten, um die Blattnamen in der gleichen Reihenfolge zu erhalten. Lassen Sie mich wissen, wenn Sie Hilfe benötigen, um dies durch Code zu tun.

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

UPDATE: Eine andere Lösung - NPOI könnte hier hilfreich sein 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();

Diese Lösung funktioniert für xls. Ich habe xlsx nicht ausprobiert.

Vielen Dank,

Esen

2
Esen

Das hat bei mir funktioniert. Von hier gestohlen: Wie erhält man den Namen der ersten Seite einer Excel-Arbeitsmappe?

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;
1
eviljack

Versuche dies. Hier ist der Code, um die Blattnamen in der richtigen Reihenfolge zu erhalten.

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;
}
1
Ravi Shankar

Gemäß MSDN funktioniert dies bei Tabellenkalkulationen in Excel möglicherweise nicht, da Excel-Dateien keine echten Datenbanken sind. Daher können Sie den Blattnamen nicht in der Reihenfolge ihrer Visualisierung in der Arbeitsmappe abrufen.

Code zum Abrufen des Blattnamens gemäß ihres visuellen Erscheinungsbilds mit interop:

Verweis auf Microsoft Excel 12.0-Objektbibliothek hinzufügen.

Der folgende Code gibt den Blattnamen in der tatsächlichen Reihenfolge an, die in der Arbeitsmappe gespeichert ist, nicht den sortierten Namen.

Beispielcode:

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

Ich sehe keine Dokumentation, die besagt, dass die Reihenfolge in app.xml garantiert der Reihenfolge der Blätter entspricht. Es ist wahrscheinlich, aber nicht gemäß der OOXML-Spezifikation.

Die Datei workbook.xml enthält andererseits das Attribut sheetId, das die Reihenfolge bestimmt - von 1 bis zur Anzahl der Blätter. Dies entspricht der OOXML-Spezifikation. workbook.xml wird als der Ort beschrieben, an dem die Reihenfolge der Blätter beibehalten wird.

Daher wäre es meine Empfehlung, workbook.xml nach dem Extrahieren aus dem XLSX zu lesen. NICHT app.xml. Verwenden Sie anstelle von docProps/app.xml xl/workbook.xml und sehen Sie sich das Element wie hier gezeigt an.

`

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

`

0
Vern Hamberg