Соответствует определенной строке, за которой не следует другая конкретная строка до следующего появления первой - PullRequest
0 голосов
/ 29 октября 2018

Я начну с примера, так как это может быть самым простым объяснением. У нас есть многострочный файл:

...
STARTING LINE with something 83
...
STARTING LINE with other 12
...
ENDING LINE with yet another info
...
STARTING LINE with another 43
...

... означает что угодно (несколько строк, включая пустые строки), кроме STARTING LINE .* и ENDING LINE .*.

Мы должны захватить группы, содержащие все STARTING LINE .*, которые не , за которыми следует ENDING LINE .*, что означает первое и последнее вхождение STARTING LINE .* в примере.

Число вхождений одних пар STARTING LINE .* и STARTING LINE .*...ENDING LINE .* неизвестно.

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

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

  1. (^STARTING LINE .*?$)(?!^ENDING LINE)[.\n]+

  2. (^STARTING LINE .*?$(?!.*^ENDING LINE)[.\n]*)

Обратите внимание, что мы хотим, чтобы в группе были только STARTING LINE .* строк.

Мы используем Python 2.7 regex engine с re.MULTILINE flags (gm). Пробовал также с дополнительной опцией re.DOTALL (s), но безуспешно.

Ответы [ 3 ]

0 голосов
/ 29 октября 2018

Следующее регулярное выражение работает для меня в режиме MULTILINE ( демо ):

^STARTING LINE .+$\n(?!(?:(?!(?:STARTING|ENDING) LINE ).+\n)*ENDING LINE )

Пояснение:

  • ^STARTING LINE .+\n: стартовая строка ($ не нужна из-за \n)
  • (?:(?!(?:STARTING|ENDING) LINE ).+\n)*: ноль или более средних строк (^ или $ не требуется из-за \n)
  • ENDING LINE: конечная строка (^ не требуется из-за предыдущего \n)

PS. Это предполагает, что ваши переводы строки действительно \n, а не \r\n.

0 голосов
/ 30 октября 2018

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

В последнем матче вы можете проверить, используя отрицательный прогноз, что вы не можете найти новую строку, за которой следует ENDING LINE.

^STARTING LINE(?:.*(?:(?!\n(STARTING|ENDING) LINE)\n.*)*(?=\nSTARTING LINE)|(?![\s\S]*\nENDING LINE)[\s\S]*$)

Regex demo

Объяснение

  • ^ Начало строки
  • STARTING LINE Совпадение буквально
  • (?: Начать без захвата группы
    • .* Совпадение 0+ символов
    • (?: Группа без захвата
      • (?! Отрицательный взгляд вперед, чтобы утверждать, что на правой стороне нет
        • \n(STARTING|ENDING) LINE Соответствует символу новой строки, после которого следует НАЧАЛО ЛИНИИ или КОНЕЦ ЛИНИИ
      • ) Закрыть группу захвата
      • \n.* соответствует символу новой строки и 0+ символам
    • )* Закрыть негативный прогноз и повторить 0+ раз
    • (?= Позитивный взгляд, чтобы утверждать, что на правой стороне
      • \nSTARTING LINE Соответствует символу новой строки, после которого следует НАЧАЛО ЛИНИИ
    • ) Закрыть взгляд
    • | или
    • (?! Начать негативный взгляд
      • [\s\S]*\nENDING LINE Соответствует любому символу, включая символы разрыва строки, 0+ раз, за ​​которыми следует новая строка и ENDING LINE
    • ) Закрыть негативный взгляд
    • [\s\S]*$ Соответствует любому символу, включая символы разрыва строки, 0+ раз до конца строки
  • ) Закрыть группу без захвата
0 голосов
/ 29 октября 2018

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

Если полезно, вот решение для awk:

$ awk '/^STARTING LINE / { if ( startingline > "" ) { print(startingline); startingline=""; } else { startingline=$0; } } /^ENDING LINE / { startingline=""; } END { if ( startingline > "" ) print(startingline); }' file.txt
STARTING LINE with something 83
STARTING LINE with another 43
...