【问题标题】:Convert.ChangeType doesn't accept "1E-5" scientific notation for Decimal, how to fix?Convert.ChangeType 不接受 Decimal 的“1E-5”科学记数法,如何解决?
【发布时间】:2021-09-09 21:06:53
【问题描述】:

MRE:https://dotnetfiddle.net/M7WeMH

我有这个通用实用程序:

    static T readValue<T>(string value) 
    {
        return (T)Convert.ChangeType(value, typeof(T));
    }

从一个文件中读取一些应该都是decimal 值的数据,当文本是科学记数法时我得到一个例外,例如"1E-5" - 这在文件中很少发生。进一步测试发现decimal.Parse也有同样的问题:

using System;
                    
public class Program
{
    static T readValue<T>(string value) 
    {
        return (T)Convert.ChangeType(value, typeof(T));
    }

    public static void Main()
    {
        try
        {
            Console.WriteLine($"{readValue<decimal>("1E-5")}");
        }
        catch(Exception e)
        {
            Console.WriteLine(e);
        }
        try
        {
            Console.WriteLine($"{decimal.Parse("1E-5")}");
        }
        catch(Exception e)
        {
            Console.WriteLine(e);
        }
    }
}

System.FormatException:输入字符串的格式不正确。
在 System.Number.ThrowOverflowOrFormatException(ParsingStatus 状态, TypeCode 类型)在 System.Number.ParseDecimal(ReadOnlySpan`1 值, NumberStyles 样式,NumberFormatInfo 信息)在 System.Convert.ToDecimal(字符串值,IFormatProvider 提供程序)在 System.String.System.IConvertible.ToDecimal(IFormatProvider 提供程序) 在 System.Convert.ChangeType(对象值,类型转换类型, IFormatProvider 提供程序)在 System.Convert.ChangeType(Object 值,类型转换类型)在 Program.readValue[T](字符串值)
在 Program.Main()

System.FormatException:输入字符串的格式不正确。
在 System.Number.ThrowOverflowOrFormatException(ParsingStatus 状态, TypeCode 类型)在 System.Number.ParseDecimal(ReadOnlySpan`1 值, NumberStyles 样式,NumberFormatInfo 信息)在 System.Decimal.Parse(String s) at Program.Main()

这个问题解释了如何修复它decimal.Parse (Parse a Number from Exponential Notation) 但我这个通用方法用于在许多地方从 CSV 文件加载各种文件数据......是否有等效的修复方法来避免大量代码重构?

【问题讨论】:

  • 您的方法绝对不会阻止您切换内部并对科学记数法等异常值执行不同的行为。
  • 说真的,被标记为我明确包含在我的问题的部分中的问题的欺骗......这不能回答我关于以通用方法执行此操作的问题。
  • 当我看到它重新打开时我会发布一个答案,但现在你需要三个部分:if (typeof(T) == typeof(decimal)) 用于基于类型的控制流(你可以将typeof(T) 放入一个变量中以进行多个检查)。检查value 字符串中的科学记数法。一系列复杂的解析、转换和转换:return (T)Convert.ChangeType(decimal.Parse(value, NumberStyles.Float), typeof(T));
  • @Logarr 最后一步是我遇到的问题。我以为你可以打开类型并返回显式类型,但我猜编译器不够智能:)
  • @Logarr 但是,这让我可以轻松地完成工作,所以感谢您抽出宝贵的时间!

标签: c# .net-5


【解决方案1】:

这很难看,但这里有一个如何完成特定类型行为的示例:

static T readValue<T>(string value) where T : struct
{
    var type = typeof(T);
    if (type == typeof(decimal))
    {
        // Put this return statement in a block that verifies the content of the string is scientific notation.
        return (T)Convert.ChangeType(decimal.Parse(value, NumberStyles.Float), typeof(T));
    }
    return (T)Convert.ChangeType(value, typeof(T));
}

【讨论】:

    【解决方案2】:

    原因是因为Decimal.ParseDecimal.TryParse使用的默认样式是这样的

    [ws][sign][digits,]digits[.fractional-digits][ws]
    

    不允许使用指数。如果您阅读Parse's documentation,您会看到完整的数字格式是

    [ws][$][sign][digits,]digits[.fractional-digits][e[sign]digits][ws]
    

    在哪里

    e:“e”或“E”字符,表示该值以指数表示法表示。如果style 包含NumberStyles.AllowExponent 标志,则s 参数可以用指数表示法表示数字。

    这意味着您需要如上所述使用NumberStyles.Float(包括NumberStyles.AllowExponent)才能使其工作。一些简单的演示:

    var style = System.Globalization.NumberStyles.Float;
    var culture = CultureInfo.GetCultureInfo("en-US");
    string s = "1E-5";
    var i = decimal.Parse(s, style);
    decimal dec = 0;
    decimal.TryParse(s, style, culture, out dec);
    

    【讨论】:

    • 这个问题与 decimal.parse 无关...我包含一个问题的链接,该问题在我的问题中解释了这一点
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-22
    • 1970-01-01
    • 2022-11-17
    • 1970-01-01
    • 2010-10-12
    相关资源
    最近更新 更多