web-dev-qa-db-fra.com

Utilisation d'Excel OleDb pour obtenir les noms de feuille dans l'ordre des feuilles

J'utilise OleDb pour lire à partir d'un classeur Excel comportant de nombreuses feuilles.

Je dois lire les noms des feuilles, mais j'en ai besoin dans l'ordre dans lequel elles ont été définies dans le tableur. donc si j'ai un fichier qui ressemble à ceci;

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

Ensuite, je dois obtenir le dictionnaire

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

J'ai essayé d'utiliser OleDbConnection.GetOleDbSchemaTable(), et cela me donne la liste des noms, mais les trie par ordre alphabétique. Le tri alphabétique signifie que je ne sais pas à quel numéro de feuille correspond un nom particulier. Alors je reçois;

GERMANY, IRELAND, UK

qui a changé l'ordre de UK et IRELAND.

La raison pour laquelle j'ai besoin de le trier est que je dois laisser l'utilisateur choisir une plage de données par nom ou par index; ils peuvent demander "toutes les données de l'ALLEMAGNE vers l'IRLANDE" ou "les données des feuilles 1 à 3".

Toutes les idées seraient grandement appréciées.

si je pouvais utiliser les classes interop Office, ce serait simple. Malheureusement, je ne peux pas, car les classes d'interopérabilité ne fonctionnent pas de manière fiable dans des environnements non interactifs tels que les services Windows et les sites ASP.NET. J'ai donc dû utiliser OLEDB.

101
Steve Cooper

Impossible de trouver cela dans la documentation MSDN, mais un modérateur des forums a déclaré

Je crains que OLEDB ne conserve pas l'ordre des feuilles comme dans Excel

Noms des feuilles Excel dans l'ordre des feuilles

On dirait que cela serait une exigence assez commune pour qu'il y ait une solution de contournement décente.

17
Jeremy Breece

Ne pouvez-vous pas simplement parcourir les feuilles de 0 à Nombre de noms -1? De cette façon, vous devriez les obtenir dans le bon ordre.

Modifier

J'ai remarqué à travers les commentaires que l'utilisation des classes Interop pour récupérer les noms des feuilles suscite de nombreuses inquiétudes. Par conséquent, voici un exemple utilisant OLEDB pour les récupérer:

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

Extrait de Article sur le CodeProject.

74
James

Comme le code ci-dessus ne couvre pas les procédures d'extraction de la liste des noms de feuille pour Excel 2007, le code suivant sera également applicable à Excel (97-2003) et à 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 fonction ci-dessus renvoie la liste des feuilles dans un fichier Excel particulier pour les deux types Excel (97,2003,2007).

23
user1082916

Autrement:

un fichier xls (x) est simplement une collection de fichiers * .xml stockés dans un conteneur * .Zip. Décompressez le fichier "app.xml" dans le dossier 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>

Le fichier est un fichier allemand (Arbeitsblätter = worksheets). Les noms de table (Tabelle3, etc.) sont dans le bon ordre. Il vous suffit de lire ces balises;)

cordialement

8
kraeppy

J'ai créé la fonction ci-dessous en utilisant les informations fournies dans la réponse de @kraeppy ( https://stackoverflow.com/a/19930386/2617732 ). Cela nécessite l'utilisation de .net framework v4.5 et d'une référence à System.IO.Compression. Cela ne fonctionne que pour les fichiers xlsx et non pour les anciens fichiers xls.

    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

C'est court, rapide, sûr et utilisable ...

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

J'aime l'idée de @deathApril de nommer les feuilles 1_Germany, 2_UK, 3_IRELAND. J'ai également demandé à votre problème de renommer des centaines de feuilles. Si vous ne rencontrez pas de problème pour renommer le nom de la feuille, vous pouvez utiliser cette macro pour le faire à votre place. Il faudra moins de secondes pour renommer tous les noms de feuilles. Malheureusement, ODBC, OLEDB renvoie l'ordre des noms de feuille par asc. Il n'y a pas de remplacement pour cela. Vous devez soit utiliser COM ou renommer votre nom pour être dans l'ordre.

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

MISE À JOUR: Après avoir lu le commentaire de @SidHoland concernant BIFF, une idée a été émise. Les étapes suivantes peuvent être effectuées via le code. Je ne sais pas si vous voulez vraiment faire cela pour obtenir les noms de feuilles dans le même ordre. Faites-moi savoir si vous avez besoin d'aide pour le faire via le code.

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

MISE À JOUR: Une autre solution - NPOI pourrait être utile ici 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();

Cette solution fonctionne pour xls. Je n'ai pas essayé xlsx.

Merci,

Esen

2
Esen

Cela a fonctionné pour moi. Volé d'ici: Comment obtenez-vous le nom de la première page d'un classeur 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;
1
eviljack

Essaye ça. Voici le code pour obtenir les noms de feuilles dans l'ordre.

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

Selon MSDN, il se peut que les feuilles de calcul contenues dans Excel ne fonctionnent pas car les fichiers Excel ne sont pas de véritables bases de données. Ainsi, vous ne pourrez pas obtenir le nom des feuilles dans l’ordre de leur visualisation dans le classeur.

Code pour obtenir le nom des feuilles selon leur apparence visuelle en utilisant interop:

Ajoutez une référence à la bibliothèque d'objets Microsoft Excel 12.0.

Le code suivant donnera le nom des feuilles dans l'ordre réel stocké dans le classeur, pas le nom trié.

Exemple de code:

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
Romil Kumar Jain

Je ne vois aucune documentation indiquant que l'ordre dans le fichier app.xml est garanti comme étant l'ordre des feuilles. C’est PROBABLEMENT, mais pas selon la spécification OOXML.

Le fichier workbook.xml, en revanche, inclut l’attribut sheetId, qui détermine la séquence - de 1 au nombre de feuilles. Ceci est conforme à la spécification OOXML. workbook.xml est décrit comme le lieu où la séquence des feuilles est conservée.

Donc, la lecture de workbook.xml après son extraction de XLSX serait ma recommandation. PAS app.xml. Au lieu de docProps/app.xml, utilisez xl/workbook.xml et regardez l’élément, comme illustré ici -

`

<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