НейроАгент

Регулярное выражение для строк без определенного слова

Узнайте, как использовать регулярные выражения с отрицательным просмотром вперед для сопоставления строк, не содержащих определенных слов. Полное руководство с примерами grep и советами по производительности.

Вопрос

Регулярное выражение для поиска строк, не содержащих конкретное слово

Я знаю, что можно сопоставить слово, а затем инвертировать совпадения с помощью других инструментов (например, grep -v). Однако, возможно ли с помощью регулярного выражения находить строки, которые не содержат конкретное слово, например, ‘hede’?

Входные данные:
hoho
hihi
haha
hede

Код:
grep “<Регулярное выражение для ‘не содержит hede’>” input

Желаемый вывод:
hoho
hihi
haha

НейроАгент

Чтобы сопоставить строки, которые не содержат определенное слово, например ‘hede’, с помощью регулярного выражения, можно использовать негативное утверждение просмотра вперед (negative lookahead). Регулярное выражение ^(?!.*\bhede\b).*$ будет сопоставлять любую строку, которая не содержит слово “hede”, где негативный просмотр вперед (?!.*\bhede\b) гарантирует отсутствие слова перед сопоставлением всей строки с помощью .*.

Содержание


Понимание негативного просмотра вперед

Негативный просмотр вперед (negative lookahead) - это утверждение нулевой ширины в регулярных выражениях, которое проверяет, не появляется ли определенный шаблон в текущей позиции, не потребляя при этом никаких символов. Синтаксис для негативного просмотра вперед - (?!pattern), где pattern - это последовательность, которую вы хотите исключить.

Согласно Regular Expressions Cookbook, “утверждения негативного просмотра вперед могут быть полезны для проверки строк, которые не начинаются с определенных слов” и, в более широком смысле, для обеспечения отсутствия шаблонов в любом месте текста.

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


Базовое решение для сопоставления строк

Наиболее распространенный шаблон для сопоставления строк, которые не содержат определенного слова:

regex
^(?!.*\bhede\b).*$

Разберем этот шаблон:

  • ^ - Якорь начала строки
  • (?!.*\bhede\b) - Негативный просмотр вперед, который утверждает, что слово “hede” не появляется нигде в строке
    • .* - Жадно сопоставляет любые символы (кроме символа новой строки)
    • \b - Граница слова гарантирует, что мы сопоставляем целое слово, а не часть другого слова
  • .* - Сопоставляет все содержимое строки
  • $ - Якорь конца строки

Обсуждение на Stack Overflow объясняет, что этот подход “позволяет части просмотра вперед проверить весь текст, убедиться в отсутствии ‘hede’, а затем обычная часть (.) может “съесть” весь текст за один раз.”


Рассмотрения границ слов

Границы слов (\b) являются важными при сопоставлении определенных слов для избежания частичных совпадений. Без границ слов шаблон вроде hede также будет сопоставлять подстроки внутри более крупных слов, таких как “behemoth” или “hedged”.

Шаблон \bhede\b гарантирует, что вы сопоставляете полное слово “hede”, а не просто подстроку. Как объясняется на Saturn Cloud, “\b соответствует границе слова, что гарантирует, что ‘word’ не является частью более крупного слова.”

Если вы хотите специально сопоставлять строки, которые не содержат подстроку независимо от границ слов, можно опустить маркеры \b:

regex
^(?!.*hede).*$

Это исключит любую строку, содержащую “hede” в качестве подстроки, что может быть полезно в некоторых случаях, но может привести к непреднамеренным исключениям.


Практическая реализация с помощью Grep

Чтобы использовать это с grep, как указано в вашем вопросе, вы бы использовали:

bash
grep -P '^(?!.*\bhede\b).*$' input

Флаг -P включает функции регулярных выражений, совместимые с Perl, которые поддерживают негативный просмотр вперед. Согласно руководству по регулярным выражениям от Sentry, “(?!word): Утверждение негативного просмотра вперед” - это ключевой синтаксис для этой функциональности.

Для вашего конкретного примера:

bash
grep -P '^(?!.*\bhede\b).*$' input

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

hoho
hihi
haha

Обратите внимание, что флаг -P может быть недоступен во всех реализациях grep. Для систем, которые не поддерживают регулярные выражения, совместимые с Perl, могут потребоваться альтернативные подходы, такие как grep -v 'hede', как вы упоминали.


Альтернативные подходы

Использование ленивого квантификатора в просмотре вперед

Некоторые движки регулярных выражений выигрывают от использования ленивых квантификаторов в негативном просмотре вперед для лучшей производительности:

regex
^(?=.*?)(?:(?!hede).)*$

Как отмечается в ответе на Stack Overflow, “Обратите внимание, что ленивый квантификатор (?) в части негативного просмотра вперед является необязательным, вы можете использовать () жадный квантификатор вместо него, в зависимости от ваших данных: если ‘hede’ присутствует и в первой половине текста, ленивый квантификатор может быть быстрее; в противном случае жадный квантификатор будет быстрее.”

Использование нескольких просмотров вперед

Для более сложных шаблонов исключения можно объединять несколько негативных просмотров вперед:

regex
^(?!.*\bhede\b)(?!.*\berror\b)(?!.*\bwarning\b).*$

Это будет сопоставлять строки, не содержащие ни одного из слов “hede”, “error” или “warning”.

Использование классов символов

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

regex
^(?:(?!hede).)*$

Как объясняется на RexEgg, “После того как негативный просмотр вперед утверждает, что следующее за текущей позицией не является Q, \w соответствует символу слова.”


Рассмотрения производительности

Негативные просмотры вперед могут быть вычислительно затратными, особенно когда применяются к каждой позиции в длинной строке. Как предупреждает O’Reilly Regular Expressions Cookbook, “Проверка негативного просмотра вперед для каждой позиции в строке или тексте является довольно неэффективной.”

Для лучшей производительности с большими файлами:

  1. Рассмотрите возможность использования grep -v 'hede' вместо этого, что более эффективно
  2. Если вы должны использовать регулярные выражения, сохраняйте шаблоны как можно проще
  3. Избегайте сложных вложенных просмотров вперед, когда существуют более простые альтернативы
  4. Рассмотрите возможность предварительной фильтрации с помощью более быстрых инструментов перед применением регулярных выражений

Источники

  1. Регулярное выражение для сопоставления строки, не содержащей слово - Stack Overflow
  2. Написать регулярное выражение для сопоставления строк, не содержащих слово | Sentry
  3. 5.11. Сопоставление полных строк, не содержащих слово - Regular Expressions Cookbook, 2-е издание
  4. Регулярное выражение для сопоставления строки, не содержащей слово | Блог Saturn Cloud
  5. Регулярное выражение для сопоставления строки, не содержащей слово
  6. Учебник по регулярным выражениям: Просмотр вперед и назад - Утверждения нулевой длины
  7. Учебник по просмотру вперед и назад - Советы и хитрости

Заключение

Сопоставление строк, не содержащих определенное слово, с помощью регулярных выражений действительно возможно через утверждения негативного просмотра вперед. Ключевые выводы:

  1. Используйте негативный просмотр вперед (?!pattern), чтобы указать, что вы хотите исключить
  2. Наиболее надежный шаблон - ^(?!.*\bword\b).*$ для сопоставления полных строк без определенного слова
  3. Границы слов (\b) важны для избежания частичных совпадений внутри более крупных слов
  4. Для вашего примера с grep используйте grep -P '^(?!.*\bhede\b).*$' input для достижения желаемого результата
  5. Учитывайте последствия для производительности - в простых случаях grep -v 'hede' может быть более эффективным

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