十进制字符串与千位分隔符?

Posted

技术标签:

【中文标题】十进制字符串与千位分隔符?【英文标题】:Decimal to string with thousand's separators? 【发布时间】:2012-07-10 14:21:44 【问题描述】:

考虑一个Decimal 值:

Decimal value = -1234567890.1234789012M;

我想将此Decimal 值转换为字符串,并包含“千位分隔符”

注意:我不想包含千位分隔符,我想包含数字分组。对于不将数字分组为数千或不使用逗号分隔组的文化,这种差异很重要

在我的计算机上使用我当前的语言环境使用不同标准格式字符串的一些示例输出:

value.ToString()        =  -1234567890..1234789012   (Implicit General)
value.ToString("g")     =  -1234567890..1234789012   (General)
value.ToString("d")     =          FormatException   (Decimal whole number)
value.ToString("e")     =         -1..234568e++009   (Scientific)
value.ToString("f")     =         -1234567890..123   (Fixed Point)
value.ToString("n")     =     -12,,3456,,7890..123   (Number with commas for thousands)
value.ToString("r")     =          FormatException   (Round trippable)
value.ToString("c")     =   -$$12,,3456,,7890..123   (Currency)
value.ToString("#,0.#") =     -12,,3456,,7890..1

想要(取决于文化)是:

en-US      -1,234,567,890.1234789012
ca-ES      -1.234.567.890,1234789012
gsw-FR     -1 234 567 890,1234789012    (12/1/2012: fixed gws-FR to gsw-FR)
fr-CH      -1'234'567'890.1234789012
ar-DZ       1,234,567,890.1234789012-
prs-AF      1.234.567.890,1234789012-
ps-AF       1،234،567،890,1234789012-
as-IN     -1,23,45,67,890.1234789012
lo-LA      (1234567,890.1234789012)     (some debate if numbers should be "1,234,567,890")
qps-PLOC  12,,3456,,7890..1234789012

如何将Decimal 转换为带有数字分组的字符串?


更新:更多期望输出,使用我目前的文化:

-1234567890M             -->   -12,,3456,,7890
-1234567890.1M           -->   -12,,3456,,7890..1
-1234567890.12M          -->   -12,,3456,,7890..12
-1234567890.123M         -->   -12,,3456,,7890..123
-1234567890.1234M        -->   -12,,3456,,7890..1234
-1234567890.12347M       -->   -12,,3456,,7890..12347
-1234567890.123478M      -->   -12,,3456,,7890..123478
-1234567890.1234789M     -->   -12,,3456,,7890..1234789
-1234567890.12347890M    -->   -12,,3456,,7890..1234789
-1234567890.123478901M   -->   -12,,3456,,7890..123478901
-1234567890.1234789012M  -->   -12,,3456,,7890..1234789012

更新:我尝试查看Decimal.ToString() 如何设法使用General 格式来显示它需要显示的所有数字:

public override string ToString()

    return Number.FormatDecimal(this, null, NumberFormatInfo.CurrentInfo);

除了Number.FormatDecimal 隐藏在某处:

[MethodImpl(MethodImplOptions.InternalCall)]
public static extern string FormatDecimal(decimal value, string format, NumberFormatInfo info);

所以这是一条死胡同。

【问题讨论】:

你为什么不喜欢 value.ToString("n")? @banging 它只显示 3 位小数。我也喜欢General (value.ToString("G")),只是它不显示数字分组。我喜欢General (g)的小数和Number (n)的分组。他们一起势不可挡。 【参考方案1】:

默认情况下,ToString 小数方法将 CultureInfo.CurrentCulture 用于用户会话,因此会因运行代码的人员而异。

ToString 方法还接受各种重载中的 IFormatProvider。这是您需要提供特定于文化的格式化程序的地方。

例如,如果您为 fr-CH 传递 NumberFormat,您可以按照该文化所期望的方式格式化内容:

var culture = CultureInfo.CreateSpecificCulture("fr-CH");
Decimal value = -1234567890.1234789012M;

