使用C# DocumentFormat.OpenXml读取Excel数值单元格异常问题咨询
问题分析与解决方案
嘿,我一眼就看出问题出在哪了——你把所有单元格都当成共享字符串来处理了,但数值类型的单元格根本不需要去SharedStringTable里找!这就是为什么数值单元格返回的总是第一行第一列内容的原因:你把数值本身当成了共享字符串的索引,而索引0对应的正好是共享字符串表里的第一个条目(也就是你看到的第一行第一列内容)。
核心问题拆解
Excel用不同的方式存储不同类型的单元格内容:
- 字符串类型:会把重复的字符串统一存在
SharedStringTable中,单元格的InnerText是该字符串在表中的索引,这时候你去查共享表是正确的。 - 数值类型(数字、日期等):单元格的
InnerText直接就是实际数值,而且这类单元格的DataType通常是CellValues.Number,甚至有时候不指定DataType(默认就是数值类型)。这时候如果还拿InnerText当索引去查共享表,自然会拿到错误的内容。
修正后的代码
下面是调整后的完整代码,我加了类型判断、空值处理和异常防护:
// 先获取共享字符串表,注意判空(有些Excel可能没有共享字符串表) var sharedStringTable = workbookPart.SharedStringTablePart?.SharedStringTable; // 去掉原有的过滤条件,因为数值单元格可能没有DataType属性,会被误过滤 List<Row> rows = sheetData.Elements<Row>().ToList(); foreach (Row row in rows) { foreach (Cell cell in row.Elements<Cell>()) { string cellValue = string.Empty; if (cell.DataType != null) { switch (cell.DataType.Value) { case CellValues.SharedString: // 仅共享字符串类型才去查询共享表 if (sharedStringTable != null && int.TryParse(cell.InnerText, out int stringIndex)) { var sharedStringItem = sharedStringTable.Elements<SharedStringItem>().ElementAtOrDefault(stringIndex); cellValue = sharedStringItem?.InnerText ?? string.Empty; } break; case CellValues.Number: // 数值类型直接读取InnerText cellValue = cell.InnerText; // 如果需要转成数值类型,可以在这里处理: // if (double.TryParse(cellValue, out double numberValue)) { ... } break; // 额外补充布尔类型的处理(可选) case CellValues.Boolean: cellValue = cell.InnerText == "1" ? "True" : "False"; break; } } else { // DataType为null时,默认是数值类型 cellValue = cell.InnerText; } Console.WriteLine(cellValue); } }
关键优化点
- 类型区分处理:通过
DataType判断单元格类型,只对共享字符串类型查询SharedStringTable。 - 空值防护:用
?.和??处理可能的空引用,避免程序崩溃。 - 异常避免:用
int.TryParse代替int.Parse,防止索引格式错误;用ElementAtOrDefault代替ElementAt,避免索引越界。 - 修正行过滤:去掉了原有的行过滤条件,避免漏掉只有数值的行(如果需要过滤空行,可以改成判断行内是否有非空单元格)。
内容的提问来源于stack exchange,提问作者asisleep
相关产品推荐
相关产品推荐

