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

时间:2012-07-10 14:21:44

标签: c# .net localization globalization

考虑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()如何设法使用常规格式来显示需要显示的所有数字:

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

所以这是一个死胡同。

3 个答案:

答案 0 :(得分:7)

默认情况下,小数点上的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

正如约书亚所指出的,这只适用于某些地区。

从它的外观来看,你需要选择两个邪恶中较小的一个:了解你的数字的精确度,或指定每种文化的格式。我打赌知道你的数字的精确度可能会更容易。 在这种情况下,我的答案的先前版本可能是有用的:

要显式控制要输出的小数位数,可以克隆区域性提供的数字格式并修改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

答案 1 :(得分:4)

您可以指定自定义模式(模式将适当地解析为特定于文化的分组方法以及相应的分组和小数分隔符字符)。模式可以有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 -)的区域设置,因此无法测试该案例。但是没有理由认为它会失败。

答案 2 :(得分:2)

在格式化字符串时,您需要包含文化。您可以使用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将起作用,但如果值的精度较低,则数字将以尾随零输出。如果您需要确保显示每个数字而没有任何额外的数字,您需要事先计算精度并在将其转换为字符串之前进行设置。 StackOverflow问题Calculate System.Decimal Precision and Scale可以帮助您确定小数的精度。