Разбор плавающего значения из XML с потерей десятичных знаков (C #) - PullRequest
0 голосов
/ 29 сентября 2019

У меня очень неловкое поведение в C # (внутри Unity, использующем VS2019), которое сводит меня с ума на несколько дней, и я действительно буду признателен за вашу помощь.

У меня очень простой XML-файл,в этом простом случае содержит только корневой узел и пару атрибутов:

<?xml version="1.0" encoding="utf-8"?>
<root bit_depth="8" end_datetime="737061.75" start_datetime="737061">
</root>

И я пытаюсь прочитать его:

    XmlDocument document = new XmlDocument();
    document.Load( _projectFilePath );
    XmlElement root = document.DocumentElement;

    System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo( "en-US" );

    string startTime = root.Attributes[ "start_datetime" ].Value;
    Debug.Log( "1st::: Read startTime number: " + startTime );
    double startTimeValue = double.Parse(startTime);
    Debug.Log( "2nd::: Parse startTime number: " + startTimeValue );

    string endTime = root.Attributes[ "end_datetime" ].Value;
    Debug.Log( "1st::: Read endTime number: " + endTime );
    double endTimeValue = double.Parse( endTime, CultureInfo.InvariantCulture );
    Debug.Log( "2nd::: Parse endTime number: " + endTimeValue );

В результате получается следующее:

1st::: Read startTime number: 737061
2nd::: Parse startTime number: 737061

1st::: Read endTime number: 7,370618E+07
2nd::: Parse endTime number: 73706180000000

Просто ... почему?!?!?!?! Почему это плавающее число портится до 7,370618E + 07, когда я явно разбираю double?

Ответы [ 2 ]

2 голосов
/ 29 сентября 2019

В вашем вопросе двойники в вашем тексте отформатированы с помощью десятичного разделителя запятой : 7,370618E+07. Это означает, что в текущем языковом стандарте на вашем компьютере (представленном Thread.CurrentCulture) используется этот разделитель.

Однако числа в файле XML отформатированы с использованием десятичного разделителя period : 737061.75. double.Parse() не будет анализировать их правильно, поскольку входная строка интерпретируется с использованием информации о форматировании в NumberFormatInfo объекте, который инициализирован для текущей культуры потоков. Я смог воспроизвести эту проблему, используя скрипту здесь , изменив текущую версию на new CultureInfo("de-DE").

Поскольку файлы XML обычно форматируются с использованием инвариантной культуры, вы должны проанализировать, используя инвариантные настройки :

double startTimeValue = double.Parse(startTime, NumberFormatInfo.InvariantInfo);
double endTimeValue = double.Parse(endTime, NumberFormatInfo.InvariantInfo);

Или используйте System.Globalization.CultureInfo.InvariantCulture:

double startTimeValue = double.Parse(startTime, System.Globalization.CultureInfo.InvariantCulture);
double endTimeValue = double.Parse(endTime,  System.Globalization.CultureInfo.InvariantCulture);

Еще лучше использовать утилиты из XmlConvert class:

double startTimeValue = XmlConvert.ToDouble(startTime);
double endTimeValue = XmlConvert.ToDouble(endTime);

Этот класс предоставляет методы для преобразования между типами среды выполнения общего языка и типами языка определения схемы XML (XSD). При преобразовании типов данных возвращаемые значения не зависят от локали. Таким образом, он инкапсулирует подробности о соглашениях XML для форматирования примитивных типов.

Демонстрационная скрипта # 2, показывающая вышеуказанные исправления здесь .


В качестве другой, еще более простой альтернативы попробуйте выполнить синтаксический анализ вашего XML с помощью LINQ to XML :

var doc = XDocument.Load(_projectFilePath);

var startTimeValue = (double)doc.Root.Attribute("start_datetime");
var endTimeValue = (double)doc.Root.Attribute("end_datetime");

LINQ to XML XATtribute поддерживает прямое приведение к double или decimal, устраняя необходимость в ручном разборе.

Демонстрационная скрипка # 3 здесь .


Наконец, в вашем коде вы делаете:

double endTimeValue = double.Parse(startTime);

Я предполагаю, что это опечатка в вашем вопросе и должно быть вместо:

double endTimeValue = double.Parse(endTime);
1 голос
/ 29 сентября 2019

Я запустил ваш код в Visual Studio и dotnetfiddle и получил следующий результат:

        XmlDocument document = new XmlDocument();
        document.LoadXml("<root bit_depth=\"8\" end_datetime=\"737061.75\" start_datetime=\"737061\"></root >");

        string startTime = document.ChildNodes[0].Attributes["start_datetime"].Value;
        Console.WriteLine("1st::: Read startTime number: " + startTime);
        double startTimeValue = double.Parse(startTime);
        Console.WriteLine("2nd::: Parse startTime number: " + startTimeValue);

        string endTime = document.ChildNodes[0].Attributes["end_datetime"].Value;
        Console.WriteLine("1st::: Read endTime number: " + endTime);
        double endTimeValue = double.Parse(startTime);
        Console.WriteLine("2nd::: Parse endTime number: " + endTimeValue);

        1st::: Read startTime number: 737061
        2nd::: Parse startTime number: 737061
        1st::: Read endTime number: 737061.75
        2nd::: Parse endTime number: 737061

Я советую вам проверить кодировку вашего файла и прочитать его также с той же кодировкой. А также проверьте локализацию, установленную на компьютере.

...