Console.WriteLine(value.ToString("##,#.###############", culture.NumberFormat));

会输出

 -1'234'567'890.1234789012

编辑 #3 - 使用自定义格式化程序重写。这应该根据新更新的问题做你想要的。

编辑 #4 - 接受您的所有输入,然后运行:

public void TestOutput()

    PrintValue(-1234567890M);
    PrintValue(-1234567890.1M);
    PrintValue(-1234567890.12M);
    PrintValue(-1234567890.123M);
    PrintValue(-1234567890.1234M);
    PrintValue(-1234567890.12347M);
    PrintValue(-1234567890.123478M);
    PrintValue(-1234567890.1234789M);
    PrintValue(-1234567890.12347890M);
    PrintValue(-1234567890.123478901M);
    PrintValue(-1234567890.1234789012M);


private static void PrintValue(decimal value)

   var culture = CultureInfo.CreateSpecificCulture("qps-PLOC");
   Console.WriteLine(value.ToString("##,#.###############", culture.NumberFormat));

提供与您提供的匹配的输出:

--12,,3456,,7890
--12,,3456,,7890..1
--12,,3456,,7890..12
--12,,3456,,7890..123
--12,,3456,,7890..1234
--12,,3456,,7890..12347
--12,,3456,,7890..123478
--12,,3456,,7890..1234789
--12,,3456,,7890..1234789
--12,,3456,,7890..123478901
--12,,3456,,7890..1234789012

正如 Joshua 所指出的,这仅适用于某些语言环境。

从表面上看,您需要从两个弊端中取其轻:了解数字的精确度,或指定每种文化的格式。我敢打赌知道你的数字的准确性可能会更容易。 在这种情况下,我的答案的先前版本可能有用:

要显式控制要输出的小数位数,您可以克隆区域性提供的数字格式并修改 NumberDecimalDigits 属性。

var culture = CultureInfo.CreateSpecificCulture("fr-CH");
Decimal value = -1234567890.1234789012M;
NumberFormatInfo format = (NumberFormatInfo)culture.NumberFormat.Clone();
format.NumberDecimalDigits = 30;

Console.WriteLine(value.ToString("n", format));

这个输出:

-1'234'567'890.123478901200000000000000000000

【讨论】:

将小数位数截断为CultureInfo.NumberFormat.NumberDecimalDigits 不幸的是,使用自定义格式会破坏某些文化的负数。在您的示例中,lo-LA 将打印 -1234567,890.1234789012,而它应该是 (1234567,890.1234789012)。此外,ar-DZprs-AFps-AF 的负号位于左侧,而不是右侧。 (这就是自定义格式的祸根) @Joshua ...ack,对,所以,使用数字格式化程序和 N 的克隆可能...更容易更准确? @WillHughes,我想你可以更准确地称呼它。处理文化很困难,所以我倾向于尝试并依赖微软在 .Net 框架中正确处理它。我还使用它来尝试保持一致,因为我将调用相同的底层框架。我知道甚至微软之前都弄错了,并在随后的补丁中修复了它。 这可能需要另一个问题,“如何知道十进制数中的小数位数?”。这是一个完全不同的问题,这是这个问题的重复。【参考方案2】:

您可以指定自定义模式(该模式将适当地解析为特定于文化的分组方法以及适当的分组和小数分隔符)。一个模式可以有positive, negative and zero sections。正面模式始终相同,但负面模式取决于文化,可以从 NumberFormatInfo 的NumberNegativePattern 属性中检索。由于您希望尽可能精确,因此需要在小数点后填写 28 位占位符;逗号强制分组。

    public static class DecimalFormatters
    
        public static string ToStringNoTruncation(this Decimal n, IFormatProvider format)
        
            NumberFormatInfo nfi = NumberFormatInfo.GetInstance(format);
            string[] numberNegativePatterns = 
                    "(#,0.############################)", //0:  (n)
                    "-#,0.############################",  //1:  -n
                    "- #,0.############################", //2:  - n
                    "#,0.############################-",  //3:  n-
                    "#,0.############################ -";//4:  n -
            var pattern = "#,0.############################;" + numberNegativePatterns[nfi.NumberNegativePattern];
            return n.ToString(pattern, format);
        

        public static string ToStringNoTruncation(this Decimal n)
        
            return n.ToStringNoTruncation(CultureInfo.CurrentCulture);
        
    

