C#使用decimal TryParse转换时,字符串中有逗号的坑
文章目录
起源
一切都要从那个线上bug说起,执行decimal.TryParse时程序并没有按照预期输出结果💔💔💔
|
|
因为按照我的理解这里是不能成功转换的,所以排查问题的着重点根本就没放在这,以至于整个排查过程也显得颇为辛苦。
查看究竟
去MSDN上翻帮助文档,发现TryParse有这样一个重载方法

看到Globalization的时候就知道凉了一半,在往下看示例,果然,货币符号 、千分位 、小数点 全出来了。

现在问题很明显了,我的代码没有转换失败很有可能就是因为逗号被识别成了千分位。
反编译一下,发现转换的时候确实使用了一个默认值 System.Globalization.NumberStyles.Number。

继续来看这个枚举值,微软官方文档里给的说明如下:
指示使用
AllowLeadingWhite、AllowTrailingWhite、AllowLeadingSign、AllowTrailingSign、AllowDecimalPoint和AllowThousands样式。 这是复合数字样式。
我们从最后一个类型AllowThousands可以看出来,默认确实使用了千分位,所以在上述示例中"1,2,3"的确能够成功的完成转换。
问题延申
默认使用了千分位解析没错,但是"1,2,3"这个字符串的逗号也没在实际的千分位位置,这又是咋回事呢,想不通。😨😨😨
继续往下看,看看AllowThousands又是如何定义的。
指示数字字符串可以具有组分隔符,例如将百位与千位分隔开来的符号。 如果
NumberStyles值包括AllowCurrencySymbol标志,要分析的字符串包括货币符号,则有效组分隔符字符由CurrencyGroupSeparator属性确定,且每个组中的位数由CurrencyGroupSizes属性确定。 否则,有效的组分隔符字符由NumberGroupSeparator属性确定,每组的位数由NumberGroupSizes属性确定。
从文档中我们知道每组数字的位数由NumberGroupSizes来决定。
回过头来继续看刚才反编译的代码,FormatInfo的参数使用的NumberFormatInfo.CurrentInfo,现在我们看下NumberGroupSizes的值到底是多少(调试发现默认千分位每组的位数是3)。

到这里我已经是摸不到头绪了,所以去网上搜索了一下,但是结果也不理想,总结一下网上就两种结论:
- 有逗号时可以转换成功
- 如果要验证千分位的位置,使用正则来辅助
打破砂锅干到底
网上找不到满意的答案,那就只能研究一下.Net的源码了,所以我对decimal.TryParse方法进行了调试,终于弄清了这个原因。下面放一部分关键代码:

实际上在转换的时候,.NET将需要转换的字符串转换成了字符指针,然后逐字符去做对比。当要对比的字符等于千分位的分隔符时直接跳过,继续对比下一位。因此,不管字符串中有多少个分隔符都是可以转换的,比如下面这个例子:
|
|
当然,实际上这个转换的过程远比我描述的复杂,我这里只是针对性的分析了一下而已。 至此,整个问题都找到了答案,圆满收官😏😏
