OpenXML从Excel创buildDataTable – 资金单元格值不正确

我正在尝试使用OpenXML从Excel电子表格创build一个数据表。 当使用Cell.CellValue.innerXml获取一行的单元格值时,用户input的并在电子表格上可见的货币值的返回值与解释的值不同。

电子表格单元格格式为文本,单元格值为570.81。 在OpenXML中获取数据时,该值被解释为570.80999999999995。

此方法用于许多不同的excel导入,其中按表头或列索引的单元格数据types在构build表时不知道。

我见过一些关于Ecma Office Open XML文件格式标准的文章,并提到了numFmtId。 这可能是有价值的吗?

我假设由于数据types是文本,并且数字有两位小数,所以必须假定单元格已经被四舍五入(即使没有公式存在)。

我希望有人可以提供一个解决scheme来正确解释数据。

下面是GetCellValue方法:

private static string GetCellValue(SharedStringTablePart stringTablePart, DocumentFormat.OpenXml.Spreadsheet.Cell cell,DocumentFormat.OpenXml.Spreadsheet.Stylesheet styleSheet) { string value = cell.CellValue.InnerXml; if (cell.DataType != null && cell.DataType.Value == DocumentFormat.OpenXml.Spreadsheet.CellValues.SharedString) { return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText; } else { if (cell.StyleIndex != null) { DocumentFormat.OpenXml.Spreadsheet.CellFormat cellFormat = (DocumentFormat.OpenXml.Spreadsheet.CellFormat)styleSheet.CellFormats.ChildElements[(int)cell.StyleIndex.Value]; int formatId = (int)cellFormat.NumberFormatId.Value; if (formatId == 14) //[h]:mm:ss { DateTime newDate = DateTime.FromOADate(double.Parse(value)); value = newDate.Date.ToString(CultureInfo.InvariantCulture); } } return value; } } 

正如你在你的问题中指出的那样,格式与样式表中的数字格式是分开存储的。

您应该能够扩展您用于格式化date的代码,以包含数字的格式。 实际上,您需要获取与您正在阅读的cellFormat.NumberFormatId.Value对应的NumberingFormatNumberingFormat可以在styleSheet.NumberingFormats元素中find。

一旦你有了这个,你可以访问NumberingFormatFormatCode属性,然后你可以用它来格式化你的数据。

不幸的是,格式不是那么简单的使用。 首先,根据MSDN 在这里不是所有的格式都写入文件,所以我想你将不得不有那些地方访问和加载它们取决于你有的NumberFormatId

其次格式string的格式不能与C#兼容,所以你需要做一些操作。 格式布局的细节可以在MSDN上find 。

我已经敲了一些示例代码来处理您的问题中的货币情况,但是您可能需要考虑将excel格式stringparsing为C#之一。

 private static string GetCellValue(SharedStringTablePart stringTablePart, DocumentFormat.OpenXml.Spreadsheet.Cell cell, DocumentFormat.OpenXml.Spreadsheet.Stylesheet styleSheet) { string value = cell.CellValue.InnerXml; if (cell.DataType != null && cell.DataType.Value == DocumentFormat.OpenXml.Spreadsheet.CellValues.SharedString) { return stringTablePart.SharedStringTable.ChildElements[Int32.Parse(value)].InnerText; } else { if (cell.StyleIndex != null) { DocumentFormat.OpenXml.Spreadsheet.CellFormat cellFormat = (DocumentFormat.OpenXml.Spreadsheet.CellFormat)styleSheet.CellFormats.ChildElements[(int)cell.StyleIndex.Value]; int formatId = (int)cellFormat.NumberFormatId.Value; if (formatId == 14) //[h]:mm:ss { DateTime newDate = DateTime.FromOADate(double.Parse(value)); value = newDate.Date.ToString(CultureInfo.InvariantCulture); } else { //find the number format NumberingFormat format = styleSheet.NumberingFormats.Elements<NumberingFormat>() .FirstOrDefault(n => n.NumberFormatId == formatId); double temp; if (format != null && format.FormatCode.HasValue && double.TryParse(value, out temp)) { //we have a format and a value that can be represented as a double string actualFormat = GetActualFormat(format.FormatCode, temp); value = temp.ToString(actualFormat); } } } return value; } } private static string GetActualFormat(StringValue formatCode, double value) { //the format is actually 4 formats split by a semi-colon //0 for positive, 1 for negative, 2 for zero (I'm ignoring the 4th format which is for text) string[] formatComponents = formatCode.Value.Split(';'); int elementToUse = value > 0 ? 0 : (value < 0 ? 1 : 2); string actualFormat = formatComponents[elementToUse]; actualFormat = RemoveUnwantedCharacters(actualFormat, '_'); actualFormat = RemoveUnwantedCharacters(actualFormat, '*'); //backslashes are an escape character it seems - I'm ignoring them return actualFormat.Replace("\"", ""); ; } private static string RemoveUnwantedCharacters(string excelFormat, char character) { /* The _ and * characters are used to control lining up of characters they are followed by the character being manipulated so I'm ignoring both the _ and * and the character immediately following them. Note that this is buggy as I don't check for the preceeding backslash escape character which I probably should */ int index = excelFormat.IndexOf(character); int occurance = 0; while (index != -1) { //replace the occurance at index using substring excelFormat = excelFormat.Substring(0, index) + excelFormat.Substring(index + 2); occurance++; index = excelFormat.IndexOf(character, index); } return excelFormat; } 

给定一个值为570.80999999999995的表格,使用货币格式(在英国),我得到的输出是£570.81