Разбор пользовательских токенов данных и замена значениями в C#

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