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

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

Входные данные:

hoho
hihi
haha
hede

Код:

grep "<Regex for 'doesn't contain hede'>" input

Желаемый результат:

hoho
hihi
haha

36 ответов

Решение

The notion that regex doesn't support inverse matching is not entirely true. You can mimic this behavior by using negative look-arounds:

^((?!hede).)*$

The regex above will match any string, or line without a line break, not containing the (sub)string 'hede'. As mentioned, this is not something regex is "good" at (or should do), but still, it is possible.

And if you need to match line break chars as well, use the DOT-ALL modifier (the trailing s in the following pattern):

/^((?!hede).)*$/s

or use it inline:

/(?s)^((?!hede).)*$/

(where the /.../are the regex delimiters, ie, not part of the pattern)

If the DOT-ALL modifier is not available, you can mimic the same behavior with the character class[\s\S]:

/^((?!hede)[\s\S])*$/

объяснение

A string is just a list of n персонажи. Before, and after each character, there's an empty string. So a list of ncharacters will haven+1empty strings. Рассмотрим строку"ABhedeCD":

    ┌──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┬───┬──┐
S = │e1│ A │e2│ B │e3│ h │e4│ e │e5│ d │e6│ e │e7│ C │e8│ D │e9│
    └──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┴───┴──┘

index    0      1      2      3      4      5      6      7

где e это пустые строки. Регулярное выражение (?!hede). смотрит вперед, чтобы увидеть, если нет подстроки "hede"чтобы увидеть, и если это так (так что-то еще видно), то. (точка) будет соответствовать любому символу, кроме разрыва строки. Осмотры также называются утверждениями нулевой ширины, потому что они не потребляют никаких символов. Они только утверждают / подтверждают что-то.

Итак, в моем примере каждая пустая строка сначала проверяется, чтобы увидеть, нет ли "hede" впереди, прежде чем персонаж будет поглощен .(Точка). Регулярное выражение (?!hede).будет делать это только один раз, поэтому он упакован в группу и повторяется ноль или более раз:((?!hede).)*, Наконец, начало и конец ввода привязываются, чтобы убедиться, что весь вход используется:^((?!hede).)*$

Как видите, вход "ABhedeCD" потерпит неудачу, потому что на e3Регулярное выражение(?!hede) не удается (есть"hede" впереди!).

Обратите внимание, что решение не начинается с "хеде":

^(?!hede).*$

обычно гораздо эффективнее, чем решение , не содержащее "хеде":

^((?!hede).)*$

Первый проверяет "hede" только в первой позиции входной строки, а не в каждой позиции.

Если вы просто используете его для grep, вы можете использовать grep -v hede получить все строки, которые не содержат хеде.

ЭТА О, перечитывая вопрос, grep -v это, вероятно, то, что вы имели в виду под "настройками инструментов".

Ответ:

^((?!hede).)*$

Объяснение:

^начало строки,( сгруппировать и записать в \1 (0 или более раз (соответствует максимально возможному количеству)),
(?! посмотрите вперед, чтобы увидеть, если нет,

hede твоя строка,

) конец прогнозирования, . любой символ, кроме \ n,
)* конец \ 1 (Примечание: поскольку вы используете квантификатор для этого захвата, в \ 1 будет сохраняться только последнее ПОСЛЕДНЕЕ повторение захваченного шаблона)
$ перед необязательным \ n и концом строки

Приведенные ответы отлично, просто академический балл:

Регулярные выражения в смысле теоретических компьютерных наук НЕ МОГУТ делать это так. Для них это должно было выглядеть примерно так:

^([^h].*$)|(h([^e].*$|$))|(he([^h].*$|$))|(heh([^e].*$|$))|(hehe.+$) 

Это только соответствует ПОЛНОМУ. Делать это для под-матчей было бы еще более неловко.

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

^(?!hede$).*

Например, если вы хотите разрешить все значения, кроме "foo" (то есть "foofoo", "barfoo" и "foobar" пройдут, но "foo" завершится ошибкой), используйте: ^(?!foo$).*

Конечно, если вы проверяете точное равенство, лучшим общим решением в этом случае является проверка на равенство строк, т.е.

myStr !== 'foo'

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

