Строка C # в десятичную на весь стиль или культура - PullRequest
0 голосов
/ 10 января 2019

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

  1. $ 1,30
  2. £ 1,50
  3. € 2,50
  4. 2,50 €
  5. 2.500,00 €

Я вижу много примеров использования культуры для преобразования . & ,. Но в моем случае у меня нет ничего, чтобы идентифицировать культуру.

Это поле отображения я получаю от клиента и мне нужно извлечь значение.

Я пытался следовать (что не работает для всех сценариев), но хотел бы знать, есть ли у нас лучший способ справиться с этим.

Decimal.Parse(value,NumberStyles.Currency |
                    NumberStyles.Number|NumberStyles.AllowThousands |
                    NumberStyles.AllowTrailingSign | NumberStyles.AllowCurrencySymbol)

Я также пытался использовать Regex для удаления знака валюты, но не смог преобразовать 1,8 или 1,8 в одной логике.

Ответы [ 3 ]

0 голосов
/ 10 января 2019

Единственный способ, которым вы могли бы это сделать, это просто удалить строку из символов и изменить. и, до десятичного разделителя. Что-то вроде:

public decimal UniversalConvertDecimal(string str)
{
    char currentDecimalSeparator = Convert.ToChar(Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator);

    str = str.Replace('.', currentDecimalSeparator);
    str = str.Replace(',', currentDecimalSeparator);

    StringBuilder builder = new StringBuilder(str.Length);
    foreach(var ch in str)
    {
       if(Char.IsDigit(ch) || ch == currentDecimalSeparator)
         builder.Add(ch);             
    }

    string s = builder.ToString();
    return Convert.ToDecimal(s);

}

Сначала вы должны получить текущий десятичный разделитель из вашей системы. Тогда вы должны заменить. и, с текущим десятичным разделителем. Далее вам нужно будет убрать строку из любого другого символа, кроме цифр или десятичного разделителя. В конце вы можете быть уверены, что Convert.ToDecimal будет работать. Но я не знаю, хотите ли вы этого достичь.

Если вам нужен какой-то механизм для сохранения валюты в базе данных, есть гораздо более простое решение. Просто конвертируйте эту валюту в наименее валютную часть. Например, вместо 1 доллара, сэкономьте 100 центов.

Так что, если у вас есть $ 1,99, просто умножьте его на 100, и вы получите: 199 центов. И это целое число может быть сохранено в БД.

0 голосов
/ 10 января 2019

Хорошо, если вы всегда получаете правильный формат валюты, и меняется только культура, вы можете угадать , какой символ используется в качестве десятичной точки, а какой - в качестве разделителя тысяч, проверяя, какой появляется последний в номере. Затем удалите все тысячи разделителей и проанализируйте их, как если бы их культура была неизменной.

Код будет выглядеть следующим образом:

// Replace with your input
var numberString = "2.500,00  €";

// Regex to extract the number part from the string (supports thousands and decimal separators)
// Simple replace of all non numeric and non ',' '.' characters with nothing might suffice as well
// Depends on the input you receive
var regex = new Regex"^[^\\d-]*(-?(?:\\d|(?<=\\d)\\.(?=\\d{3}))+(?:,\\d+)?|-?(?:\\d|(?<=\\d),(?=\\d{3}))+(?:\\.\\d+)?)[^\\d]*$");

char decimalChar;
char thousandsChar;

// Get the numeric part from the string
var numberPart = regex.Match(numberString).Groups[1].Value;

// Try to guess which character is used for decimals and which is used for thousands
if (numberPart.LastIndexOf(',') > numberPart.LastIndexOf('.'))
{
    decimalChar = ',';
    thousandsChar = '.';
}
else
{
    decimalChar = '.';
    thousandsChar = ',';
}

// Remove thousands separators as they are not needed for parsing
numberPart = numberPart.Replace(thousandsChar.ToString(), string.Empty);

// Replace decimal separator with the one from InvariantCulture
// This makes sure the decimal parses successfully using InvariantCulture
numberPart = numberPart.Replace(decimalChar.ToString(),
    CultureInfo.InvariantCulture.NumberFormat.CurrencyDecimalSeparator);

// Voilá
var result = decimal.Parse(numberPart, NumberStyles.AllowDecimalPoint | NumberStyles.Number, CultureInfo.InvariantCulture);

Это выглядит немного сложным для простого десятичного разбора, но я думаю, что нужно выполнить работу для всех вводимых чисел или хотя бы для большинства из них.

Если вы делаете это в каком-то цикле, вы можете использовать скомпилированное регулярное выражение.

0 голосов
/ 10 января 2019

Проблема здесь в том, что в одном случае . означает десятичную точку, а в другом - разделитель тысяч. И тогда у вас есть , в качестве десятичного разделителя. Очевидно, что синтаксический анализатор не может «угадать», что имеется в виду, поэтому единственное, что вы можете сделать, - это принять решение о некоторых правилах обработки того или иного случая.

Если у вас есть контроль над пользовательским интерфейсом, лучшим подходом будет проверка пользовательского ввода и просто отклонение любого значения, которое не может быть проанализировано с объяснением того, какой формат ожидается.

Если у вас нет контроля над пользовательским интерфейсом, вторым лучшим вариантом будет проверить некоторые «правила», а затем придумать, какая культура подходит для данного заданного ввода, и попытаться выполнить его через decimal.TryParse для этой заданной культуры.

Для данного ввода у вас могут быть следующие правила:

  • input.StartsWith("$") -> en-US
  • input.StartsWith("£") -> ru-RU
  • input.StartsWith("€") || input.EndsWith("€") -> de-DE

Они могут разумно обрабатывать все случаи.

В коде:

static void Main(string[] args)
{
    string[] inputs =
    {
        "$1.30",
        "£1.50",
        "€2,50",
        "2,50 €",
        "2.500,00 €"
    };
    for (int i = 0; i < inputs.Length; i++)
    {
        Console.Write((i + 1).ToString() + ". ");
        if (decimal.TryParse(inputs[i], NumberStyles.Currency,
                             GetAppropriateCulture(inputs[i]), out var parsed))
        {
            Console.WriteLine(parsed);
        }
        else
        {
            Console.WriteLine("Can't parse");
        }
    }
}

private static CultureInfo GetAppropriateCulture(string input)
{
    if (input.StartsWith("$")) 
        return CultureInfo.CreateSpecificCulture("en-US");
    if (input.StartsWith("£")) 
        return CultureInfo.CreateSpecificCulture("en-GB");
    if (input.StartsWith("€") || input.EndsWith("€")) 
        return CultureInfo.CreateSpecificCulture("de-DE");
    return CultureInfo.InvariantCulture;
}

Выход:

  1. 1,30
  2. 1,50
  3. 2,50
  4. 2,50
  5. 2500,00
...