Регулярное выражение.IsMatch не работает после backspace в текстовом поле.Текст

#c# #regex #winforms

Вопрос:

У меня есть текстовое поле с обработчиком LostFocus событий, который вызывает метод, форматирующий строку чисел в текстовом поле в числовой формат. Например, 123456,78 возвращает 123 456,78 .
Если я вместо этого начну с 123 45 правильного возврата 12 345,00 .

Однако, если я сначала наберу 123456,78 , и он правильно вернется 123 456,78 , а затем удалю последние четыре символа в текстовом поле с помощью клавиши backspace, т. Е. Удалю 6,78 , нажав четыре раза на кнопку backspace, это не сработает. Он просто остается 123 45 в текстовом поле.
Однако, если я выделю весь текст в текстовом поле и вставлю 123 45 его, он вернется 12 345,00 правильно.

Когда я отлаживаю, шагая по одной строке за раз , я вижу, что аргумент метода amountIn правильно сохраняет строку 123 45 , как при использовании клавиш backspace, так и при выборе и вставке. Однако Regex.IsMatch() возвращается false , когда я использую backspace и true когда я выбираю и вставляю. Следовательно, я считаю, что обратное пространство оставляет в строке какой-то артефакт, который не виден во время отладки, но распознается IsMatch() методом. Вот мой метод:

 private void txtAmount_LostFocus(object sender, EventArgs e)
{
    txtAmount.Text = ReformatAmount(txtAmount.Text);
}

public static string ReformatAmount(string amountIn)
{
    string amountOut;
    if (Regex.IsMatch(amountIn, @"^[0-9 ,.] $"))
    {
        amountOut = Regex.Replace(amountIn, "[. ]", "");

        amountOut = Convert.ToDecimal(amountOut).ToString("N");

        return amountOut;
    }
    else
    {
        amountOut = amountIn;
        return amountOut;
    }
}
 

Комментарии:

1. Вы рассматривали возможность использования чего-то подобного decimal.TryParse вместо этого?

2. Флайдог, я попробовал твой метод, и он тоже сработал. Отличное решение! Спасибо

Ответ №1:

Разделитель CultureInfo.NumberFormat.numbergroup в Швеции-это неразрывный пробел, символ 0xA0 (160), а не символ 0x20 (32), пробел, обычно используемый для разделения, например, слов.

Вы можете видеть это лучше, написав:

 var cultureSE = CultureInfo.GetCultureInfo("se-SE");
string hexChar = ((int)cultureSE.NumberFormat.NumberGroupSeparator[0]).ToString("X2");
 

hexChar будет "A0" .

Используемое регулярное ^[0-9 ,.] $ выражение не учитывает это, оно учитывает только символ 0x20 .
Вы можете изменить его на просто [0-9 ,.] игнорировать, но, вероятно, вы захотите использовать s вместо этого, он также будет соответствовать всем символам пробела в Юникоде, включая неразрывные символы пробела, как символ 0xA0 .
См. Также: Классы символов в регулярных выражениях

Затем выражение может быть изменено в:

 if (Regex.IsMatch(amountIn, @"^[0-9s,.] $"))
{
     // Convert to a culture-specific number representation
}
 

Комментарии:

1. Потрясающе! Спасибо!

Ответ №2:

Нет необходимости в регулярных выражениях:

 string[] nums = new[]
{
  "123456,78",
  "123 456,78",
  "6,78",
  "123 45"
};
foreach (var num in nums)
{
  if (Decimal.TryParse(num, NumberStyles.Any, null, out decimal result))
  {
    // Number successfully parsed:
    Console.WriteLine($"{result:N2}");
  }
  else
  {
    // Parsing error here
    Console.WriteLine($"Could not parse: '{num}'");
  }
}
 

Выход:

 123 456,78
123 456,78
6,78
12 345,00
 

Комментарии:

1. Поскольку вы анализируете пользовательский ввод (который часто включает опечатки с жирным пальцем ), рассмотрите decimal.TryParse вместо .Parse

2. Это очевидно, но сделает это более понятным ))