С# String заменить словарем и условием Regex

Я хочу заменить значения строки словарем, который у меня есть, но только если они соответствуют условию регулярного выражения.

Вот что у меня есть:

string input = @"A.4 AND ([10] A.4 OR A.4) OR [10]A.4 A.5 [10]A.5";
Dictionary <string, string> dict = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
                                                    {"A.4", "Test"},
                                                    {"A.5", "Test2"},
                                                    };
var output = dict.Aggregate(input, (current, value) => current.Replace(value.Key, value.Value));

//current output = "Test AND ([10] Test OR Test) OR [10]Test Test2 [10]Test2"
//wished output = "Test AND ([10] A.4 OR Test) OR [10]A.4 Test2 [10]A.5"

Я не хочу заменять текст, если перед текстом стоит "[10]" или "[10] ". Я думаю, что мне следует использовать регулярное выражение или что-то подобное, но я не знаю, как это сделать.

Вы уже упомянули регулярное выражение. Вы пытались использовать Regex.Replace вместо этого? Или, по крайней мере, напишите регулярное выражение, которое соответствует нужным вам частям?

Panagiotis Kanavos 05.10.2022 11:34

Как выглядят теги? Если у них есть шаблон, вы можете использовать его в одном регулярном выражении, например, (?<!\[10\]\s?)A\.\d+ будет соответствовать всем значениям A.n без префикса.

Panagiotis Kanavos 05.10.2022 11:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
2
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

var output = dict.Aggregate(input, (current, sub) => Regex.Replace(current, $@"(?<!\[10\]\s?){Regex.Escape(sub.Key)}", sub.Value));

Отрицательное утверждение просмотра назад (?<!\[10\]\s?) гарантирует, что совпадающий термин никогда не следует за [10] или [10] .


Как отмечает Панайотис Канавос, вы можете полностью пропустить вызов Aggregate, передав делегату, который выполняет поиск в словаре, Regex.Replace:

var replacePattern = $@"(?<!\[10\]\s?)(?:{string.Join('|', dict.Keys.Select(Regex.Escape))})";
var output = Regex.Replace(input, replacePattern, (m) =>  dict[m.Value]);

Нет необходимости в Aggregate, по крайней мере, для замены. Regex.Replace имеет перегрузку с делегатом, который возвращает значение замены.

Panagiotis Kanavos 05.10.2022 11:36

@PanagiotisKanavos Хороший вопрос, добавлено к ответу

Mathias R. Jessen 05.10.2022 11:44

@Mathias R. Jessen Большое спасибо, это того стоит. Однако я буду использовать решение с Aggregate, потому что у меня есть такие ключи, как (A.4 и A.41), а во втором решении A.41 не заменяется правильным значением, оно заменяется значением A.4, а затем это 1 в конце. Итак, я использую первый с dict.Reverse().Aggregate(... будучи dict SortedDictionary, поэтому сначала будут проверены самые длинные ключи.

Evil Morty 05.10.2022 11:58

@EvilMorty добавьте \b после последнего ) в replacePattern, чтобы это исправить :)

Mathias R. Jessen 05.10.2022 11:58

Regex.Replace имеет перегрузку с делегатом, который создает замещающие значения. Если теги имеют шаблон, можно использовать одно регулярное выражение, чтобы сопоставить их и заменить их значениями словаря.

Это регулярное выражение соответствует тегам в форме A.n, где n число:

var regex=new Regex(@"(?<!\[10\]\s?)A\.\d+");

var output=regex.Replace(input,match=>dict[match.Value]);

Console.WriteLine(output);

Это производит

Test AND ([10] A.4 OR Test) OR [10]A.4 Test2 [10]A.5

(?<!...) — это отрицательный ретроспективный паттерн. Он соответствует строкам, которые не начинаются с инвертированного шаблона. (?<!\[10\]\s?) соответствует строкам, которые не начинаются с [10] и необязательного пробела.

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