样本输出

Locale    Output
========  ============================
en-US     -1,234,567,890.1234789012
ca-ES     -1.234.567.890,1234789012
hr-HR     - 1.234.567.890,1234789012
gsw-FR    -1 234 567 890,1234789012
fr-CH     -1'234'567'890.1234789012
ar-DZ     1,234,567,890.1234789012-
prs-AF    1.234.567.890,1234789012-
ps-AF     1،234،567،890,1234789012-
as-IN     -1,23,45,67,890.1234789012
lo-LA     (1234567,890.1234789012)
qps-PLOC  -12,,3456,,7890..1234789012

目前没有使用NegativeNumberFormat 4 (n -) 的语言环境,因此无法测试这种情况。但没有理由认为它会失败。

【讨论】:

这很聪明,处理了五种不同的负数模式。【参考方案3】:

格式化字符串时需要包含文化。您可以使用 String.Format 并将文化作为第一个参数包含在内,也可以使用对象的 ToString 方法并使用采用文化的重载。

以下代码产生预期的输出(除了 gws-FR,它找不到具有该字符串的区域性)。

namespace CultureFormatting 
  using System;
  using System.Globalization;

  class Program 
    public static void Main() 
      Decimal value = -1234567890.1234789012M;
      Print("en-US", value);
      Print("ca-ES", value);
      //print("gws-FR", value);
      Print("fr-CH", value);
      Print("ar-DZ", value);
      Print("prs-AF", value);
      Print("ps-AF", value);
      Print("as-IN", value);
      Print("lo-LA", value);
      Print("qps-PLOC", value);
    

    static void Print(string cultureName, Decimal value) 
      CultureInfo cultureInfo = new CultureInfo(cultureName);
      cultureInfo.NumberFormat.NumberDecimalDigits = 10;
      // Or, you could replace the 1:N with 1:N10 to do the same
      // for just this string format call.
      string result = 
        String.Format(cultureInfo, "0,-8 1:N", cultureName, value);
      Console.WriteLine(result);
    
  

以上代码产生以下输出:

en-US    -1,234,567,890.1234789012
ca-ES    -1.234.567.890,1234789012
fr-CH    -1'234'567'890.1234789012
ar-DZ    1,234,567,890.1234789012-
prs-AF   1.234.567.890,1234789012-
ps-AF    1،234،567،890,1234789012-
as-IN    -1,23,45,67,890.1234789012
lo-LA    (1234567,890.1234789012)
qps-PLOC --12,,3456,,7890..1234789012

如果您使用的是多线程系统,例如 ASP.Net,您可以更改线程的 CurrentCulture 属性。更改线程的文化将允许所有关联的 ToStringString.Format 调用使用该文化。

更新

由于您想要显示所有精度,因此您需要做一些工作。使用NumberFormat.NumberDecimalDigits 将起作用,但如果该值的精度较低,则该数字将输出尾随零。如果您需要确保显示每个数字而没有任何额外内容,则需要事先计算精度并在将其转换为字符串之前进行设置。 *** 问题Calculate System.Decimal Precision and Scale 或许可以帮助您确定小数的精度。

【讨论】:

将小数位数截断为CultureInfo.NumberFormat.NumberDecimalDigits

以上是关于十进制字符串与千位分隔符?的主要内容,如果未能解决你的问题,请参考以下文章

使用货币符号和千位分隔符解析浮点值

如何在 5.7.0 版本的 D3.js 中制作自定义千位分隔符和小数字符

asp.net mvc 设置数字格式默认十进制千位分隔符

如何获取 toLocaleString() 方法的小数和千位分隔符?

mySQL格式数字输出,千位分隔符

在Textview中显示千位分隔符和十进制格式的数字