Как мне сопоставить последнее вхождение Pattern перед другим Pattern с REGEX - PullRequest
1 голос
/ 26 сентября 2019

У меня огромный XML-файл, и мне нужно извлечь содержимое целого тега, который содержит последовательность чисел.В моем файле все по одной строке, я добавил здесь разрывы строк, чтобы сделать его более читабельным

Итак, у меня есть упрощенный пример

Файл:

<ORDERS>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>12345</tag3><tag4>ccc</tag4></IDOC>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>23456</tag3><tag4>ccc</tag4></IDOC>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>0007537181</tag3><tag4>ccc</tag4></IDOC>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>34567</tag3><tag4>ccc</tag4></IDOC>
</ORDER>

Iхочу соответствовать тегу IDOC BEGIN, который содержит последовательность 0007537181. Таким образом, это будет

<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>0007537181</tag3><tag4>ccc</tag4></IDOC>

Пока я получил это регулярное выражение:

cat myfile | grep -oP '<IDOC BEGIN.*?0007536846.*?</IDOC>'

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

<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>12345</tag3><tag4>ccc</tag4></IDOC>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>23456</tag3><tag4>ccc</tag4></IDOC>
<IDOC BEGIN><tag1>aaa</tag1><tag2>bbb</tag2><tag3>0007537181</tag3><tag4>ccc</tag4></IDOC>

Мне удалось обойти это, отправив это второму регулярному выражению, которое получает последнее вхождение IDOC BEGIN

cat myfile | grep -oP '<IDOC BEGIN.*?0007536846.*?</IDOC>' | grep -oP '<IDOC BEGIN(?!.*<IDOC BEGIN).*?</IDOC>'

Подводя итог, мне нужно получить последнюю IDOC BEGIN перед последовательностью числа

Имейте в виду, что в исходном файле нет разрывов строк, все в одной строке.

1 Ответ

0 голосов
/ 26 сентября 2019

Регулярное выражение, которое вы можете использовать, основано либо на шаблоне жадных точек, помещенном в начале и сопровождаемом \K оператором сброса совпадения , либо на основе жадного жетона ,И то, и другое очень небезопасно, когда дело доходит до больших строк с частичным совпадением (но не совпадением).

Итак, два регулярных выражения:

.*\K<IDOC BEGIN.*?0007536846.*?</IDOC>
<IDOC BEGIN(?:(?!<IDOC BEGIN).)*?0007536846(?:(?!<IDOC BEGIN).)*?</IDOC>

Лучшая идея - развернуть закаленный жадный жетонв этих случаях:

<IDOC BEGIN[^<]*(?:<(?!IDOC BEGIN)[^<]*?)*0007537181.*?</IDOC>

См. демонстрационную версию regex

Первый .*? заменен на [^<]*(?:<(?!IDOC BEGIN)[^<]*?)*:

  • [^<]* - отрицательный класс символов, соответствующий 0 или более символам, отличным от <, максимально возможному числу
  • (?:<(?!IDOC BEGIN)[^<]*?)* - 0 или более повторений
    • <(?!IDOC BEGIN) - a < символ, за которым сразу не следует IDOC BEGIN строка
    • [^<]*? - класс отрицанных символов, соответствующий 0 или более символам, отличным от <, как можно меньше
...