Convert.ChangeType 不接受 "1E-5" Decimal 的科学记数法,如何解决?



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);
}
}
}

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

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

这个问题解释了如何修复对于decimal.Parse(从指数表示法解析一个数字),但我这个通用的方法是用于加载各种文件数据从CSV文件在许多地方…是否有类似的修复方法来避免大量的代码重构?

这看起来很难看,但这里有一个如何实现特定类型行为的示例:

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));
}

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

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

不允许使用指数。如果你阅读Parse的文档,你会看到完整的数字格式是

[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);

最新更新