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

时间:2016-01-22 04:52:01

标签: asp.net-mvc openxml

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

在下面的代码块中,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;
            }
        }
    }
}

2 个答案:

答案 0 :(得分:0)

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

假设您有一张工作表。

这是你获取行集合的方式。

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
}

答案 1 :(得分:0)

  

在下面的代码块中,sst.ChildElements [7] .InnerText返回第一行第7列的内容,但每次行循环来自同一个CELL的内容!

这是因为您始终在SharedStringsTable(您的7变量)中读取元素sst中的值,而不是Row中元素7中的值(你的row变量。)

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

您可以通过查看DataType属性来判断Cell对象是否包含共享字符串索引(请注意,字符串可以存储在线和其他数字等数据类型总是以内联方式存储。

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

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

要按索引获取行单元格,您可以获取Cell的子Row,然后使用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中创建了一个文件,其结构如下: Excel file with empty B2 cell

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

  

9
  8

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