C # Поиск конкретных значений из строк формата сообщения - PullRequest
0 голосов
/ 29 мая 2019

Учитывая строку формата сообщения, такую ​​как str ниже. Я хочу иметь возможность получать значения «уведомления» и «имя», которые используются для отображения текстовых значений.

var str = @"You have {notifications, plural,
          zero {no notifications}
           one {one notification}
           =42 {a universal amount of notifications}
         other {# notifications}
        }. Have a nice day, {name}!";

Я пытался использовать регулярное выражение , например:

var matches = Regex.Matches(str, @"{(.*?)}");
//var matches = Regex.Matches(str, @"(?<=\{)[^}{]*(?=\})");
var results = matches.Cast<Match>().Select(m => m.Groups[1].Value).Distinct().ToList();

Но вышеизложенное не учитывает, что {notifications,.. сама заключена в фигурные скобки и включает в себя ненужные внутренние значения, которые также заключены в фигурные скобки.

Короче говоря, я просто хочу иметь возможность проанализировать строку, такую ​​как str выше, и получить notifications & name по возвращаемым значениям.

Строка типа var str2 = @"Hello {name}" должна просто возвращать name в качестве значения.

EDIT

Значения notifications & name не будут известны заранее - я просто использовал это в качестве примера для значений, которые мне требуется вернуть из строки.

Ответы [ 2 ]

1 голос
/ 29 мая 2019

TL; DR: Вот дополнительное решение

var str = @"You have {notifications, plural,
          zero {no notifications}
           one {one notification}
           =42 {a universal amount of notifications}
         other {# notifications}
        }. Have a nice day, {name}!";

// get matches skipping nested curly braces
var matches = 
    Regex.Matches(str, @"{((?:[^{}]|(?<counter>{)|(?<-counter>}))+(?(counter)(?!)))}");

var results = matches.Cast<Match>().Select(m => m.Groups[1].Value).Distinct()
    .Select(v => Regex.Match(v, @"^\w+").Value) // take 1st word
    .ToList();

, которое приводит к (копируется из окна Visual Studio Locals при отладке)

results Count = 2   System.Collections.Generic.List<string>
    [0] "notifications"
    [1] "name"

... следует оригинальный ответ ...


Одна вещь, о которой следует упомянуть о текущем решении исходного вопроса:

  • использование . не соответствует строкеразрывы, так что это одна из причин, почему он в настоящее время соответствует вложенным значениям (см. источник )

Если я понимаю вашу цель, эта статья является хорошим объяснением и демонстрацией связанныхпроблема и решение:

(в этой статье рассматривается основная проблема, отмеченная в исходном вопросе - вложенные фигурные скобки )

https://blogs.msdn.microsoft.com/timart/2013/05/14/nestedrecursive-regex-and-net-balancing-groups-detect-a-function-with-a-regex/

Из этой статьи я бы предложил следующий шаблон в качестве необязательного решения:

var str = @"You have {notifications, plural,
          zero {no notifications}
           one {one notification}
           =42 {a universal amount of notifications}
         other {# notifications}
        }. Have a nice day, {name}!";

// get matches skipping nested curly braces
var matches = 
    Regex.Matches(str, @"{((?:[^{}]|(?<counter>{)|(?<-counter>}))+(?(counter)(?!)))}");
var results = matches.Cast<Match>().Select(m => m.Groups[1].Value).Distinct().ToList();

, что приводит к (копируется из окна Visual Studio Locals при отладке)

results Count = 2   System.Collections.Generic.List<string>
    [0] "notifications, plural,\r\n          zero {no notifications}\r\n           one {one notification}\r\n           =42 {a universal amount of notifications}\r\n         other {# notifications}\r\n        "
    [1] "name"

(или если вы должны были распечатать эти результаты на консоли):

// Result 0 would look like:
notifications, plural,
          zero {no notifications}
           one {one notification}
           =42 {a universal amount of notifications}
         other {# notifications}


// Result 1 would look like:
name

Обновление

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

Затем возьмите первое слово из каждого результата

(я повторяю приведенный выше фрагмент с дополнительным оператором select, чтобы показать полное решение)

var str = @"You have {notifications, plural,
          zero {no notifications}
           one {one notification}
           =42 {a universal amount of notifications}
         other {# notifications}
        }. Have a nice day, {name}!";

// get matches skipping nested curly braces
var matches = 
    Regex.Matches(str, @"{((?:[^{}]|(?<counter>{)|(?<-counter>}))+(?(counter)(?!)))}");

var results = matches.Cast<Match>().Select(m => m.Groups[1].Value).Distinct()
    .Select(v => Regex.Match(v, @"^\w+").Value) // take 1st word
    .ToList();

, что приводит к (копируется из окна Visual Studio Locals при отладке)

results Count = 2   System.Collections.Generic.List<string>
    [0] "notifications"
    [1] "name"

Немного больше информации

(Я только нашел это интересным и потратил немного больше времени на исследование / изучение и подумал, что стоит включить в него некоторую дополнительную информацию)

Беседы здесь и здесь включают в себя некоторые мнения за и против использования регулярных выражений для этого типа проблемы.

  • Я думаю, что интересно прочитать этимнения и получить более всестороннюю точку зрения

Независимо от вышеприведенных мнений, создатели .NET сочли целесообразным реализовать определения балансирующей группы - aфункциональность, которую использует этот ответ:

0 голосов
/ 29 мая 2019

Один из способов сделать это - написать метод, который отформатирует для вас строку на основе ввода count и формы строки в единственном и множественном числе:

private static string FormatWord(int count, string singluar)
{
    return Format(count, singluar, singluar + "s");
}

private static string FormatWord(int count, string singular, string plural)
{
    return count == 0 ? "no " + plural
        : count == 1 ? "one " + singular
        : count == 42 ? "a universal number of " + plural
        : count + " " + plural;
}

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

private static void Main()
{
    var name = "User";

    while (true)
    {
        var count = GetIntFromUser("Enter notification count: ");
        Console.WriteLine($"You have {FormatWord(count, "notification")}. " + 
            $"Have a nice day, {name}");
    }
}

Обратите внимание, что этот метод также использует вспомогательный метод для получения строго типизированного целого числа от пользователя:

private static int GetIntFromUser(string prompt, Func<int, bool> validator = null)
{
    int result;
    var cursorTop = Console.CursorTop;

    do
    {
        ClearSpecificLineAndWrite(cursorTop, prompt);
    } while (!int.TryParse(Console.ReadLine(), out result) ||
             !(validator?.Invoke(result) ?? true));

    return result;
}

private static void ClearSpecificLineAndWrite(int cursorTop, string message)
{
    Console.SetCursorPosition(0, cursorTop);
    Console.Write(new string(' ', Console.WindowWidth));
    Console.SetCursorPosition(0, cursorTop);
    Console.Write(message);
}
...