【问题标题】:How to retrieve Tab names from excel sheet using OpenXML如何使用 OpenXML 从 Excel 工作表中检索选项卡名称
【发布时间】:2011-09-21 17:50:00
【问题描述】:

我有一个包含 182 列的电子表格文档。我需要将电子表格数据逐个选项卡地放入数据表中,但我需要在从每个选项卡添加数据时找出选项卡名称是什么,并将选项卡名称添加到数据表中的列中.

这就是我设置数据表的方式。

然后我在工作簿中循环并深入到sheetData 对象并遍历每一行和每一列,获取单元格数据。

DataTable dt = new DataTable();
for (int i = 0; i <= col.GetUpperBound(0); i++)
{
    try
    {
        dt.Columns.Add(new DataColumn(col[i].ToString(), typeof(string)));
    }
    catch (Exception e)
    {
        MessageBox.Show("Uploader  Error" + e.ToString());
        return null;
    }
}

dt.Columns.Add(new DataColumn("SheetName", typeof(string)));

但是,在我用于数据表的字符串数组的末尾,我需要添加选项卡名称。当我在 Open XML 的工作表中循环时,如何找出选项卡名称?

到目前为止,这是我的代码:

using (SpreadsheetDocument spreadSheetDocument = 
           SpreadsheetDocument.Open(Destination, false))
{
    WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
    Workbook workbook = spreadSheetDocument.WorkbookPart.Workbook;

    Sheets sheets = 
        spreadSheetDocument
            .WorkbookPart
            .Workbook
            .GetFirstChild<DocumentFormat.OpenXml.Spreadsheet.Sheets>();

    OpenXmlElementList list = sheets.ChildElements;

    foreach (WorksheetPart worksheetpart in workbook.WorkbookPart.WorksheetParts)
    {
        Worksheet worksheet = worksheetpart.Worksheet;

        foreach (SheetData sheetData in worksheet.Elements<SheetData>())
        {
            foreach (Row row in sheetData.Elements())
            {
                string[] thisarr = new string[183];
                int index = 0;
                foreach (Cell cell in row.Elements())
                {
                    thisarr[(index)] = GetCellValue(spreadSheetDocument, cell);
                    index++;
                }
                thisarr[182] = ""; //need to add tabname here
                if (thisarr[0].ToString() != "")
                {
                    dt.Rows.Add(thisarr);
                }
            }
        }
    }
}

return dt;

请注意:我之前确实从“列表”的 InnerXML 属性中获取了选项卡名称

OpenXmlElementList list = sheets.ChildElements;

但是我注意到,当我在电子表格中循环时,它没有以正确的顺序获取选项卡名称。