!/^[a-f]oo$/i.test(myStr)

Однако решение regex в верхней части этого ответа может быть полезным в ситуациях, когда требуется положительный тест regex (возможно, через API).

С отрицательным взглядом, регулярное выражение может соответствовать чему-то, не содержащему определенного шаблона. На это отвечает и объясняет Барт Киерс. Отличное объяснение!

Тем не менее, с ответом Барта Киерса, предварительная часть будет проверять от 1 до 4 символов вперед при сопоставлении с любым отдельным символом. Мы можем избежать этого и позволить части предпросмотра проверить весь текст, убедиться, что нет "хеде", и тогда нормальная часть (.*) Может съесть весь текст за один раз.

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

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

Обратите внимание, что (*?) Ленивый квантификатор в части с отрицательным прогнозом не является обязательным, вы можете вместо этого использовать (*) жадный квантификатор, в зависимости от ваших данных: если 'hede' присутствует и в первой половине текста, ленивый квантификатор может быть быстрее; иначе жадный квантификатор будет быстрее. Однако, если "хеде" не присутствует, оба будут равны медленно.

Вот демонстрационный код.

Для получения дополнительной информации о Lookahead, пожалуйста, прочитайте большую статью: Освоение Lookahead и Lookbehind.

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

var _ = regexGen;

var regex = _(
    _.startOfLine(),             
    _.anything().notContains(       // match anything that not contains:
        _.anything().lazy(), 'hede' //   zero or more chars that followed by 'hede',
                                    //   i.e., anything contains 'hede'
    ), 
    _.endOfLine()
);

FWIW, так как регулярные языки (или рациональные языки) закрыты при дополнении, всегда можно найти регулярное выражение (также называемое рациональное выражение), которое отрицает другое выражение. Но не многие инструменты реализуют это.

Vcsn поддерживает этот оператор (который он обозначает {c}, постфикс).

Сначала вы определяете тип ваших выражений: метки являются буквами (lal_char) выбрать из a в z например (определение алфавита при работе с дополнением, конечно, очень важно), а "значение", вычисляемое для каждого слова, является просто логическим: true слово принято, false, отклонено.

В Python:

In [5]: import vcsn
        c = vcsn.context('lal_char(a-z), b')
        c
Out[5]: {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z} → 

Затем вы вводите выражение:

In [6]: e = c.expression('(hede){c}'); e
Out[6]: (hede)^c

преобразовать это выражение в автомат:

In [7]: a = e.automaton(); a

Соответствующий автомат

наконец, преобразовать этот автомат обратно в простое выражение.

In [8]: print(a.expression())
        \e+h(\e+e(\e+d))+([^h]+h([^e]+e([^d]+d([^e]+e[^]))))[^]*

где + обычно обозначается |, \e обозначает пустое слово, и [^] обычно написано . (любой персонаж). Итак, немного переписав ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*,

Вы можете увидеть этот пример здесь и попробовать Vcsn онлайн там.

Вот хорошее объяснение того, почему не легко отрицать произвольное регулярное выражение. Я должен согласиться с другими ответами, хотя: если это что-то кроме гипотетического вопроса, то регулярное выражение здесь не правильный выбор.

Ориентиры

Я решил оценить некоторые из представленных опций и сравнить их производительность, а также использовать некоторые новые функции. Сравнительный анализ на.NET Regex Engine: http://regexhero.net/tester/

Контрольный текст:

Первые 7 строк не должны совпадать, так как они содержат искомое выражение, а нижние 7 строк должны совпадать!

Regex Hero is a real-time online Silverlight Regular Expression Tester.
XRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex HeroRegex HeroRegex HeroRegex HeroRegex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her Regex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.Regex Hero
egex Hero egex Hero egex Hero egex Hero egex Hero egex Hero Regex Hero is a real-time online Silverlight Regular Expression Tester.
RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRegex Hero is a real-time online Silverlight Regular Expression Tester.

