Ошибка в Notepad ++ / BOOST или ошибка в моем регулярном выражении? - PullRequest
3 голосов
/ 07 октября 2019

У меня есть файл, который структурирован следующим образом:

Line
foo Änderbar: PM baz
Line

Line
foo Änderbar: OM baz
Line

Line
foo Änderbar: ++ baz
Line

Line
foo Änderbar: -- baz
Line

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

В каждом блоке есть ровно одна строка в следующем формате:

  • хотя бы один символ, который не является новой строкойсопровождаемый
  • литеральной строкой 'Änderbar: ', сопровождаемый
  • точно одной из буквенных строк '++', '--', 'OM', 'PM', сопровождаемый
  • хотя бы один символ, который не является символом новой строки, за которым следует
  • символ новой строки, заканчивающийся строкой

В той же строке всегда есть хотя бы еще одна непустая строкаблок выше этой специальной строки и еще одна непустая строка ниже этой специальной строки.

Мне нужен эффективный метод для поиска (и, следовательно, выбора) всех блоков, где литерал после Änderbar: равен -- (найти/ выберите один блок за другим, каждый после повторного нажатия Find Next, то есть не выбирая все эти блоки одновременно).

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

Notepad ++ использует BOOST (и поддерживает выражения PCRE через BOOST),Поскольку это широко используется, я считаю эту проблему достаточно важной, чтобы разместить ее здесь, на тот случай, если BOOST действительно является причиной неправильного поведения.

Сказав это: я загрузил этот файл в Notepad ++, запустилв диалоговом окне «Поиск и замена» отметили . matches newline, отметили Regular Expression и ввели следующее регулярное выражение в текстовое поле Find What::

\n([^\n]+\n)+[^\n]+(Änderbar\:\ --[^\n]+\n)([^\n]+\n)+

Я был очень удивлен, что это заставило Notepad ++ вести себя странно: когда курсорбыл помещен в пустую строку непосредственно перед блоком с Änderbar: --, ударив Find Next нашел / выбрал этот блок, как и ожидалось. Но когда курсор был в другом месте, нажатие Find Next заставило Notepad ++ найти / выбрать весь остальной файл , то есть все блоки ниже позиции курсора.

Затем я проверил,он обнаружил бы блоки, имеющие ++ после Änderbar:, т.е. я изменил свое регулярное выражение на

\n([^\n]+\n)+[^\n]+(Änderbar\:\ \+\+[^\n]+\n)([^\n]+\n)+

Угадайте, что: это работало надежно в каждой ситуации. То же самое верно для последних обоих:

\n([^\n]+\n)+[^\n]+(Änderbar\:\ PM[^\n]+\n)([^\n]+\n)+
\n([^\n]+\n)+[^\n]+(Änderbar\:\ OM[^\n]+\n)([^\n]+\n)+

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

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

Так в чем здесь проблема? Есть ли ошибка в моем регулярном выражении или в Notepad ++?

ОБНОВЛЕНИЕ

Я удалил нужный файл и загрузил его в https://pastebin.com/w62E57U5. Чтобы воспроизвести проблему, выполните следующие действия:

  • Загрузите файл по ссылке выше и сохраните его где-нибудь на жестком диске (не копируйте текст непосредственно в Notepad ++).

  • Загрузить файл в Блокнот ++. Курсор теперь находится в самой верхней строке, и ничего не выделено.

  • Это важно: Нажмите Правка -> Преобразование EOL -> Unix (LF).

  • Убедитесь, что курсор все еще находится в самой верхней строке (которая пуста) и что ничего не выбрано.

  • Откройте диалоговое окно «Найти», выберите настройки и введите строку поиска, как описано выше.

  • Нажмите «Найти далее».

  • Обратите внимание, что теперь полный текст найден / выделен.

  • Оставив окно поиска открытым, удалите третью строку файла (там написано "Funktionspaket (e): ML"). ). Не просто очистите эту строку, но действительно удалите ее, чтобы между строкой до и после нее не осталось пустой строки.

  • Опять же, поместите курсор в самую верхнюю строку (котораявсе еще пусто) и убедитесь, что ничего не выбрано.

  • Нажмите «Найти далее».

  • Обратите внимание, что регулярное выражение теперь работает как положено.

Очевидно, кто-то пытается одурачить меня, верно?

Ответы [ 2 ]

1 голос
/ 07 октября 2019

Я думаю, что ключ в следующем: вам нужно начать свое регулярное выражение с ^ (начало строки).

Ваше первоначальное регулярное выражение становится:

^\n([^\n]+\n)+[^\n]+(Änderbar\:\ --[^\n]+\n)([^\n]+\n)+

Но вы можете упростить егос:

^\R(?:.+\R)+.+Änderbar: --.+\R(?:.+(?:\R|\z))+

Примечание: отметьте галочкой . matches newline

Где:

  • \R соответствует любому виду разрыва строки, нет необходимости изменять EOL.
  • \z соответствует концу файла, если вы его не используете, вы не можете сопоставить последнюю строку файла, если нет перевода строки.
  • (?:...)это группа без захвата, гораздо более эффективная (если вам, конечно, не нужно захватывать)

Оба отлично работают с вашими 2 примерами файлов.

1 голос
/ 07 октября 2019

Это не ошибка. Вы просто забываете что-то очень важное - с окончаниями строк Windows, ваши строки имеют \r перед \n, поэтому часть \n([^\n]+\n)+ вашего RegEx будет также соответствовать вашим пустым строкам, поэтому нажмите «Найти далее»соответствует всему с позиции курсора, а не с начала блока.

Перейдите в Edit> EOL Conversion> Unix (LF), и вы увидите, что он работает сейчас. Если вы хотите поддерживать окончания строк в Windows и Unix, вам придется менять каждый [^\n] на [^\r\n] и каждый \n на \r?\n.

...