Utilisation d'Excel OleDb pour obtenir les noms de feuilles DANS L'ORDRE DES FEUILLES

103

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

J'ai besoin de lire les noms des feuilles, mais j'en ai besoin dans l'ordre dans lequel ils sont définis dans la feuille de calcul; donc si j'ai un fichier qui ressemble à ceci;

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

Ensuite, j'ai besoin du dictionnaire

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

J'ai essayé d'utiliser OleDbConnection.GetOleDbSchemaTable(), et cela me donne la liste des noms, mais cela les trie par ordre alphabétique. Le tri alpha 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 UKet IRELAND.

La raison pour laquelle j'ai besoin qu'il soit trié 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 de la feuille 1 à la feuille 3».

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

si je pouvais utiliser les classes d'interopérabilité de bureau, 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.

Steve Cooper
la source
Quelle version du fichier Excel lisez-vous?
yamen
30
wow comment as-tu dessiné ça et comment as-tu eu la patience de dessiner ça
l --''''''--------- '' '' '' '' '' '30
4
@ АртёмЦарионов - ce sont des rangées de barres verticales (|) et de traits de soulignement (_) pour le tableau, et des barres obliques avant et arrière (\ /) pour les onglets. Copiez-le dans un éditeur de texte et vous verrez.
Sid Holland

Réponses:

17

Impossible de trouver cela dans la documentation MSDN réelle, mais un modérateur des forums a dit

J'ai peur que OLEDB ne conserve pas l'ordre des feuilles comme ils l'étaient dans Excel

Noms des feuilles Excel dans l'ordre des feuilles

Il semble que ce soit une exigence assez courante pour qu'il y ait une solution de contournement décente.

Jeremy Breece
la source
Cependant, cela a répondu directement, cela permet de gagner beaucoup de temps en essais inutiles.
Shihe Zhang
75

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

Éditer

J'ai remarqué à travers les commentaires que l'utilisation des classes Interop pour récupérer les noms de feuille suscitait de nombreuses inquiétudes. Voici donc 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 l' article sur le CodeProject.

James
la source
C'est du code que j'aimerais voir! Comment pouvez-vous rechercher «la Nième feuille» et le nombre de feuilles?
Steve Cooper
13
Bonjour James. C'est à peu près mon problème d'origine - alors que la méthode GetOleDbSchemaTable () obtient les noms, le numéro de ligne ne correspond pas au numéro de feuille de classeur. La feuille 4 serait donc la ligne 0, si elle venait en premier dans l'alphabet.
Steve Cooper
23
Ne répond pas à la question des affiches (il le veut par ordre d'apparition dans Excel)
Andrew White
7
@Samuel Je ne pense pas que cela ait résolu le problème du PO directement, cependant, cela a semblé aider beaucoup d'autres personnes avec un problème similaire.
James
1
Ne résout pas la question du PO, ce que je cherchais. (Je poste toujours la raison d'un vote défavorable.)
Phil Nicholas
23

Étant donné que 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 pour 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).

TruthOf42
la source
11
Ce code ne renvoie pas les feuilles dans l'ordre dans lequel elles apparaissent dans Excel
Andrew White
10

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;
}
Mohammad Fathi MiMFa
la source
Ne fonctionne pas «hors de la boîte». exceladdress- Qu'est-ce que c'est?
Michael Hutter
8

Autrement:

un fichier xls (x) est juste 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 = feuilles de calcul). Les noms de table (Tabelle3, etc.) sont dans le bon ordre. Il vous suffit de lire ces balises;)

Cordialement

kraeppy
la source
1
Cela fonctionne bien pour les fichiers xlsx mais pas pour les fichiers xls. Ils n'ont pas la même structure. Savez-vous comment les mêmes données peuvent être extraites d'un fichier xls?
rdans le
6

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 du framework .net v4.5 et 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);
        }
    }
rdans
la source
2

J'aime l'idée de @deathApril de nommer les feuilles comme 1_Germany, 2_UK, 3_IRELAND. J'ai également eu votre problème pour 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 feuille. malheureusement ODBC, OLEDB renvoient l'ordre des noms de feuille par asc. Il n'y a pas de remplacement pour cela. Vous devez utiliser COM ou renommer votre nom pour qu'il soit 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 flashé. Les étapes suivantes peuvent être effectuées via du code. Je ne sais pas si vous voulez vraiment faire cela pour obtenir les noms de feuille 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

Esen
la source
1
Vous n'êtes pas obligé de renommer les feuilles ou d'utiliser uniquement COM, comme le montre ma réponse, vous pouvez utiliser DAO. Je pense qu'il pourrait aussi y avoir un moyen de les récupérer en lisant le BIFF , mais j'étudie toujours cela.
Sid Holland
1
@SidHolland: DAO est un composant COM. L'utilisation du composant COM dans Server 2008 est un problème, donc Steve est allé avec ADO.NET
Esen
Mon cerveau n'a pas dit que DAO est un composant COM, bien qu'il ait dû l'ajouter comme référence COM pour l'utiliser. Merci pour la correction. Votre ajout (renommer en zip et lire le XML) est génial. Je n'avais aucune idée que cela fonctionnerait. C'est, jusqu'à présent, la seule méthode qui affichera les feuilles dans l'ordre sans utiliser COM. +1!
Sid Holland
1

Cela a fonctionné pour moi. Volé d'ici: comment obtenir 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;
eviljack
la source
2
Salut. Heureux que vous ayez du code fonctionnel, mais qui utilise les classes Interop, et elles ne fonctionnent pas de manière fiable sur un serveur; vous ne pouvez pas exécuter ce code sur, par exemple, Windows Server 2008. Vous ne pouvez donc pas l'utiliser dans une application Web ou dans un code côté serveur. C'est pourquoi j'ai opté pour oledb plutôt que pour Interop.
Steve Cooper
1

Essaye ça. Voici le code pour obtenir les noms des 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;
}
Ravi Shankar
la source
Ist nicht mal compilierfähig! (Zeile Excel.XlFileFormat.xlWorkbookNormal)
Michael Hutter
0

Selon MSDN, dans le cas de feuilles de calcul à l'intérieur d'Excel, cela peut ne pas fonctionner car les fichiers Excel ne sont pas de vraies bases de données. Vous ne pourrez donc 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 l'interopérabilité:

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);
}
Romil Kumar Jain
la source
0

Je ne vois aucune documentation indiquant que l'ordre dans app.xml est garanti comme étant l'ordre des feuilles. Il 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 l'endroit où la séquence des feuilles est conservée.

Donc, lire workbook.xml après avoir été extrait du XLSX serait ma recommandation. PAS app.xml. Au lieu de docProps / app.xml, utilisez xl / workbook.xml et regardez l'élément, comme indiqué 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>

»

Vern Hamberg
la source