C# Поиск определенных значений из строк формата сообщения

Дана строка формата сообщения, такая как 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 в качестве значения.

РЕДАКТИРОВАТЬ

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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
143
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Один из способов сделать это — написать метод, который будет форматировать строку для вас на основе ввода 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);
}
Ответ принят как подходящий

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

Обновлять

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

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

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

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 сочли целесообразным реализовать определения групп балансировки — функциональность, которую использует этот ответ:

Другие вопросы по теме