c# - 在 Excel 中编辑后 OpenXML 无法读取电子表格数据
问题描述
我正在使用 OpenXML 将一些翻译数据导出到 Excel 电子表格,然后将相同的电子表格重新导入我的程序。
现在,如果我直接导出和导入,这工作得非常好,但是,如果我在 excel 中打开电子表格并编辑它或只是保存它,我的程序将无法读取数据,就好像它已损坏一样。但是,如果我再次打开它,Excel 会很好地查看它,因此文件实际上并没有损坏。
导出数据
using (SpreadsheetDocument document = SpreadsheetDocument.Create(saveLocation, SpreadsheetDocumentType.Workbook))
...
//Add a WorkbookPart to the document
WorkbookPart workbookPart = document.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
//Add a WorksheetPart to the WorkbookPart
WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet();
//Add style
WorkbookStylesPart stylePart = workbookPart.AddNewPart<WorkbookStylesPart>();
stylePart.Stylesheet = GenerateStylesheet();
stylePart.Stylesheet.Save();
...
SheetData sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());
//Append some rows
...
worksheetPart.Worksheet.Save();
阅读电子表格
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (SpreadsheetDocument document = SpreadsheetDocument.Open(fileStream, false))
{
WorkbookPart workbookPart = document.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
var headerRow = sheetData.Elements<Row>().FirstOrDefault();
...
//All data here are numbers
所以最初我认为可能更多的工作表是由 excel 创建的,我认为 .First() 会产生正确的工作表是错误的,但是在调试时查看对象表明数据在某种程度上是正确的。所以很明显这里出了点问题,所以我决定查看 .xlsx 文件的存档内容。
这是在我的程序导出并创建文件之后。可以看到xml文件中有一些实际的数据(字符串)。
这是在我在 Excel 中打开电子表格并保存它而不进行任何编辑之后。
如果它是某种预期的行为,我不知道为什么会发生这种情况。我发现 OpenXML 充满了奇怪的设计选择,但是在我穿过迷宫之后,这是真正阻止我前进的第一件事。
我在 macOS 上基于 Netcore 2.0 构建,显然 Excel 也在 macOS 上运行。
如果有人能帮我解决这个问题,我会很高兴。
解决方案
在 Excel 文件中存储字符串有两种方法;使用您在创建时所做的内联字符串,或者正如 rene 在评论中指出的那样,使用名为 SharedStringTable 的单独 XML 文件。Excel 恰好使用后一种方法,因此直接插入包含字符串的每个单元格,它们包含指向 SharedStringTable 中条目的索引。
幸运的是,您可以像阅读文档的任何其他部分一样使用 OpenXML 阅读 SharedStringTable。例如:
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (SpreadsheetDocument document = SpreadsheetDocument.Open(fileStream, false))
{
WorkbookPart workbookPart = document.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.First();
SheetData sheetData = worksheetPart.Worksheet.Elements<SheetData>().First();
SharedStringTablePart stringTable = workbookPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
var headerRow = sheetData.Elements<Row>().FirstOrDefault();
foreach (Cell c in headerRow.Elements<Cell>())
{
string cellText;
if (c.DataType == CellValues.SharedString)
{
//the value will be a number which is an index into the shared strings table
int index = int.Parse(c.CellValue.InnerText);
cellText = stringTable.SharedStringTable.ElementAt(index).InnerText;
}
else
{
//just take the value from the cell (note this won't work for some types e.g. dates)
cellText = c.CellValue.InnerText;
}
Console.WriteLine(cellText);
}
}
}
对于大文件,您可能需要考虑缓存共享字符串,如本问答中所述。
推荐阅读
- reactjs - 对 localhost 使用错误的 IP 地址做出反应
- python - 如何使碰撞在此侧滚动条中起作用?
- sql - MS Access 查询不返回任何结果
- python - 忽略 jenkins 中 pylint 扫描中的某些文件
- python - discord.py wait_for('reaction_add') 与直接消息的功能不同
- c# - 如何在命令运行时从 C# 中的 powershell 获取输出?
- javascript - 面对 - '未捕获(承诺)SyntaxError:JSON.parse:JSON数据第1行第1列的意外字符'
- python - Boto3 中的胶水更新连接返回 InvalidInputException -
- excel - 为 1 行中的不同值创建多列
- javascript - puppeteer 网页异步代码未执行