【问题讨论】:

  • 如果我只想将选项卡名称拉出来,那效果很好..我可以通过解析内部/外部 xml.. 但我想在我自己的 For 循环中执行它..我在 SheetData 级别访问工作表时遇到问题..
  • 您不能使用“工作表”对象来获取其名称?该页面上的最后一个代码示例显示了如何遍历工作表的属性:大概工作表名称是这些属性之一(虽然我自己没有经验)。
  • 嗯,工作表级别有一个“名称”属性,但它没有选项卡名称。我只是继续这样做: OpenXmlElementList list = sheet.ChildElements; foreach (OpenXmlElement elm in list) { string xml = elm.OuterXml;

标签: c# .net excel openxml openxml-sdk


【解决方案1】:

这是一个方便的帮助方法来获取对应于 WorksheetPart 的工作表:

public static Sheet GetSheetFromWorkSheet
    (WorkbookPart workbookPart, WorksheetPart worksheetPart)
{
    string relationshipId = workbookPart.GetIdOfPart(worksheetPart);
    IEnumerable<Sheet> sheets = workbookPart.Workbook.Sheets.Elements<Sheet>();
    return sheets.FirstOrDefault(s => s.Id.HasValue && s.Id.Value == relationshipId);
}

然后你可以从表格 Name-property 中获取名称:

Sheet sheet = GetSheetFromWorkSheet(myWorkbookPart, myWorksheetPart);
string sheetName = sheet.Name;

...这将是 OP 引用的“选项卡名称”。


为了记录,相反的方法如下所示:

public static Worksheet GetWorkSheetFromSheet(WorkbookPart workbookPart, Sheet sheet)
{
    var worksheetPart = (WorksheetPart)workbookPart.GetPartById(sheet.Id);
    return worksheetPart.Worksheet;
}

...我们还可以添加以下方法:

public static IEnumerable<KeyValuePair<string, Worksheet>> GetNamedWorksheets
    (WorkbookPart workbookPart)
{
    return workbookPart.Workbook.Sheets.Elements<Sheet>()
        .Select(sheet => new KeyValuePair<string, Worksheet>
            (sheet.Name, GetWorkSheetFromSheet(workbookPart, sheet)));
}

现在您可以轻松枚举所有工作表,包括它们的名称。

如果您愿意,可以将其全部放入字典中进行基于名称的查找:

IDictionary<string, WorkSheet> wsDict = GetNamedWorksheets(myWorkbookPart)
    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

...或者如果您只想要一个特定的工作表名称:

public static Sheet GetSheetFromName(WorkbookPart workbookPart, string sheetName)
{
    return workbookPart.Workbook.Sheets.Elements<Sheet>()
        .FirstOrDefault(s => s.Name.HasValue && s.Name.Value == sheetName);
}

(然后调用GetWorkSheetFromSheet获取对应的Worksheet。)

【讨论】:

    【解决方案2】:

    工作表名称存储在 Sheets 元素中的 WorkbookPart 中,该元素具有元素 Sheet 的子元素,该元素对应于 Excel 文件中的每个工作表。您所要做的就是从Sheets 元素中获取正确的索引,这将是您在循环中的Sheet。我在下面添加了一段 sn-p 代码来做你想做的事。

    int sheetIndex = 0;
    foreach (WorksheetPart worksheetpart in workbook.WorkbookPart.WorksheetParts)
    {                     
        Worksheet worksheet = worksheetpart.Worksheet;
    
        // Grab the sheet name each time through your loop
        string sheetName = workbookPart.Workbook.Descendants<Sheet>().ElementAt(sheetIndex).Name;
    
        foreach (SheetData sheetData in worksheet.Elements<SheetData>())
        {
    
           ...
        }
        sheetIndex++;
    }
    

    【讨论】:

    • 根据我的经验,使用您的代码 sn-p,sheetName 以正确的顺序读取工作表名称(因为它们在文件中),但 sheetData 的读取顺序与它们在 Excel 中的顺序不同文件。因此,代码会导致所有工作表的名称混合。
    • 问题似乎来自“excel_file.xlsx\xl_rels\workbook.xml.rels”文件,该文件具有电子表格名称和对电子表格内容的引用以某种随机顺序存储。如果您手动将它们从 1 重新排序到 N(Id="rId1", Id="rId2", ..., Id="rIdN"),则电子表格名称将在读取文件后与其内容对齐。不知道如何在代码中处理这个问题。
    • 正如用户@Skull 指出的那样,这个解决方案是错误的!要查看的正确值是 relationshipId,它每次都不会与元素索引重合 - 经常出现这种情况只是巧合和运气。 (有关如何检索和使用 relationshipId,请参阅我的答案。)
    • 以上所有cmets都是正确的,这个解决方案是不正确的。
    【解决方案3】:
        Using spreadsheetDocument As SpreadsheetDocument = spreadsheetDocument.Open("D:\Libro1.xlsx", True)
    
            Dim workbookPart As WorkbookPart = spreadsheetDocument.WorkbookPart
    
            workbookPart.Workbook.Descendants(Of Sheet)()
    
    
    
            Dim worksheetPart As WorksheetPart = workbookPart.WorksheetParts.Last
            Dim text As String
    
    
    
            For Each Sheet As Sheet In spreadsheetDocument.WorkbookPart.Workbook.Sheets
                Dim sName As String = Sheet.Name
                Dim sID As String = Sheet.Id
    
                Dim part As WorksheetPart = workbookPart.GetPartById(sID)
                Dim actualSheet As Worksheet = part.Worksheet
    
                Dim sheetData As SheetData = part.Worksheet.Elements(Of SheetData)().First
    
                For Each r As Row In sheetData.Elements(Of Row)()
                    For Each c As Cell In r.Elements(Of Cell)()
                        text = c.CellValue.Text
                        Console.Write(text & " ")
                    Next
                Next
            Next
    
        End Using
    
        Console.Read()
    

    【讨论】:

    • 如果您能在代码之外添加一些关于您的方法为何有效的讨论,那就太好了。
    • 这种方式对我来说更有意义,因为您正在通过名称搜索工作表,获取该工作表的 id,然后根据名称获取工作表(使用 linq 意味着您没有根本不需要循环)。标记为答案的回复使用了一个索引,该索引需要循环访问,直到找到所需内容。
    【解决方案4】:
    worksheet.GetAttribute("name","").Value
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多