Как завершить удаление всей строки с помощью многострочных регулярных выражений? - PullRequest
0 голосов
/ 05 марта 2019

Я хочу удалить все строки, которые включают b в этой многострочной строке:

aba\n
aaa\n
aba\n
aaa\n
aba[\n\n - optional]

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

Это ожидаемый результат:

aaa\n
aaa[\n\n - as in the input file]

Это то, что я пробовал:

import re
String = "aba\naaa\naba\naaa\naba"
print(String)
print(re.sub(".*b.*", "", String))  # this one leaves three empty lines
print(re.sub(".*b.*\n", "", String))  # this one misses the last line
print(re.sub("\n.*b.*", "", String))  # this one misses the first line
print(re.sub(".*b.*\n?", "", String))  # this one leaves an empty last line
print(re.sub("\n?.*b.*", "", String))  # this one leaves an empty first line
print(re.sub("\n?.*b.*\n?", "", String))  # this one joins the two remaining lines

Я также опробовал flags=re.M и различные прогнозные и задние планы, но основной вопрос, по-видимому, заключается в следующем: как удалить первое или последнее вхождение \n в соответствующей строке,в зависимости от того, что существует - но не оба, если оба существуют?

Ответы [ 2 ]

0 голосов
/ 05 марта 2019

В вашем вызове re.sub () нужно учесть три случая удаления строк с буквой ab:

  1. шаблонов, за которыми следует символ конца строки (eol)
  2. последняя строка в тексте (без завершающего eol)
  3. , когда есть только одна строка без завершающего eol

Во втором случае вы хотите удалитьпредыдущий символ eol, чтобы избежать создания пустой строки.Третий случай выдаст пустую строку, если есть «b».

Жадность регулярных выражений введет четвертый случай, потому что не может быть никаких перекрытий шаблонов.Если ваша последняя строка содержит «b», а предыдущая строка также содержит «b», то в случае # 1 будет использован символ eol в предыдущей строке, поэтому он не сможет определить шаблон в последней строке (то есть eol, сопровождаемый образцом в конце текста).Эту проблему можно устранить путем очистки (случай № 1) последовательных совпадающих строк в качестве группы и включения последней строки в качестве необязательного компонента этой группы.Что бы это ни пропускало, это будут задние строки (случай № 2), где вы хотите удалить предыдущий eol, а не следующий.

Для управления повторением шаблона линии .*b.* вам необходимо собрать шаблон поиска из двух частей: шаблона линии и шаблона списка, который использует его несколько раз.Так как мы уже глубоко разбираемся в регулярных выражениях, почему бы не использовать re.sub () для этого.

import re

LinePattern = "(.*b.*)"
ListPattern = "(Line\n)+(Line$)?|(\nLine$)|(^Line$)" # Case1|Case2|Case3
Pattern     = re.sub("Line",LinePattern,ListPattern)

String  = "aba\naaa\naba\naaa\naba"
cleaned = re.sub(Pattern,"",String)

Примечание: этот метод также будет работать с другим символом разделения (например,запятая вместо eol), но символ должен быть исключен из шаблона линии (например, ([^,]*b[^,]*))

0 голосов
/ 05 марта 2019

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

import re
s = "aba\naaa\naba\naaa\naba"
print( "\n".join([st for st in s.splitlines() if 'b' not in st]) )
print( re.sub(r'^[^b\r\n]*b.*[\r\n]*', '', s, flags=re.M).strip() )

См. Демонстрационную версию Python .

подход без регулярных выражений, "\n".join([st for st in s.splitlines() if 'b' in st]),разбивает строку на разрывы строк, отфильтровывает все строки, не имеющие b, а затем соединяет строки обратно.

Подход с использованием регулярных выражений включает шаблон, подобный r'^[^b\r\n]b.*[\r\n]*':

  • ^ - начало строки
  • [^b\r\n]* - любые 0 или более символов, кроме CR, LF и b
  • b - b char
  • .* - любые 0+ символов, кроме символов разрыва строки
  • [\r\n]* - 0+ символов CR или LF.

Обратите внимание, что вам нужно использовать .strip()чтобы избавиться от нежелательных пробелов в начале / конце строки после этого.

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

rx = r'(?:{0}(?:\n|$))+|(?:\n|^){0}'.format(r'[^b\n]*b.*')
print( re.sub(rx, '', s) )

См. Демо Python .

Шаблон будет выглядеть как (?:[^b\n]*b.*(?:\n|$))+|(?:\n|^)[^b\n]*b.* и будет соответствовать

  • (?:[^b\n]*b.*(?:\n|$))+ - 1 или более повторений
    • [^b\n]* - любые 0+ символов, другие тхан b и новая строка
    • b.* - b и остальная часть строки (.* соответствует любым 0+ символам, кроме новой строки)
    • (?:\n|$) -перевод строки или конец строки
  • | - или
    • (?:\n|^) - перевод строки или начало строки
    • [^b\n]*b.* - aстрока с хотя бы одной b на ней
...