使用 Excel OleDb 获取工作表名称 按工作表顺序排列

Using Excel OleDb to get sheet names IN SHEET ORDER

提问人:Steve Cooper 提问时间:7/22/2009 最后编辑:Steve Cooper 更新时间:7/23/2019 访问量:219616

问:

我正在使用 OleDb 从包含许多工作表的 excel 工作簿中读取。

我需要读取工作表名称,但我需要按照它们在电子表格中定义的顺序排列;所以如果我有一个看起来像这样的文件;

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

然后我需要拿字典

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

我试过使用 ,这给了我名字列表,但它按字母顺序对它们进行了排序。alpha 排序意味着我不知道特定名称对应于哪个工作表编号。所以我明白了;OleDbConnection.GetOleDbSchemaTable()

GERMANY, IRELAND, UK

这改变了 和 的顺序。UKIRELAND

我需要对它进行排序的原因是我必须让用户按名称或索引选择一系列数据;他们可以要求提供“从德国到爱尔兰的所有数据”或“从工作表 1 到工作表 3 的数据”。

任何想法将不胜感激。

如果我可以使用办公室互操作类,这将很简单。不幸的是,我不能,因为互操作类在非交互式环境(如 Windows 服务和 ASP.NET 站点)中无法可靠地工作,因此我需要使用 OLEDB。

C# Excel OLEDB 服务器端

评论

0赞 yamen 4/25/2012
您正在阅读哪个版本的 Excel 文件?
33赞 Alex Gordon 4/30/2012
哇,你是怎么画的,你怎么有耐心画出来
4赞 Sid Holland 5/2/2012
@АртёмЦарионов - 它们是表的垂直条 (|) 和下划线 (_) 行,以及选项卡的反斜杠和正斜杠 (\/)。将其复制到文本编辑器中,您就会看到。

答:

83赞 James 7/22/2009 #1

你不能只从 0 到 Count of names -1 遍历工作表吗?这样,您应该按正确的顺序获取它们。

编辑

我通过评论注意到,对于使用 Interop 类检索工作表名称存在很多问题。因此,下面是一个使用 OLEDB 检索它们的示例:

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

摘自 CodeProject 上的文章

评论

0赞 Steve Cooper 7/23/2009
这是我想看到的代码!如何查询“第 N 张”和张数?
13赞 Steve Cooper 11/6/2009
嗨,詹姆斯。这几乎是我最初的问题 - 虽然 GetOleDbSchemaTable() 方法获取名称,但行号与工作簿工作表编号不对应。因此,如果工作表 4 在字母表中排在第一位,则它将是第 0 行。
27赞 Andrew White 4/25/2012
不回答海报问题(他希望它按在 Excel 中的出现顺序)
8赞 James 9/14/2012
@Samuel我不认为它直接解决了 OP 的问题,但是,它似乎帮助了很多其他人解决类似的问题。
1赞 Phil Nicholas 10/1/2016
没有解决OP的问题,这就是我来寻找的。(我总是发布投反对票的原因。
1赞 eviljack 9/16/2009 #2

这对我有用。从这里偷来的:你如何获得 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;

评论

3赞 Steve Cooper 10/1/2009
你好。很高兴你有工作代码,但它使用了 Interop 类,它们在服务器上不能可靠地工作;例如,不能在 Windows Server 2008 上运行此代码。因此,您不能在 Web 应用或服务器端代码中使用它。这就是为什么我选择oledb,而不是Interop。
1赞 Ravi Shankar 10/23/2010 #3

试试这个。这是按顺序获取工作表名称的代码。

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赞 Michael Hutter 7/16/2019
Ist nicht mal compilierfähig!(泽勒Excel.XlFileFormat.xlWorkbookNormal)
23赞 user1082916 2/14/2012 #4

由于上述代码不包括提取Excel 2007的工作表名称列表的过程,因此以下代码也适用于Excel(97-2003)和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;
}

上面的函数返回两个excel类型的工作表列表,特别是excel文件(97,2003,2007)。

评论

14赞 Andrew White 5/1/2012
此代码不会按工作表在 Excel 中的显示顺序返回工作表
0赞 Romil Kumar Jain 4/25/2012 #5

根据 MSDN,在 Excel 内部的电子表格的情况下,它可能不起作用,因为 Excel 文件不是真正的数据库。因此,您将无法按照工作簿中的可视化顺序获取工作表名称。