Regex Her
egex Hero
egex Hero is a real-time online Silverlight Regular Expression Tester.
Regex Her is a real-time online Silverlight Regular Expression Tester.
Regex Her Regex Her Regex Her Regex Her Regex Her Regex Her is a real-time online Silverlight Regular Expression Tester.
Nobody is a real-time online Silverlight Regular Expression Tester.
Regex Her o egex Hero Regex  Hero Reg ex Hero is a real-time online Silverlight Regular Expression Tester.

Результаты:

Результатами являются итерации в секунду в качестве медианы 3 прогонов - большее число = лучшее

01: ^((?!Regex Hero).)*$                    3.914   // Accepted Answer
02: ^(?:(?!Regex Hero).)*$                  5.034   // With Non-Capturing group
03: ^(?>[^R]+|R(?!egex Hero))*$             6.137   // Lookahead only on the right first letter
04: ^(?>(?:.*?Regex Hero)?)^.*$             7.426   // Match the word and check if you're still at linestart
05: ^(?(?=.*?Regex Hero)(?#fail)|.*)$       7.371   // Logic Branch: Find Regex Hero? match nothing, else anything

P1: ^(?(?=.*?Regex Hero)(*FAIL)|(*ACCEPT))  ?????   // Logic Branch in Perl - Quick FAIL
P2: .*?Regex Hero(*COMMIT)(*FAIL)|(*ACCEPT) ?????   // Direct COMMIT & FAIL in Perl

Поскольку.NET не поддерживает глаголы действия (*FAIL и т. Д.), Я не смог протестировать решения P1 и P2.

Резюме:

Я пытался протестировать большинство предложенных решений, возможна некоторая оптимизация для определенных слов. Например, если первые две буквы строки поиска не совпадают, ответ 03 можно расширить до^(?>[^R]+|R+(?!egex Hero))*$ в результате чего небольшой прирост производительности.

Но в целом наиболее читаемым и быстродействующим решением, по-видимому, является 05 с условным оператором или 04 с положительным квантификатором. Я думаю, что Perl-решения должны быть еще быстрее и более легко читаемыми.

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

Ответ в том, что с POSIX grep, невозможно буквально удовлетворить этот запрос:

grep "Regex for doesn't contain hede" Input

Причина в том, что POSIX grep требуется только для работы с базовыми регулярными выражениями, которые просто недостаточно мощны для выполнения этой задачи (они не способны анализировать обычные языки из-за отсутствия чередования и группировки).

Тем не менее, GNU grep реализует расширения, которые позволяют это. Особенно, \| является оператором чередования в реализации BRE в GNU, и \( а также \) являются операторами группировки. Если ваш механизм регулярных выражений поддерживает чередование, выражения с отрицательными скобками, группировку и звездочку Клини и может привязывать начало и конец строки, это все, что вам нужно для этого подхода.

С GNU grepбыло бы что-то вроде:

grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" Input

(найдено с Grail и некоторыми дополнительными оптимизациями, сделанными вручную).

Вы также можете использовать инструмент, который реализует расширенные регулярные выражения, такие как egrep, чтобы избавиться от обратной косой черты:

egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" Input

Вот скрипт для его проверки (обратите внимание, что он генерирует файл testinput.txt в текущем каталоге):

#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$"

# First four lines as in OP's testcase.
cat > testinput.txt <<EOF
hoho
hihi
haha
hede

h
he
ah
head
ahead
ahed
aheda
ahede
hhede
hehede
hedhede
hehehehehehedehehe
hedecidedthat
EOF
diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)

В моей системе это печатает:

Files /dev/fd/63 and /dev/fd/62 are identical

как и ожидалось.

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

И, наконец, как все уже заметили, если ваш движок регулярных выражений поддерживает отрицательный прогноз, это значительно упрощает задачу. Например, с помощью GNU grep:

grep -P '^((?!hede).)*$' Input

Обновление: я недавно нашел превосходную библиотеку FormalTheory Кендалла Хопкинса, написанную на PHP, которая обеспечивает функциональность, аналогичную Grail. Используя его и написанный мной упрощатель, я смог написать онлайн-генератор отрицательных регулярных выражений с учетом входной фразы (в настоящее время поддерживаются только буквенно-цифровые и пробельные символы): http://www.formauri.es/personal/pgimeno/misc/non-match-regex/

За hede это выводит:

^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$

что эквивалентно вышеизложенному.

Не регулярное выражение, но я нашел логичным и полезным использовать последовательные greps с pipe для устранения шума.

например. искать файл конфигурации Apache без всех комментариев-

grep -v '\#' /opt/lampp/etc/httpd.conf      # this gives all the non-comment lines

а также

grep -v '\#' /opt/lampp/etc/httpd.conf |  grep -i dir

Логика последовательных grep'ов есть (не комментарий) и (соответствует dir)

При этом вы избегаете проверять прогноз на каждой позиции:

/^(?:[^h]+|h++(?!ede))*+$/

эквивалентно (для.net):

^(?>(?:[^h]+|h+(?!ede))*)$

Старый ответ:

/^(?>[^h]+|h+(?!ede))*$/

Вышеупомянутый (?:(?!hede).)* это здорово, потому что это можно закрепить.

^(?:(?!hede).)*$               # A line without hede

foo(?:(?!hede).)*bar           # foo followed by bar, without hede between them

Но в этом случае будет достаточно:

^(?!.*hede)                    # A line without hede

Это упрощение готово к добавлению предложений "И":

^(?!.*hede)(?=.*foo)(?=.*bar)   # A line with foo and bar, but without hede
^(?!.*hede)(?=.*foo).*bar       # Same

На мой взгляд, более читаемый вариант верхнего ответа:

^(?!.*hede)

По сути, "сопоставьте любую строку, кроме той, в которой есть" хеде ", - так что требование почти прямо переведено в регулярное выражение.

Вот как я это сделаю:

^[^h]*(h(?!ede)[^h]*)*$

Точнее и эффективнее других ответов. Он реализует метод эффективности "развернутой петли" Фридла и требует гораздо меньшего возврата.

Другой вариант - добавить позитивный прогноз и проверить, hede находится в любом месте входной строки, то мы бы отрицаем это с помощью выражения, похожего на:

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

с границами слов.


Выражение объясняется на верхней правой панели regex101.com, если вы хотите изучить / упростить / изменить его, и по этой ссылке вы можете посмотреть, как оно будет соответствовать некоторым образцам входных данных, если хотите.


Цепь RegEx

jex.im визуализирует регулярные выражения:

Если вы хотите сопоставить символ, чтобы отрицать слово, подобное отрицанию класса символов:

Например, строка:

<?
$str="aaa        bbb4      aaa     bbb7";
?>

Не использовать:

<?
preg_match('/aaa[^bbb]+?bbb7/s', $str, $matches);
?>

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

<?
preg_match('/aaa(?:(?!bbb).)+?bbb7/s', $str, $matches);
?>

уведомление "(?!bbb)." это не взгляд назад и не взгляд вперед, это выглядит как ток, например:

"(?=abc)abcde", "(?!abc)abcde"

ОП не указал или не пометил пост, чтобы указать контекст (язык программирования, редактор, инструмент), в котором будет использоваться регулярное выражение.

Для меня иногда нужно сделать это при редактировании файла с помощью Textpad,

Textpad поддерживает некоторые Regex, но не поддерживает lookahead или lookbehind, поэтому требуется несколько шагов.

Если я хочу сохранить все строки, которые НЕ содержат строку hede Я бы сделал это так:

1. Поиск / замена всего файла, чтобы добавить уникальный "тег" в начале каждой строки, содержащей любой текст.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. Удалите все строки, содержащие строку hede (строка замены пуста):

    Search string:<@#-unique-#@>.*hede.*\n  
    Replace string:<nothing>  
    Replace-all  

3. На этом этапе все оставшиеся строки НЕ содержат строку hede, Удалите уникальный "тег" из всех строк (строка замены пуста):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

Теперь у вас есть оригинальный текст со всеми строками, содержащими строку hede удален.


Если я хочу сделать что-то еще только для строк, которые не содержат строку hede Я бы сделал это так:

1. Поиск / замена всего файла, чтобы добавить уникальный "тег" в начале каждой строки, содержащей любой текст.

    Search string:^(.)  
    Replace string:<@#-unique-#@>\1  
    Replace-all  

2. Для всех строк, содержащих строку hede, удалите уникальный "тег":

    Search string:<@#-unique-#@>(.*hede)
    Replace string:\1  
    Replace-all  

3. На этом этапе все строки, начинающиеся с уникального "тега", НЕ содержат строку hede, Теперь я могу сделать что- то еще только для этих строк.

4. Когда я закончу, я удаляю уникальный "Tag" из всех строк (строка замены пуста):

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  

С момента появления ruby-2.4.1 мы можем использовать новый оператор Absent в регулярных выражениях Ruby.

из официального документа

(?~abc) matches: "", "ab", "aab", "cccc", etc.
It doesn't match: "abc", "aabc", "ccccabc", etc.

Таким образом, в вашем случае ^(?~hede)$ делает работу за вас

2.4.1 :016 > ["hoho", "hihi", "haha", "hede"].select{|s| /^(?~hede)$/.match(s)}
 => ["hoho", "hihi", "haha"]

Через глагол PCRE (*SKIP)(*F)

^hede$(*SKIP)(*F)|^.*$

Это полностью пропустит строку, которая содержит точную строку hede и соответствует всем оставшимся строкам.

DEMO

Исполнение частей:

Давайте рассмотрим приведенное выше регулярное выражение, разбив его на две части.

  1. Часть до | условное обозначение. Часть не должна совпадать.

    ^hede$(*SKIP)(*F)
    
  2. Часть после | условное обозначение. Часть должна соответствовать.

    ^.*$
    

ЧАСТЬ 1

Движок Regex начнет выполнение с первой части.

^hede$(*SKIP)(*F)

Объяснение:

  • ^ Утверждает, что мы на старте.
  • hede Соответствует строке hede
  • $ Утверждает, что мы находимся в конце строки.

Таким образом, строка, которая содержит строку hede будет соответствовать. После того, как двигатель регулярных выражений видит следующее (*SKIP)(*F) (Примечание: вы могли бы написать (*F) как (*FAIL) глагол, он пропускает и делает совпадение неудачным. | вызываемое изменение или логический оператор ИЛИ, добавленный рядом с глаголом PCRE, который соответствует всем границам, существующим между каждым и каждым символом во всех строках, кроме строки, содержащей точную строку hede, Смотрите демо здесь. То есть он пытается сопоставить символы из оставшейся строки. Теперь регулярное выражение во второй части будет выполнено.

ЧАСТЬ 2

^.*$

Объяснение:

  • ^ Утверждает, что мы на старте. то есть он соответствует всем началам строки, кроме той, что в hede линия. Смотрите демо здесь.
  • .* В многострочном режиме . будет соответствовать любому символу, кроме символов новой строки или возврата каретки. А также * будет повторять предыдущий символ ноль или более раз. Так .* будет соответствовать всей линии. Смотрите демо здесь.

    Эй, почему ты добавил.* Вместо.+?

    Так как .* будет соответствовать пустой строке, но .+ не будет совпадать с пробелом Мы хотим сопоставить все строки, кроме hede, может быть возможность пустых строк также на входе. так что вы должны использовать .* вместо .+, .+ будет повторять предыдущий символ один или несколько раз. Увидеть .* соответствует пустой строке здесь.

  • $ Закрепление конца строки здесь не обязательно.

Язык TXR поддерживает отрицание регулярных выражений.

$ txr -c '@(repeat)
@{nothede /~hede/}
@(do (put-line nothede))
@(end)'  Input

Более сложный пример: сопоставить все строки, начинающиеся с a и заканчивается z, но не содержат подстроки hede:

$ txr -c '@(repeat)
@{nothede /a.*z&~.*hede.*/}
@(do (put-line nothede))
@(end)' -
az         <- echoed
az
abcz       <- echoed
abcz
abhederz   <- not echoed; contains hede
ahedez     <- not echoed; contains hede
ace        <- not echoed; does not end in z
ahedz      <- echoed
ahedz

Отрицание регулярных выражений само по себе не особенно полезно, но когда у вас также есть пересечение, все становится интереснее, поскольку у вас есть полный набор операций с булевыми множествами: вы можете выразить "множество, которое соответствует этому, за исключением того, что соответствует этому".

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

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

Я хотел бы добавить еще один пример, если вы пытаетесь соответствовать целую строку, содержащую строку X, но также не содержит строку Y.

Например, предположим, что мы хотим проверить, содержит ли наш URL / строка "вкусные угощения", если она также нигде не содержит "шоколад".

Этот шаблон регулярного выражения будет работать (работает и в JavaScript)

^(?=.*?tasty-treats)((?!chocolate).)*$

(глобальные, многострочные флаги в примере)

Интерактивный пример: https://regexr.com/53gv4

совпадения

(Эти URL содержат "вкусняшки", но не "шоколад")

  • example.com/tasty-treats/strawberry-ice-cream
  • example.com/desserts/tasty-treats/banana-pudding
  • example.com/tasty-treats-overview

Не совпадает

(Эти URL-адреса где-то содержат слово "шоколад" - поэтому они не будут совпадать, даже если содержат "вкусняшки")

  • example.com/tasty-treats/chocolate-cake
  • example.com/home-cooking/oven-roasted-chicken
  • example.com/tasty-treats/banana-chocolate-fudge
  • example.com/desserts/chocolate/tasty-treats
  • example.com/chocolate/tasty-treats/desserts

Пока вы имеете дело с линиями, просто отметьте отрицательные совпадения и нацелите остальные.

Фактически, я использую этот трюк с sed, потому что ^((?!hede).)*$ похоже, это не поддерживается.

Для желаемого результата

  1. Отметьте отрицательное совпадение: (например, строки с hede), используя символ, который вообще не вошел во весь текст. Эмодзи, вероятно, может быть хорошим выбором для этой цели.

    s/(.*hede)/\1/g
    
  2. Нацелить остальные (немаркированные строки: например, строки без hede). Предположим, вы хотите оставить только цель и удалить остальные (как хотите):

    s/^.*//g
    

Для лучшего понимания

Предположим, вы хотите удалить цель:

  1. Отметьте отрицательное совпадение: (например, строки с hede), используя символ, который вообще не вошел во весь текст. Эмодзи, вероятно, может быть хорошим выбором для этой цели.

    s/(.*hede)/\1/g
    
  2. Нацелить остальные (немаркированные строки: например, строки без hede). Предположим, вы хотите удалить цель:

    s/^[^].*//g
    
  3. Убрать отметку:

    s///g
    

Функция ниже поможет вам получить желаемый результат

<?PHP
      function removePrepositions($text){

            $propositions=array('/\bfor\b/i','/\bthe\b/i'); 

            if( count($propositions) > 0 ) {
                foreach($propositions as $exceptionPhrase) {
                    $text = preg_replace($exceptionPhrase, '', trim($text));

                }
            $retval = trim($text);

            }
        return $retval;
    }


?>

^((?!hede).)*$ - элегантное решение, за исключением того, что оно использует символы, поэтому вы не сможете комбинировать его с другими критериями. Например, скажем, вы хотели проверить отсутствие "хеде" и наличие "хаха". Это решение будет работать, потому что оно не будет потреблять символы:

^ (?!.\ bhede \ b) (? =.\ bhaha \ b)

Самое простое, что я мог найти, было бы

      [^(hede)]

Проверено на https://regex101.com/

Вы также можете добавить модульные тесты на этот сайт.

Как использовать контрольные глаголы PCRE для отслеживания строки, не содержащей слова

Вот метод, который я раньше не видел:

/.*hede(*COMMIT)^|/

Как это устроено

Сначала он пытается найти "хеде" где-то в очереди. В случае успеха, на данный момент, (*COMMIT) говорит двигателю не только не возвращаться в случае отказа, но и не пытаться выполнить дальнейшее сопоставление в этом случае. Затем мы пытаемся сопоставить что-то, что не может совпадать (в этом случае ^).

Если строка не содержит "hede", тогда вторая альтернатива, пустой подшаблон, успешно соответствует строке темы.

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

Более простое решение - использовать оператор not !

Ваше заявление if должно соответствовать "содержит", а не соответствовать "исключает".

var contains = /abc/;
var excludes =/hede/;

if(string.match(contains) && !(string.match(excludes))){  //proceed...

Я считаю, что дизайнеры RegEx ожидали использования не операторов.

Другие вопросы по тегам