#c# #regex #lexer #parser-generator
#c# #регулярное выражение #lexer #анализатор-генератор
Вопрос:
У меня есть около 10 фрагментов данных из записи, и я хочу иметь возможность определять расположение строки, в которой возвращаются эти данные, с возможностью исключения некоторых фрагментов. Моя мысль заключалась в том, чтобы использовать enum для присвоения целочисленных значений моим токенам / полям, а затем иметь формат, подобный {0}{1}{2}{3}
или что-то столь же сложное, как {4} - {3}{1} [{8}]
. Значение токенов относится к полям в моей базе данных. Например, у меня есть это перечисление для моих токенов, относящихся к произведенным платежам.
AccountMask = 0,
AccountLast4 = 1,
AccountFirstDigit = 2,
AccountFirstLetter = 3,
ItemNumber = 4,
Amount = 5
Маска учетной записи — это строка типа VXXXXX1234, где V обозначает visa, а 1234 — это последние 4 цифры карты. Иногда клиентам требуется V, иногда им нужна первая цифра (тип карты легко перевести в первую цифру).
Моя цель — создать что-то повторно используемое для генерации строки с использованием токенов в строке формата, которая затем будет использовать данные, связанные с цифрой внутри токена, для выполнения замены данных на месте.
Итак, для примера, используя маску выше и мое перечисление, если бы я хотел определить формат 9{2}{1}{4:[0:0000000000]}
если номер элемента равен 678934
который затем будет преобразован в 9412340000678934, где внутренняя часть токена 4 становится определением для String.Format
этого значения. Кроме того, данные, размещенные вокруг токенов, игнорируются и сохраняются на месте.
Моя проблема связана с манипулированием строкой и наилучшей практикой. Мне сказали, что регулярные выражения могут быть дорогостоящими, если вы собираетесь создавать их на лету. Как специалист по CS, я чувствую, что «правильным» (каким бы сложным) решением ни было создание лексера / анализатора для моих токенов. У меня нет опыта написания лексера / синтаксического анализа на C #, поэтому я не уверен в лучших практиках вокруг этого. Здесь я ищу рекомендации по эффективной и простой в настройке системе.
Комментарии:
1. Правильно ли я понимаю: вам нужно упаковать некоторые данные в строку, куда-то преобразовать ее, а затем распаковать?
2. Я хочу, чтобы каждый пользователь мог определять одну строку данных как строку единого формата. Они должны иметь возможность выбирать доступные фрагменты информации из полей в базе данных и могут определять формат с пробелами, разделительными символами и т.д. Кроме того, каждому токену для замены данных должен быть разрешен собственный формат для определения способа замены данных (например, дополнение номера элемента 0 в приведенном выше примере)
Ответ №1:
Я вижу эту проблему, и я сразу подумал, что решение довольно простое; сохраните маски, которые вы хотите использовать, с постоянными значениями для различных полей данных, которые вы хотите включить, и передайте маски в постоянный вызов String .Формат ():
const string CreditCardWithFirstLetterMask = "{3}XXXXXXXXXXX{1}";
const string CreditCardWithFirstDigitMask = "{2}XXXXXXXXXXX{1}";
...
var formattedString = String.Format(CreditCardWithFirstDigitMask,
record.AccountMask,
record.AccountLast4,
record.AccountFirstDigit,
record.AccountFirstLetter,
record.ItemNumber,
record.Amount);
Комментарии:
1. Я не могу просто хранить их таким образом. Форматы будут определяться пользователем.
Ответ №2:
В итоге я поместил регулярное выражение в качестве статического объекта в классе, а затем перебирал соответствия, чтобы выполнить замены и создать свой токен.
var token = request.TokenFormat;
var matches = tokenExpression.Matches(request.TokenFormat);
foreach (Match match in matches)
{
var value = match.Value;
var tokenCode = (Token)Convert.ToInt32(value.Substring(1, (value.Contains(":") ? value.IndexOf(":") : value.IndexOf("}")) - 1));
object data = null;
switch (tokenCode)
{
case Token.AccountMask:
data = accountMask;
break;
case Token.AccountLast4:
data = accountMask.Substring(accountMask.Length - 4);
break;
case Token.AccountFirstDigit:
string firstLetter = accountMask.Substring(0, 1);
switch (firstLetter.ToUpper())
{
case "A":
data = 3;
break;
case "V":
data = 4;
break;
case "M":
data = 5;
break;
case "D":
data = 6;
break;
}
break;
case Token.AccountFirstLetter:
data = accountMask.Substring(0, 1);
break;
case Token.ItemNumber:
if(item != null)
data = item.PaymentId;
break;
case Token.Amount:
if (item != null)
data = item.Amount;
break;
case Token.PaymentMethodId:
if (paymentMethod != null)
data = paymentMethod.PaymentMethodId;
break;
}
if (formatExpression.IsMatch(value))
{
Match formatMatch = formatExpression.Match(value);
string format = formatMatch.Value.Replace("[", "{").Replace("]", "}");
token = token.Replace(value, String.Format(format, data));
}
else
{
token = token.Replace(value, String.Format("{0}", data));
}
}
return token;