使用互操作根据工作表的视觉外观获取工作表名称的代码:

添加对 Microsoft Excel 12.0 对象库的引用。

以下代码将按工作簿中存储的实际顺序提供工作表名称,而不是排序名称。

示例代码:

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赞 Esen 5/2/2012 #6

我喜欢@deathApril将工作表命名为 1_Germany、2_UK 3_IRELAND 的想法。我还遇到了您的问题,需要对数百张纸进行重命名。如果您在重命名工作表名称时没有问题,则可以使用此宏为您完成此操作。重命名所有工作表名称只需不到几秒钟的时间。不幸的是,ODBC、OLEDB 通过 asc 返回工作表名称顺序。这是无可替代的。您必须使用 COM 或重命名您的名称以按顺序排列。

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

更新: 在阅读了@SidHoland关于BIFF的评论后,一个想法闪现出来。以下步骤可以通过代码完成。不知道你是否真的想这样做以相同的顺序获取工作表名称。如果您需要帮助通过代码执行此操作,请告诉我。

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

更新: 另一个解决方案 - NPOI 可能会有所帮助 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();

此解决方案适用于 xls。我没有尝试 xlsx。

谢谢

埃森

评论

1赞 Sid Holland 5/2/2012
不必重命名工作表或仅使用 COM,因为我的回答表明您可以使用 DAO。我认为可能还有一种方法可以通过阅读 BIFF 来检索它们,但我仍在调查。
1赞 Esen 5/3/2012
@SidHolland:DAO 是一个 COM 组件。在 Server 2008 中使用 COM 组件是一个问题,因此 Steve 选择了 ADO.NET
0赞 Sid Holland 5/3/2012
我的大脑并没有发现 DAO 是一个 COM 组件,尽管必须将其添加为 COM 引用才能使用它。感谢您的更正。您的添加(重命名为 zip 并读取 XML)是天才。我不知道那会起作用。到目前为止,这是唯一一种在不使用 COM 的情况下按顺序显示工作表的方法。 +1!
20赞 Jeremy Breece 9/26/2012 #7

在实际的MSDN文档中找不到这个,但论坛中的版主说

恐怕OLEDB不会像在Excel中那样保留工作表顺序

按工作表顺序排列的 Excel 工作表名称

似乎这将是一个足够普遍的要求,会有一个体面的解决方法。

评论

0赞 Shihe Zhang 7/6/2017
然而,这确实直接回答了,它确实节省了大量不必要的尝试时间。
8赞 kraeppy 11/12/2013 #8

另一种方式:

XLS(X) 文件只是存储在 *.zip 容器中的 *.xml 文件的集合。 解压缩文件夹 docProps 中的文件“app.xml”。

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

该文件是德语文件(Arbeitsblätter = 工作表)。 表名(Tabelle3 等)的顺序正确。你只需要阅读这些标签;

问候

评论

1赞 rdans 7/27/2014
这适用于 xlsx 文件,但不适用于 xls 文件。它们没有相同的结构。您知道如何从 xls 文件中提取相同的数据吗?
6赞 rdans 7/27/2014 #9

我使用@kraeppy(https://stackoverflow.com/a/19930386/2617732)的答案中提供的信息创建了以下函数。这需要使用 .net framework v4.5,并且需要对 System.IO.Compression 的引用。这仅适用于 xlsx 文件,不适用于较旧的 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);
        }
    }
0赞 Vern Hamberg 8/2/2014 #10

我没有看到任何文档表明app.xml中的顺序保证是工作表的顺序。它可能是,但不是根据 OOXML 规范。

另一方面,workbook.xml 文件包含 sheetId 属性,该属性确实确定顺序 - 从 1 到页数。这是根据 OOXML 规范。workbook.xml被描述为保存纸张序列的地方。

因此,我建议在从 XLSX 中提取workbook.xml后阅读。不是app.xml。使用 xl/workbook.xml 代替 docProps/app.xml 并查看元素,如下所示 -

`

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

`

11赞 MiMFa 6/24/2018 #11

这是短、快、安全、可用......

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

评论

0赞 Michael Hutter 7/16/2019
不能“开箱即用”。 -这是怎麽?exceladdress
1赞 shas 12/4/2020
不回答实际问题。它只是返回所有工作表,但不是按顺序返回。