Regex заменить, но только между двумя шаблонами - PullRequest
4 голосов
/ 04 марта 2009

Хорошо, у меня есть многострочная строка, которую я пытаюсь очистить.

Каждая строка может быть или не быть частью большого блока цитируемого текста. Пример:

This line is not quoted.
This part of the line is not quoted “but this is.”
This one is not quoted either.
“This entire line is quoted”
Not quoted.
“This line is quoted
and so is this one
and so is this one.”
This is not quoted “but this is
and so is this.”

Мне нужна замена RegEx, которая развернет жестко заключенные в кавычки строки, т.е. заменит "\ r \ n" пробелом, но только между фигурными кавычками.

Вот как это должно выглядеть после замены:

This line is not quoted.
This part of the line is not quoted “but this is.”
This one is not quoted either.
“This entire line is quoted”
Not quoted.
“This line is quoted and so is this one and so is this one.”
This is not quoted “but this is and so is this.”

(Обратите внимание, что последние две строки были несколькими строками во входном тексте.)

Ограничения

  • В идеале нужен один вызов на замену Regex
  • Использование библиотеки .NET RegEx
  • Кавычки всегда начальные / конечные фигурные кавычки, а не обычные двойные тики ("), что должно немного облегчить эту задачу.

Важное ограничение

Это не прямой код .NET, я заполняю таблицу строк "searchfor / replacewith", которые затем вызываются через RegEx.Replace. У меня нет возможности добавлять пользовательский код, такой как Match Evaluators, проходить по захваченным группам и т. Д.

Текущий ответ на данный момент, что-то вроде:

r.Replace("(?<=“)\r\n(?=”)", " ")

Очевидно, я даже еще не близко.

Та же логика может быть применена, скажем, к цветовому кодированию блочных комментариев в программном коде - все, что находится внутри блочного комментария, не обрабатывается так же, как вещи вне комментариев. (Код немного сложнее, так как разделители начальных / конечных блоков комментариев также могут на законных основаниях существовать в литеральной строке, и здесь мне не нужно разбираться.)

Ответы [ 5 ]

4 голосов
/ 04 марта 2009

Предполагая, что все фигурные кавычки правильно сбалансированы, это регулярное выражение должно делать то, что вы хотите:

@"[\r\n]+(?=[^“”]*”)"

[\r\n]+ будет соответствовать одному или нескольким разделителям строк любого типа - Unix (\ n), DOS (\ r \ n) или более старый Mac (\ r). Затем предвкушение утверждает, что впереди есть закрывающая цитата и что между здесь и там нет открытой цитаты. Тогда ваш текст замены может быть простым пробелом.

1 голос
/ 04 марта 2009

Примечание: для тестирования регулярных выражений я использую http://gskinner.com/RegExr/, что очень полезно.

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

Во-первых, вам нужен однострочный режим, чтобы ваше выражение совпадало со всей входной строкой, а не построчно. Поместите это в начало вашего выражения, чтобы включить его:

(?s)

Затем вы хотите, чтобы выражение для поиска соответствовало начальной кавычке:

(?<=“)

И предварительный просмотр, чтобы соответствовать конечной цитате:

(?=”)

Теперь выражение соответствует некоторому тексту, затем новой строке, затем тексту:

([^”\r]*)\r?([^”\r]*)

Обратите внимание, что есть две группы захвата для фрагментов текста вокруг новой строки, поэтому вы можете включить этот текст в выражение замены. Это будет соответствовать тексту, который имеет только одну новую строку в кавычках. Чтобы расширить это до двух новых строк, просто добавьте еще один необязательный символ новой строки и необязательный следующий текст:

(?s)(?<=“)([^”\r]*)\r?([^”\r]*)\r?([^”\r]*)(?=”)

Вы можете расширить это, чтобы соответствовать столько строк, сколько вы думаете, может произойти. Не идеально, но, возможно, достаточно. Или, если вы можете повторно запустить выражение в вашем тексте, просто замените по одному за раз.

Оставив выражение вашего лица примерно так:

r.Replace("(?s)(?<=“)([^”\r]*)\r?([^”\r]*)", "$1 $2")

(Это не совсем правильно, поскольку после текста будет добавляться пробел, даже если вторая группа не совпадает ... но это только начало)

0 голосов
/ 04 марта 2009

Я думаю, что самым простым способом было бы сопоставить процитированные разделы с “(?s:.*?)” и использовать MatchEvaluator для удаления любых новых строк. Код MatchEvaluator может быть таким простым, как

Replace(@"\s+", " ");

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

0 голосов
/ 04 марта 2009

Вы не можете делать то, что вы хотите в пределах, которые вы описали.

Доказательство:

  • Ваша фиксированная таблица замен выполнит фиксированное количество вызовов для замены (называйте это n)
  • Каждая замена сможет устранить только фиксированное количество разрывов строк (позвоните по этому номеру m).

Следовательно

  • Блок в кавычках с m * n + 1 переводом строки не будет обрабатываться должным образом.

Вам необходимо либо увеличить мощность вашей установки (например, путем более сложной замены, рекурсивных замен, неопределенного флага повторения или ...?), Либо принять тот факт, что эта задача не может быть выполнена вашим движком .

- MarkusQ

0 голосов
/ 04 марта 2009

Таким образом, нужно найти строку, начинающуюся с открывающей кавычки, за которой следует строка, которая не содержит закрывающую кавычку или любые символы \ r \ n, за которыми следует серия из одного или нескольких символов \ r \ n, захватите все, кроме символа терминала \ r \ n, и замените все совпадение захваченной частью.

- MarkusQ

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...