从电子表格读取的 OpenXML 卡在第一行

OpenXML reading from spreadsheet stuck on first row

我是 运行 一些基于 openXML 的 c sharp 代码,它停留在第一行数据上,并一次又一次地循环它。我很清楚我需要将行变量合并到组合中,但尝试了各种方法都无济于事。有人对此有任何想法吗?

在下面的代码块中,sst.ChildElements[7].InnerText 取回第一行第 7 列的内容,但每次该行循环时都是来自同一个 CELL 的内容!我想移到下一行:-(

string fileName = @"c:\temp\accountData.xlsx";

using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    using (SpreadsheetDocument doc = SpreadsheetDocument.Open(fs, false))
    {
        WorkbookPart workbookPart = doc.WorkbookPart;
        SharedStringTablePart sstpart = workbookPart.GetPartsOfType<SharedStringTablePart>().First();
        SharedStringTable sst = sstpart.SharedStringTable;

        WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
        Worksheet sheet = worksheetPart.Worksheet;

        var cells = sheet.Descendants<Cell>();
        var rows = sheet.Descendants<Row>();

        Console.WriteLine("Row count = {0}", rows.LongCount());
        Console.WriteLine("Cell count = {0}", cells.LongCount());

        CompanyProvider cp = _db.GetCompanyProvider();
        int i = 0;

        // Or... via each row
        foreach (Row row in rows.ToList())
        {
            if (i == 0)
                i = i + 1;
            else
            {
                CustomerAddress customerAddress = new CustomerAddress();
                customerAddress.AddressLine1 = sst.ChildElements[7].InnerText; // Code hidden for brevity

                i = i + 1;
            }
        }
    }
}

行间交互的基本算法涉及两个循环。一个用于行,另一个用于单元格。

假设您有一个工作表。

这就是您获取行集合的方式。

IEnumerable<Row> rows = worksheet.Descendants<Row>();

你有一组单元格。

所以你需要对行进行第一个循环

foreach (Row row in rows)
{
}

在这里你可以获得一行的单元格集合

IEnumerable<Cell> cells = row.Descendants<Cell>()

然后您可以在内循环中遍历单元格

foreach (Cell cell in cells)
{
  //Here goes the logic of reading cell value
}

In the code block below, sst.ChildElements[7].InnerText gets back the content of the first row column 7 but each time the row loops its the content from the same CELL!

这是因为您总是读取 SharedStringsTable(您的 sst 变量)中元素 7 的值,而不是 [=16= 中元素 7 的值](您的 row 变量)。

共享字符串 Table 是 OpenXML 中使用的一种机制,用于防止单元格中出现重复数据(以减小文件大小)。它可以代替直接包含字符串值的单元格,而是包含一个整数,该整数是共享字符串 table 的索引。这样,如果一个字符串在 Excel 文件中重复多次,它只会存储一次,但会多次引用该字符串。

您可以通过查看 DataType 属性 来判断 Cell 对象是否包含共享字符串索引(注意字符串 可以 被存储 in-line 而其他数据类型如数字总是被存储 in-line).

如果单元格确实包含共享字符串索引,那么您可以使用 那个 值来索引您的 sst 属性 以获得正确的内容:

 sst.ChildElements[<cell content here>].InnerText

要按索引获取行单元格,您可以获取 Row 的子 Cells,然后使用 Enumerable<T>.ElementAt 方法获取 Cell 在您需要的索引:

row.Elements<Cell>().ElementAt(7); //gives the 8th Cell in row - read the "HOWEVER" section!!

使用上面的方法,你的 foreach 会变成这样:

foreach (Row row in rows.ToList())
{
    if (i == 0)
        i = i + 1;
    else
    {
        //get the cell at index 7
        Cell cell = row.Elements<Cell>().ElementAt(7); //read the warning below

        //check the type
        if (cell.DataType != null && cell.DataType == CellValues.SharedString)
        {
            //it's a shared string so use the cell inner text as the index into the 
            //shared strings table
            Console.WriteLine(sst.ChildElements[int.Parse(cell.InnerText)].InnerText);
        }
        else
        {
            //it's NOT a shared string, output the value directly
            Console.WriteLine(cell.InnerText);
        }

        i = i + 1;
    }

}

然而...

上面的代码可以工作,但是按照您尝试的方式对单元格进行索引很容易出错。 OpenXML 模式允许从文件中省略空单元格(和行)。这意味着如果您在某个地方有一个空单元格,您最终可能会选择错误的值。

例如,我在Excel中创建了一个具有以下结构的文件:

运行 该文件的上述代码产生输出:

9
8

请注意,在我们解析的第一行中,我们最终从 I2 中获取了值,但在我们读取的第二行中,我们从 H3 中获取了值。我们读取的第一行超出了一个(假设第 7 个索引意味着您想要列 H),因为该行的 XML 中没有列 B。这就是为什么您会发现大多数读取 Excel 文件的代码都使用循环来遍历单元格的原因。