Десятичное число в строку с разделителями тысяч?

рассмотрим Decimal значение:

Decimal value = -1234567890.1234789012M;

Я хочу преобразовать это Decimal значение для строки и include "разделитель разрядов".

Примечание: я не хочу включать тысяч, я хочу включить цифра группировка. Разница важна для культур, которые не группируют числа в тысячи или не используют запятые для разделения группы

некоторый пример вывода с различными стандартными строками форматирования, на моем компьютере, с моей текущей локали:

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")     =   -$,,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 ответов


вы можете указать пользовательский шаблон (шаблон будет соответствующим образом разрешаться для конкретного метода группировки и соответствующих символов группировки и десятичного разделителя). Шаблон может иметь положительные, отрицательные и нулевые разделы. Положительный шаблон всегда один и тот же, но отрицательный шаблон зависит от культуры и может быть получен из 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 -), Так что этот случай не может быть протестирован. Но нет причин думать, что это не сработает.


на ToString метод на десятичных дробях по умолчанию использует CultureInfo.CurrentCulture для сеанса пользователя и, следовательно, зависит от того, на ком выполняется код.

метод ToString также принимает IFormatProvider при различных перегрузках. Здесь вам нужно предоставить свои культурные Форматтеры.

например, если вы передаете NumberFormat для fr-CH, вы можете форматировать вещи так, как ожидает эта культура:

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

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

будет вывод

 -1'234'567'890.1234789012

Edit #3-переписать с помощью пользовательских форматеров. Это должно делать то, что вы хотите, на основе нового обновленного вопроса.

Edit #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

вам нужно включить культуру при форматировании для ваших строк. Вы можете использовать строку.Формат и включить культуру в качестве первого параметра или использования объекта метод 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 собственность. Изменение культуры потока позволит всем связанным ToString и String.Format вызовы для использования этой культуры.

обновление

поскольку вы хотите отобразить всю точность, вам придется немного поработать. Используя NumberFormat.NumberDecimalDigits будет работать, за исключением того, что если значение имеет меньшую точность, номер будет выводиться с конечными нулями. Если вам нужно убедиться, что вы отображаете каждую цифру без каких-либо дополнительных функций, вам нужно будет заранее рассчитать точность и установить ее перед преобразованием в строку. Вопрос StackOverflow Система Расчета.Десятичная точность и масштаб может помочь вам определить точность десятичного знака.