[ID CC-CC2022] Двойной positive lookbehind в запросе grep

  • Автор темы Автор темы veretragna
  • Дата начала Дата начала

veretragna

γνώσις
Топикстартер
Сообщения
578
Реакции
200
Здравствуйте.
Работаю над усовершенствованием grep'а для переноса коротких слов в тексте, чтобы они никогда не вылезали в конце строки.
Текущий рабочий grep выглядит вот так:
(?i)(?<= )(de|un)( |[[:punct:]] )+
Переводя на человеческий, делает следующее: найти одно из слов в списке с разделителями |, заодно захватить следующий за ним пробел или знак пунктуации с пробелом, но перед этим проверить, нет ли пробела перед словом; поиск регистронезависимый.

Возникает проблема: обычно он работает правильно, но вот, например, в строчке "Dentro de un entorno", где попадаются два таких слова подряд он находит первое слово de и следующий за ним пробел, а вот слово un и следующий за ним пробел не находит.

Интуитивно понял, что предыдущий grep может просто не возвращаться к уже пройденному пробелу, и поэтому решил сделать проще и искать не "слово и пробел", а тупо пробел после искомого слова, так тоже получается нормально:
(?i)(?<=de|un)( |[[:punct:]] )

Но не понимаю, как сделать так, чтобы перед positive lookbehind (?<=de|un) сделать еще одну проверку на пробел; вот такой код не работает, совпадений не находит.
(?i)(?<= )(?<=de|un)( |[[:punct:]] )
Если оставить как есть, без проверки на пробел, то находятся даже слова, которые заканчиваются на de и un, это недопустимо.

Как правильно вызвать два подряд positive lookbehind в последнем grep'е? Гугление по этому вопросу ничего не дало.
Если этого нельзя сделать, то как заставить самый первый grep находить второе слово?
 
Возникает проблема: обычно он работает правильно, но вот, например, в строчке "Dentro de un entorno", где попадаются два таких слова подряд он находит первое слово de и следующий за ним пробел, а вот слово un и следующий за ним пробел не находит.
У меня, кстати, всегда тоже самое было, первый проверял, следующий после первого игнорировал.
 
В итоге проблему решил.
Обошелся и без positive lookbehind, и без переделки первого грепа - оказалось достаточно добавить границы слова ( \b ) по сторонам списка слов для поиска. Теперь выделяются только пробелы после цельных слов и к пробелам можно применить атрибут "No Break".
Как обычно, делюсь рабочим результатом:
(?i)(?<=\bde|un\b)( |[[:punct:]] )
 
Однако, истории еще не конец. Если в списке слов три слова, все работает; добавляю четвертое слово в середину строки - становится 0 попаданий.
Греп каждый раз удивляет
 
И с этим разобрался. Lookbehind принимает в качестве аргумента только слова одинаковой длины.
Если вставить слова разной длины в выражение, попаданий будет ноль.
Надо делать 3 отдельных прохода для слов длиной 1, 2 и 3 символа. Буду писать скрипт
 
Оказалось намного проще: специально для таких, как я, греп давно поддерживает модификатор \K.
Вот так все работает со словами любой длины
(?i)(\bde|un|e|los\b)\K( |[[:punct:]] )
 
В результате подробного тестирования пришел вот к такой форме выражения.
(?i)\b(e|o|el|al|del|su|un|de|en|la|a|y|se|si)\b\K([[:space:]]|[[:punct:]][[:space:]])
Этот точно отлично работает.
 
  • Спасибо
Реакции: NNN5 и Petruccio