OpenXML для создания DataTable из Excel – значение денежной ячейки неверно

Я пытаюсь создать datatable из таблицы Excel с использованием OpenXML. При получении значения ячейки строки с использованием Cell.CellValue.innerXml значение, возвращаемое для денежного значения, введенное пользователем и видимое в электронной таблице, не является интерпретированным значением.

Ячейка электронных таблиц форматируется как текст, а значение ячейки – 570,81. При получении данных в OpenXML значение интерпретируется как 570.80999999999995.

Этот метод используется для многих разных импортеров excel, где тип данных для ячейки по заголовку или индексу столбца неизвестен при построении таблицы.

Я видел несколько сообщений о стандарте форматов файлов Open XML в формате Ecma и упоминании о numFmtId. Может ли это быть ценным?

Я предполагаю, что поскольку тип данных является текстом, а число имеет два десятичных знака, должно быть какое-то предположение, что ячейка округлена (хотя формула не существует).

Я надеюсь, кто-то может предложить решение для правильной интерпретации данных.

Ниже приведен метод 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; } } 

Как вы указываете в своем вопросе, формат сохраняется отдельно от значения ячейки, используя форматы чисел в таблице стилей.

Вы должны иметь возможность расширять код, который у вас есть для форматирования дат, включая форматирование чисел. По существу вам нужно захватить NumberingFormat которая соответствует cellFormat.NumberFormatId.Value вы уже читаете. NumberingFormat можно найти в элементах styleSheet.NumberingFormats .

После этого вы можете получить доступ к свойству FormatCode для NumberingFormat который затем можно использовать для форматирования ваших данных по своему усмотрению.

К сожалению, формат не такой уж простой в использовании. Во-первых, согласно MSDN, здесь не все форматы записываются в файл, поэтому, я думаю, вам придется иметь доступ к ним где-нибудь и загружать их в зависимости от NumberFormatId вас NumberFormatId .

Во-вторых, формат строки формата не совместим с C #, поэтому вам нужно будет сделать некоторые манипуляции. Подробные сведения о формате формата можно найти в MSDN здесь .

Я сбил несколько примеров кода, который обрабатывает валютную ситуацию, которая у вас есть в вашем вопросе, но вам, возможно, придется немного подумать о синтаксическом анализе строки формата excel в 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 .

  • Как получить доступ к флажку FormControl в листе Excel с помощью OpenXML SDK
  • Round-tripping XML -> Excel -> XML
  • Чтение даты из файла Excel с использованием OpenXML
  • Добавление данных в существующее excel с использованием открытого xml sdk 2.5
  • Связь между листом и рабочим листом
  • Open XML SDK v2.0 Проблема с производительностью при удалении первой строки в 20 000 + строк файла Excel
  • Использование шаблона с OpenXML и SAX
  • Как сгенерировать образцы SpreasheetML для всех трех параметров совместимости дат из Excel 2010?
  • Open XML SDK - сохранить файл шаблона (.xltx в .xlsx)
  • Используя Open XML, как вы вставляете формулу в лист Excel 2010?
  • OpenXML SDK (C #): копировать все комментарии из одной книги Excel в другую
  • Давайте будем гением компьютера.