来看一个比较长的数字在.NET下的输出:
//+ using System.Globalization;
//+ using System.Threading;
//把当前线程的CultureInfo改成中文-中国"
Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("zh-cn");
decimal m = 987654321;
Console.WriteLine(m);
Console.WriteLine(m.ToString("N"));
Console.WriteLine(m.ToString("C"));
输出:
987654321
987,654,321.00
¥987,654,321.00
显然第一种输出最不可取,没有分隔符,太难分辨数位了。
但是后面的输出虽然用了格式化输出符,并且当前线程的CultureInfo也是“中文-中国”,但是.NET Framework还是会按照英文数字的模式进行输出(除了货币符号是中文的¥)。这是为了“国际化”?
管它什么化,反正这个数字中国人读起来肯定不爽。因为我们从小学习数字读法都是以4个位做分隔:个十百千,万十万百万千万,亿。九个数正好是九亿开始。而这个数是以3位做分隔,我们还是得数位数。这个数其实还是为熟悉英语的人服务的,他们可以很好得读出来:nine hundred and eighty-seven million, six hundred and fifty-four thousand, three hundred and twenty-one。人家直接把两个逗号读成million和thousand就可以了。
改进方法就是自定义CultureInfo(事实上是在自定义一个IFormatProvider,后面会讲),设置的对象就是CultureInfo的NumberFormatInfo。
NumberFormatInfo中的GroupSizes和GroupSeparator对应数字的数位分隔长度和分隔符。另外NumberFormatInfo中的属性的设置根据Currency,Number和Percentage分别对应输出时的C,G和P格式化标识符。(当直接调用ToString没有任何参数相当于G参数)。
下面程序实例中就以CurrencyGroupSizes,CurrencyGroupSeparator和NumberGroupSizes,NumberGroupSeparator做示例,读者可以举一反三。当然NumberFormatInfo不限于这些属性,推荐读者阅读下MSDN做延伸:http://msdn.microsoft.com/zh-cn/library/system.globalization.numberformatinfo
我们的目的很简单,把3位分隔改成4位分隔,并且把分隔符逗号替换成空格。(因为有些人会把逗号错看成小数点)
代码:
//+ using System.Globalization;
//+ using System.Threading;
//修改CultureInfo的NumberFormatInfo
var culture = (CultureInfo)CultureInfo.GetCultureInfo("zh-cn").Clone();
culture.NumberFormat.NumberGroupSizes =
culture.NumberFormat.CurrencyGroupSizes = new int[] { 4 };
culture.NumberFormat.NumberGroupSeparator =
culture.NumberFormat.CurrencyGroupSeparator = " ";
//设置成当前线程的CultureInfo
Thread.CurrentThread.CurrentCulture = culture;
decimal m = 987654321;
Console.WriteLine(m.ToString("N"));
Console.WriteLine(m.ToString("C"));
输出:
9 8765 4321.00
¥9 8765 4321.00
很好!这下什么数字一目了然了。九亿 八千七百六十五万 四千三百二十一!
还有一个问题,如果你不方便修改线程的CultureInfo的话(或许你的程序依赖当前操作系统的CultureInfo不能随意修改,因为一旦修改了线程的CultureInfo,所有输出都会以这种方式的)。其实细细学.NET的话,你会发现CultureInfo和NumberFormatInfo都继承IFormatProvider接口,同时.NET中的字符输出方法(ToString和String.Format方法)都提供自定义的IFormatProvider的。
代码:
//+ using System.Globalization;
//修改CultureInfo的NumberFormatInfo
var culture = (CultureInfo)CultureInfo.GetCultureInfo("zh-cn").Clone();
culture.NumberFormat.NumberGroupSizes =
culture.NumberFormat.CurrencyGroupSizes = new int[] { 4 };
culture.NumberFormat.NumberGroupSeparator =
culture.NumberFormat.CurrencyGroupSeparator = " ";
decimal m = 987654321;
//使用当前线程的CultureInfo
Console.WriteLine(m.ToString("N"));
//使用CultureInfo作为IFormatProvider
Console.WriteLine(m.ToString("N", culture));
//使用NumberFormatInfo作为IFormatProvider
Console.WriteLine(m.ToString("N", culture.NumberFormat));
//String.Format方法也有IFormatProvider
Console.WriteLine(String.Format(culture, "{0:N}", m));
输出:
987,654,321.00
9 8765 4321.00
9 8765 4321.00
9 8765 4321.00