项目背景:基于.NET Framework 2.0的Rich Client程序。

一日,风和日丽,办公室清静,程序员老李正坐在自己位子上,气定神闲地喝着咖啡。

“大事不妙!”小周忽然来报。

“何事如此慌张?”

“数字校验方法有异!”

“此法千锤百炼,自.NET Framework升级以来,已历三载,未有显著变动,何以有异?莫非幻觉?”

“非也,今有QA,误输入逗号于数字中即提交,本想数字校验时当报其格式错误,谁知校验方法竟信以为真,放它通过了校验。以至于数据库接收到非预期输入,抛异常矣!”

老李按QA步骤重现,果然如此,遂忆起该方法的实现来。这段程序本是在.NET 1.1 Framework下写成,当时只能使用decimal.Parse(string s)方法,若字符串不合法,该方法会抛出异常,若逢逗号,也是当作不合法,抛出异常了事。可这抛出异常的方式却有局限,try-catch若逢异常,额外性能开销可观。我们只关心数字是否合法,不关心异常种类,所以当年老李将.NET Framework升级到2.0之后,改弦更张,使用Framework为了避免抛出异常而提供的decimal.TryParse(string s,out decimal result)方法。这个方法尝试将一个字符串转换成decimal类型的数字,返回值表示转换是否成功,转换得到的数字保存在result参数中。如果验证不通过,不会提示遇到了哪种错误。

老李又忆起小学数学,中学数学和大学数学,大学数学已端的难以忆起,以老李的知识,若只传入一个仅包含阿拉伯数字和逗号的字符串,除非按三个数字放在一组,否则转换应当不成功。可事实并非如此。在Windows XP .NET 2.0 Framework,en-US语言环境下,老李传入“123,45”,TryParse返回的结果为true,怪哉!

老李去google了一下这个现象,发现果然有不少人因为这个问题困扰过,只不过有的人困扰的原因不一样:他们需要把句点转成逗号作为小数点!这颠覆了他的知识——他一直以为,小数点就应该是用句点表示的。

谬矣!

在英文维基百科上,有个条目叫Decimal mark,这维基条目命名得严谨,用了mark,而非point,其中必有文章:原来使用逗号作为小数点的国家还真不少,著名的有俄罗斯,德国,法国。事实上,整个欧洲除了英国,基本都用逗号做小数点。再仔细看,使用逗号的国家比使用句点的多多了,只不过使用句点的人口可能更多些,因为使用句点的国家包括中国,印度,美国,英国。

那么,为什么那些国家会使用逗号作为小数点呢?不得不提到西方表示数字的一种传统:每3位数字并为一组,要么用逗号,要么用句号分隔。西方印刷术刚刚发明时,在法国,德国等欧洲大陆国家,为了罗马数字的可读性,使用句点作为数字分组符,逗号分隔整数部分和小数部分;而英国则反其道而行之,使用逗号分隔数字,句点分隔整数和小数。

由于双方各行其是,又各自在势力范围内推广,小数点难以统一了。这时有一标准化组织出马,召集多方代表会谈,终于制定了标准:句点和逗号都是合法的小数点,但是数组分隔符不能再用句点或逗号,只能用空格表示。欲知决议详情,且看这个网页。

这样看来,.NET Framework的TryParse方法认为带逗号的数字合法,就不足为怪了。不过,TryParse方法并没有采用符合上述标准的定义来解析数字,而是将逗号作为数字分组符!这样的话,“123,45”会被解析为12345,而且即使原始字符串包含多个逗号,也会被认为是合法的。

另外,表示数字的方式还有很多种,除了我们比较熟悉的阿拉伯数字和罗马数字,常用的还有“阿拉伯文数字”。此外老李还注意到一种叫做苏州码子的数字表现形式,这种形式在大陆和台湾已经绝迹了,但香港还在使用,Unicode也收录了,但把字符集错误命名为杭州了。

原因已明,问题待修。系统用户多为华人与老美,但也有和法德一样习惯的荷兰用户,不可厚此薄彼,有所偏废。老李眉头一皱,计上心来,按照客户端所在电脑的区域和语言设置,获取当地的合法小数点,这样逗号和句点就不能在同一个数字里相容了。编码,写单元测试,运行,问题解决。

“老李,你怎么还是一直用‘小数点’这个词啊?”

“积习难改⋯⋯”