Вы наверняка использовали метод int.TryParse
с такой сигнатурой:
public static bool TryParse (string? s, out int result);
Этот метод C# принимает строку s
, которая, если ее можно разобрать, будет преобразована в значение int
, целочисленное значение которого будет сохранено в параметре result
; в то же время метод возвращает true
, чтобы сообщить, что разбор прошел успешно.
В качестве примера можно привести этот фрагмент:
if (int.TryParse("100", out int result))
{
Console.WriteLine(result + 2); // correctly parsed as an integer
}
else
{
Console.WriteLine("Failed");
}
выводит 102.
Работает ли это? Да. Это лучшее, что мы можем сделать? Нет!
Как разобрать сложную строку с помощью int.TryParse
Что если вы хотите разобрать 100 евро? Есть менее известная перегрузка, которая справится с этой задачей:
public static bool TryParse (
string? s,
System.Globalization.NumberStyles style,
IFormatProvider? provider,
out int result);
Как вы видите, у нас есть еще два параметра: style
и provider
.
IFormatProvider? provider
позволяет указать информацию о культуре: примерами являются CultureInfo.InvariantCulture
и new CultureInfo("es-es")
.
Но настоящим королем этой перегрузки является параметр style
: это Flagged Enum, который позволяет указать ожидаемый формат строки.
style
имеет тип System.Globalization.NumberStyles
, который имеет несколько значений:
[Flags]
public enum NumberStyles
{
None = 0x0,
AllowLeadingWhite = 0x1,
AllowTrailingWhite = 0x2,
AllowLeadingSign = 0x4,
AllowTrailingSign = 0x8,
AllowParentheses = 0x10,
AllowDecimalPoint = 0x20,
AllowThousands = 0x40,
AllowExponent = 0x80,
AllowCurrencySymbol = 0x100,
AllowHexSpecifier = 0x200,
Integer = 0x7,
HexNumber = 0x203,
Number = 0x6F,
Float = 0xA7,
Currency = 0x17F,
Any = 0x1FF
}
Вы можете комбинировать эти значения с помощью символа |
.
Давайте посмотрим несколько примеров.
Разбор как целое число
Самый простой пример – разобрать простое целое число:
[Fact]
void CanParseInteger()
{
NumberStyles style = NumberStyles.Integer;
var canParse = int.TryParse("100", style, new CultureInfo("it-it"), out int result);
Assert.True(canParse);
Assert.Equal(100, result);
}
Обратите внимание на стиль NumberStyles = NumberStyles.Integer;
, используемый в качестве базового.
Разбор скобок как отрицательных чисел
В некоторых случаях круглые скобки вокруг числа указывают на то, что число отрицательное. Например, (100) – это другой способ написать -100.
В этом случае можно использовать флаг NumberStyles.AllowParentheses
.
[Fact]
void ParseParenthesisAsNegativeNumber()
{
NumberStyles style = NumberStyles.Integer | NumberStyles.AllowParentheses;
var canParse = int.TryParse("(100)", style, new CultureInfo("it-it"), out int result);
Assert.True(canParse);
Assert.Equal(-100, result);
}
Разбор с валютой
А если строка представляет собой валюту? Вы можете использовать NumberStyles.AllowCurrencySymbol
.
[Fact]
void ParseNumberAsCurrency()
{
NumberStyles style = NumberStyles.Integer | NumberStyles.AllowCurrencySymbol;
var canParse = int.TryParse(
"100€",
style,
new CultureInfo("it-it"),
out int result);
Assert.True(canParse);
Assert.Equal(100, result);
}
Но помните: единственный допустимый символ – это символ, связанный с экземпляром CultureInfo
, который вы передаете в метод.
Оба
var canParse = int.TryParse(
"100€",
style,
new CultureInfo("en-gb"),
out int result);
и
var canParse = int.TryParse(
"100$",
style,
new CultureInfo("it-it"),
out int result);
не являются корректными. Первое потому, что мы используем английскую культуру для разбора евро, второе потому, что мы используем итальянскую культуру для разбора долларов.
Подсказка: как получить символ валюты, учитывая CultureInfo? Вы можете использовать NumberFormat.CurrecySymbol
, вот так:
new CultureInfo("it-it").NumberFormat.CurrencySymbol; // €
Разбор с разделителем тысяч
А что делать, если строка содержит разделитель тысяч? 10.000 – это правильное число в итальянской нотации.
Вы можете установить флаг NumberStyles.AllowThousands
.
[Fact]
void ParseThousands()
{
NumberStyles style = NumberStyles.Integer | NumberStyles.AllowThousands;
var canParse = int.TryParse("10.000", style, new CultureInfo("it-it"), out int result);
Assert.True(canParse);
Assert.Equal(10000, result);
}
Разбор шестнадцатеричных значений
Это редкий случай, но он может произойти: вы получаете строку в шестнадцатеричной нотации, но вам нужно разобрать ее как целое число.
В этом случае флаг NumberStyles.AllowHexSpecifier
является правильным.
[Fact]
void ParseHexValue()
{
NumberStyles style = NumberStyles.AllowHexSpecifier;
var canParse = int.TryParse("F", style, new CultureInfo("it-it"), out int result);
Assert.True(canParse);
Assert.Equal(15, result);
}
Обратите внимание, что строка ввода не содержит шестнадцатеричного префикса.
Использование нескольких флагов
Вы можете комбинировать несколько Flagged Enums для создания нового значения, которое представляет собой объединение указанных значений.
Мы можем использовать эту возможность, например, для разбора валюты, содержащей разделитель тысяч:
[Fact]
void ParseThousandsCurrency()
{
NumberStyles style =
NumberStyles.Integer
| NumberStyles.AllowThousands
| NumberStyles.AllowCurrencySymbol;
var canParse = int.TryParse("10.000€", style, new CultureInfo("it-it"), out int result);
Assert.True(canParse);
Assert.Equal(10000, result);
}
NumberStyles.AllowThousands | NumberStyles.AllowCurrencySymbol
делает трюк.
Заключение
Мы все используем простой метод int.TryParse
, но когда разбор входной строки требует более сложных вычислений, мы можем положиться на эти перегрузки. Конечно, если этого все еще недостаточно, вы должны создать свои собственные парсеры (или, как более простой подход, вы можете использовать регулярные выражения).
Есть ли методы с перегрузками, которые никто не использует? Поделитесь ими в комментариях!
Счастливого кодинга!
🐧