【问题标题】:Parsing floating value from XML losing decimal places (C#)从丢失小数位的 XML 中解析浮点值(C#)
【发布时间】:2019-09-29 19:04:50
【问题描述】:

我在 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?

【问题讨论】:

    标签: c# xml parsing unity3d xml-parsing


    【解决方案1】:

    在您的问题中,您的文本中的双精度值使用逗号小数分隔符进行格式化:7,370618E+07。这意味着您计算机上的current locale(由Thread.CurrentCulture 表示)使用此分隔符。

    但是,XML 文件中的数字使用句点小数分隔符进行格式化:737061.75double.Parse() 无法正确解析它们,因为使用为当前线程文化初始化的NumberFormatInfo 对象中的格式信息解释输入字符串。我能够使用通过将当前的 culure 更改为 new CultureInfo("de-DE") 来调整 here

    由于 XML 文件通常使用不变的文化进行格式化,因此您应该使用 invariant settings 进行解析:

    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 类中的实用程序:

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

    此类提供了在公共语言运行时类型和 XML 架构定义语言 (XSD) 类型之间进行转换的方法。转换数据类型时,返回的值与语言环境无关。 因此,它封装了有关基本类型格式化的 XML 约定的详细信息。

    演示小提琴 #2 显示上述修复 here


    作为另一种更简单的选择,尝试使用 LINQ to XML 解析您的 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 支持直接转换为 doubledecimal,无需任何手动解析。

    演示小提琴#3 here.


    最后,在你的代码中:

    double endTimeValue = double.Parse(startTime);
    

    我认为这是您问题中的错字,应该改为:

    double endTimeValue = double.Parse(endTime);
    

    【讨论】:

    • 感谢您的回复。不幸的是,当我使用“InvariantInfo”时,我得到的结果与以前相同。当我使用 XmlConvert 时,我收到读取 endTime 值的无效数据格式的异常...
    • @Allwo - 我可以在这里重现您的问题dotnetfiddle.net/0XUZGt,我的解决方案在这里dotnetfiddle.net/bEXfTc。因此,要么 1)unity3d 出现问题,要么 2)您没有向我们展示真正的 minimal reproducible example。您能否提供有关您的 unity3d 环境和版本的详细信息,并展示您如何初始化 node 变量?目前您的代码无法编译,因为 node 未定义。我以为是document.DocumentElement,但也许我错了。
    • 我把问题代码中的“node”换成了“root”,也就是根节点。正如@Maxim_A 所建议的那样,我发现读取 XML 字符串会返回正确的结果,从 XML 文件中读取会返回一些转换后的内容。将 XML 字符串放入文件并加载时会发生什么?
    • @Allwo - 如果从字符串中读取工作正常,但从文件中读取不正常,那么 XmlTextReader 内部使用的 XmlTextReader 在统一时出现问题 - 因为 XmlTextReader 应该甚至没有尝试将文本值识别和解析为双精度值,当然也不会在 .Net 或 .Net 核心上这样做。一种可能性是使用XmlReader.Create() 手动创建您自己的XmlReader,如下所示,看看问题是否消失:dotnetfiddle.net/T24LjY。另外,你能在 unity 上使用 LINQ to XML 吗?
    • 我发现了问题。你所有的答案都是正确的,我可以在其他课程中重现它们。在内部,输入 XML 路径已更改并导致另一个文件,其中包含不同格式的数字。非常感谢您的帮助,它帮助我排除了追查真正错误的选项。
    【解决方案2】:

    我在 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
    

    我建议您检查文件的编码并使用相同的编码读取它。并检查计算机上安装的本地化。

    【讨论】:

    • 我添加了 System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");到我的功能的开始,但它没有任何区别。即使我的计算机上有另一种语言设置,我也不敢相信我必须更改整个工作环境才能实现正确的 XML 浮动解析。我只想拥有写在我的 XML 文件中的确切数字,而不是未经请求的转换。
    • @Allwo 如果您使用我的选项将字符串加载为 XML 文档,您还有解析问题吗?
    • 不,实际上使用 LoadXML 就可以了。关键是 XML 文件中的某种语言编码吗?我现在的标题是一个简单的
    • var SourceFile = File.ReadAllText("path_to_file", Encoding.UTF8); document.LoadXml(SourceFile);尝试这种方式来读取您的文件并将文件的文本加载到文档中
    • 结果相同的错误 7,370618E+07 和 73706180000000
    猜你喜欢
    • 1970-01-01
    • 2016-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 2011-05-25
    • 1970-01-01
    相关资源
    最近更新 更多