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

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

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

hoho
hihi
haha
hede

Код:

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

Желаемый вывод:

hoho
hihi
haha
2.01.2009 07:30:16
Возможно, на пару лет позже, но что не так с ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*:? Идея проста. Продолжайте сопоставление, пока не увидите начало нежелательной строки, затем сопоставляйте только в N-1 случаях, когда строка не завершена (где N - длина строки). Эти случаи N-1: «h, сопровождаемый не-е», «он следует, не-d», и «hed, сопровождаемый не-e». Если вам удалось пропустить эти случаи N-1, вы успешно не сопоставили нежелательную строку, поэтому вы можете начать поиск [^h]*снова
stevendesu 29.09.2011 03:44:32
@stevendesu: попробуйте это как «очень-очень-длинное слово» или даже лучше половину предложения. Весело печатать. Кстати, это почти не читается. Не знаю о влиянии на производительность.
Peter Schuetze 30.01.2012 18:45:04
@PeterSchuetze: Конечно, это не очень красиво для очень длинных слов, но это жизнеспособное и правильное решение. Хотя я не проводил тесты производительности, я бы не подумал, что она слишком медленная, так как большинство последних правил игнорируются, пока вы не увидите h (или первую букву слова, предложения и т. Д.). И вы можете легко сгенерировать строку регулярного выражения для длинных строк, используя итеративную конкатенацию. Если это работает и может генерироваться быстро, важна ли разборчивость? Для этого и нужны комментарии.
stevendesu 2.02.2012 03:14:07
@stevendesu: я даже позже, но этот ответ почти полностью неверен. с одной стороны, он требует, чтобы субъект содержал «h», чего не должно быть, учитывая, что задание «сопоставить строки, которые [не содержат] конкретного слова». давайте предположим, что вы хотели сделать внутреннюю группу необязательной и что шаблон привязан: ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$ это происходит, когда экземплярам "hede" предшествуют частичные экземпляры "hede", такие как "hhede".
jaytea 10.09.2012 10:41:24
Этот вопрос был добавлен в FAQ по регулярному выражению переполнения стека в разделе «Advanced Regex-Fu».
aliteralmind 10.04.2014 01:30:10
29 ОТВЕТОВ
РЕШЕНИЕ

Понятие, что регулярное выражение не поддерживает обратное сопоставление, не совсем верно. Вы можете имитировать это поведение, используя негативные осмотры:

^((?!hede).)*$

Приведенное выше регулярное выражение будет соответствовать любой строке или строке без разрыва строки, не содержащей (под) строку 'hede'. Как уже упоминалось, это не то , что регулярное выражение «хорошо» в (или должны делать), но все же, это возможно.

И если вам нужно сопоставить символы разрыва строки, используйте модификатор DOT-ALL (трейлинг sв следующем шаблоне):

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

или используйте его в строке:

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

(где /.../разделители регулярных выражений, т. е. не являются частью шаблона)

Если модификатор DOT-ALL недоступен, вы можете имитировать то же поведение с классом символов [\s\S]:

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

объяснение

Строка - это просто список nсимволов. До и после каждого символа есть пустая строка. Таким образом, список nсимволов будет иметь n+1пустые строки. Рассмотрим строку "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" впереди!).

5815
8.05.2017 20:35:49
Я бы не сказал, что это плохо для регулярных выражений. Удобство этого решения довольно очевидно, и снижение производительности по сравнению с программным поиском часто оказывается несущественным.
Archimaredes 3.03.2016 16:09:36
Строго говоря, отрицательное ожидание делает регулярное выражение нерегулярным.
Peter K 18.11.2016 15:03:19
@PeterK, конечно, но это ТАК, а не MathOverflow или CS-Stackexchange. Люди, задающие вопрос здесь, обычно ищут практический ответ. Большинство библиотек или инструментов (например grep, которые упоминает OP) с поддержкой регулярных выражений имеют функции, которые делают их нерегулярными в теоретическом смысле.
Bart Kiers 18.11.2016 15:08:29
@ Барт Киерс, не обижайся на тебя, ответь, только это злоупотребление терминологией меня немного раздражает. Действительно запутанная часть в том, что регулярные выражения в строгом смысле слова могут делать то, что хочет OP, но общий язык их написания не позволяет этого, что приводит к (математически некрасивым) обходным путям, таким как упреждающие просмотры. Пожалуйста, смотрите этот ответ ниже и мой комментарий для (теоретически выровненного) правильного способа сделать это. Излишне говорить, что он работает быстрее на больших входах.
Peter K 18.11.2016 15:33:07
Если вы когда-нибудь задумывались, как это сделать в vim:^\(\(hede\)\@!.\)*$
baldrs 24.11.2016 11:58:11

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

ЭТА О, перечитывая вопрос, grep -vвы, вероятно, подразумевали «инструменты».

207
2.01.2009 07:41:22
Совет: для постепенной фильтрации того, что вам не нужно: grep -v "hede" | grep -v "хихи" | ...так далее.
Olivier Lalonde 5.05.2014 22:08:19
Или используя только один процессgrep -v -e hede -e hihi -e ...
Olaf Dietsche 26.04.2015 05:42:15
Или просто grep -v "hede\|hihi":)
Putnik 9.12.2016 15:29:17
Если у вас есть много шаблонов, которые вы хотите отфильтровать, поместите их в файл и используйтеgrep -vf pattern_file file
codeforester 11.03.2018 18:35:45
Или просто egrepили grep -Ev "hede|hihi|etc"чтобы избежать неловкого побега.
Amit Naidu 3.06.2018 10:54:02

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

51
2.01.2009 08:03:06
Некоторые инструменты, в частности mysqldumpslow, предлагают только этот способ фильтрации данных, поэтому в таком случае поиск регулярного выражения для этого является лучшим решением, кроме переписывания инструмента (различные исправления для этого не включены в MySQL AB / Sun). / Oracle
FGM 7.08.2012 12:21:12
Точно аналогично моей ситуации. Шаблонный движок Velocity использует регулярные выражения, чтобы решить, когда применять преобразование (escape html), и я хочу, чтобы оно всегда работало, КРОМЕ в одной ситуации.
Henno Vermeulen 18.10.2013 14:43:00
Какая альтернатива есть? Я никогда не сталкивался с чем-то, что могло бы сделать точное сопоставление строк, кроме регулярных выражений. Если OP использует язык программирования, могут быть доступны другие инструменты, но если он / она использует не пишущий код, другого выбора, вероятно, нет.
kingfrito_5005 20.10.2016 18:32:10
Один из многих негипотетических сценариев, где регулярное выражение - лучший доступный выбор: я нахожусь в IDE (Android Studio), которая показывает вывод журнала, и единственные предоставляемые инструменты фильтрации: простые строки и регулярное выражение. Попытка сделать это с простыми строками будет полным провалом.
LarsH 5.12.2016 16:11:39

Не регулярное выражение, но я нашел логичным и полезным использовать последовательные 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)

32
17.03.2011 20:19:12
Я думаю, что он запрашивает версию регулярного выраженияgrep -v
Angel.King.47 12.07.2011 15:27:15
Это опасно Также пропускает такие строки, какgood_stuff #comment_stuff
Xavi Montero 1.03.2013 19:54:47

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

^(?!hede).*$

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

^((?!hede).)*$

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

733
27.08.2013 16:58:47
Спасибо, я использовал его для проверки того, что строка не содержит последовательность цифр ^ ((?! \ D {5,}).) *
Samih A 10.05.2015 10:42:31
Здравствуйте! Я не могу сочинять , не заканчивается "хеде" регулярное выражение. Вы можете помочь с этим?
Aleks Ya 18.10.2015 21:33:02
@AleksYa: просто используйте версию «Содержать» и включите конечный якорь в строку поиска: измените строку с «не совпадает» с «hede» на «hede $»
Nyerguds 4.05.2016 10:42:45
@AleksYa: не заканчивается версия может быть сделано с помощью отрицательного просмотра назад , как: (.*)(?<!hede)$. Версия @Nyerguds тоже подойдет, но совершенно не учитывает производительность, о которой говорится в ответе.
thisismydesign 14.09.2017 16:53:33
Почему так много ответов ^((?!hede).)*$? Разве это не более эффективно для использования ^(?!.*hede).*$? Он делает то же самое, но в несколько шагов
JackPRead 15.01.2019 10:53:41

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

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

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

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

99
2.09.2011 15:53:15
Важно отметить, что в нем используются только основные регулярные выражения POSIX.2, и поэтому он более переносим, ​​когда PCRE недоступен.
Steve-o 19.02.2014 17:25:48
Согласен. Многие, если не большинство регулярных выражений, не являются регулярными языками и не могут быть распознаны конечными автоматами.
ThomasMcLeod 22.03.2014 21:36:09
@ThomasMcLeod, Hades32: Это в пределах возможного регулярного языка, чтобы быть в состоянии сказать « не » и « и », а также « или » выражения, такого как « (hede|Hihi)»? (Это может быть вопрос для CS.)
James Haigh 13.06.2014 16:54:33
@JohnAllen: Я !!! … Ну, не фактическое регулярное выражение, а академическая справка, которая также тесно связана с вычислительной сложностью; PCRE принципиально не может гарантировать ту же эффективность, что и регулярные выражения POSIX.
James Haigh 13.06.2014 17:04:37
Извините - этот ответ просто не работает, он будет совпадать с хе-хе и даже частично совпадать с хе-хе (вторая половина)
Falco 13.08.2014 12:57:05

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

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

<?
$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"
17
3.04.2014 16:17:20
В регулярном выражении Perl нет «lookcurrent». Это действительно негативный взгляд (префикс (?!). Префикс положительного предпросмотра будет в то (?=время как соответствующие префиксы заглядывания будут (?<!и (?<=соответственно. Взгляд в будущее означает, что вы читаете следующие символы (следовательно, «впереди»), не потребляя их. Взгляд назад означает, что вы проверяете символы, которые уже были использованы.
Didier L 21.05.2012 16:35:40

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

^(?!hede$).*

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

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

myStr !== 'foo'

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

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

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

60
7.11.2018 21:51:32
как насчет конечных пробелов? Например, если я хочу, чтобы тест провалился со строкой " hede "?
eagor 12.05.2017 09:45:27
@eagor \sдиректива соответствует одному пробелу
Roy Tinker 12.05.2017 21:07:42
спасибо, но мне не удалось обновить регулярное выражение, чтобы сделать эту работу.
eagor 13.05.2017 19:22:41
@eagor:^(?!\s*hede\s*$).*
Roy Tinker 15.05.2017 17:33:04

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

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

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

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

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

/^(?>[^h]+|h+(?!ede))*$/
29
4.06.2018 10:00:13
Хорошая точка зрения; Я удивлен, что никто не упомянул этот подход раньше. Тем не менее, это конкретное регулярное выражение склонно к катастрофическому откату назад применительно к тексту, который не соответствует. Вот как бы я это сделал:/^[^h]*(?:h+(?!ede)[^h]*)*$/
Alan Moore 14.04.2013 05:26:42
... или вы можете просто сделать все квантификаторы притяжательными. ;)
Alan Moore 15.04.2013 15:17:58
@ Алан Мур - я тоже удивлен. Я видел ваш комментарий (и лучшее регулярное выражение в куче) здесь только после публикации этого же паттерна в ответе ниже.
ridgerunner 20.12.2013 03:08:40
@ridgerunner, не обязательно быть лучшим. Я видел тесты, где лучший ответ работает лучше. (Я был удивлен этим.)
Qtax 20.02.2014 13:10:10

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

Для меня, иногда мне нужно сделать это при редактировании файла с помощью 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, удалите уникальный «Tag»:

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

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

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

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  
13
26.04.2013 22:46:03

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

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

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

20
20.12.2013 03:03:57

Ответ:

^((?!hede).)*$

Объяснение:

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

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

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

158
6.12.2017 11:23:51
удивительно, что работал для меня в возвышенном тексте 2, используя несколько слов ' ^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$'
Damodar Bashyal 11.08.2015 02:07:31
@DamodarBashyal Я знаю, что я довольно поздно здесь, но вы можете полностью удалить второй срок там, и вы получите точно такие же результаты
forresthopkinsa 12.06.2017 16:19:18

Язык 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

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

5
25.06.2014 01:23:45
Обратите внимание, что это также решение для регулярного выражения ElasticSearch Lucene.
Wiktor Stribiżew 19.02.2018 07:30:27

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

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

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

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

Обратите внимание, что (*?) Ленивый квантификатор в части с отрицательным прогнозом не является обязательным, вы можете вместо этого использовать (*) жадный квантификатор, в зависимости от ваших данных: если '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()
);
47
14.07.2014 18:21:55
так что просто проверить, не содержит ли данная строка str1 и str2:^(?!.*(str1|str2)).*$
S.Serpooshan 1.03.2017 07:20:19
Да, или вы можете использовать ленивый квантификатор:, в ^(?!.*?(?:str1|str2)).*$зависимости от ваших данных. Добавил, ?:так как нам не нужно захватывать его.
amobiz 2.03.2017 09:59:57
Это, безусловно, лучший ответ в 10 раз. Если вы добавили свой код jsfiddle и результаты в ответ, люди могут заметить это. Интересно, почему ленивая версия быстрее жадной версии, когда нет хеде. Разве они не должны занимать одинаковое количество времени?
user5389726598465 23.07.2017 09:06:13
Да, они занимают одинаковое количество времени, поскольку оба они проверяют весь текст.
amobiz 3.08.2017 03:50:51

Ориентиры

Я решил оценить некоторые из представленных опций и сравнить их производительность, а также использовать некоторые новые функции. Сравнительный анализ на .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-решения должны быть еще быстрее и более легко читаемыми.

41
13.08.2014 14:58:27
Вы должны время ^(?!.*hede)тоже. /// Кроме того, вероятно, лучше ранжировать выражения для совпадающего и несовпадающего корпусов по отдельности, потому что это обычно тот случай, когда большинство совпадений строк или большинство строк этого не делают.
ikegami 23.08.2016 00:07:23

Через глагол 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, что возможна также пустая строка на входе. поэтому вы должны использовать .*вместо .+. .+будет повторять предыдущий символ один или несколько раз. Смотрите .*совпадения пустой строкой здесь .

  • $ Привязка конца строки здесь не нужна.

9
9.10.2014 07:51:42

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

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

7
13.09.2016 13:55:32

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

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

Вы сначала определить тип ваших выражений: этикетки письмо ( lal_char) , чтобы выбрать из , aчтобы z, например (определение алфавита при работе с комплементарности, конечно, очень важно), и «значение» вычисляется для каждого слова просто Boolean : 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}  
53
8.01.2018 23:58:21
Правда, но некрасиво и выполнимо только для небольших наборов символов. Вы не хотите делать это со строками Unicode :-)
reinierpost 8.11.2015 23:43:24
Есть больше инструментов, которые позволяют это, одним из самых впечатляющих является Ragel . Там это будет записано как (any * - ('hehe' any *)) для начального совпадения или (any * - ('hehe' any *)) для невыровненного.
Peter K 18.11.2016 15:09:05
@reinierpost: почему это ужасно и в чем проблема с юникодом? Я не могу согласиться с обоими. (У меня нет опыта работы с vcsn, но есть с DFA).
Peter K 18.11.2016 15:39:42
Регулярное выражение ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*не работает для меня, используя egrep. Это соответствует hede. Я также пытался привязать его к началу и концу, и он все еще не работал.
Pedro Gimeno 6.12.2016 23:18:38
@PedroGimeno Когда вы поставили на якорь, вы сначала поставили это регулярное выражение в скобки? В противном случае приоритеты между якорями и |не будут играть хорошо. '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$',
akim 8.12.2016 09:03:41

Вышеупомянутое (?:(?!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
23
23.08.2016 00:10:10

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

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

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

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

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

0
13.09.2016 14:06:10

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

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

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

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

Тем не менее, GNU grepреализует расширения, которые позволяют это. В частности, \|оператор Чередование в реализации проекта GNU в Бре, а \(и \)являются круглые скобки. Если ваш механизм регулярных выражений поддерживает чередование, выражения с отрицательными скобками, круглые скобки и звездочку Клини и может привязывать начало и конец строки, это все, что вам нужно для этого подхода. Однако обратите внимание, что отрицательные наборы [^ ... ]очень удобны в дополнение к этим, потому что в противном случае вам нужно заменить их выражением в форме, в (a|b|c| ... )котором перечислены все символы, которых нет в наборе, что является чрезвычайно утомительным и чрезмерно длинным, особенно если весь набор символов Unicode.

С 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 / разное / неигровые-регулярное выражение /

Для hedeэтого выводит:

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

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

10
22.10.2019 14:07:03

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

<?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;
    }


?>
4
11.03.2017 01:42:05

С момента появления 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"]
11
23.03.2017 13:42:32

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

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

/.*hede(*COMMIT)^|/

Как это работает

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

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

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

1
11.10.2017 10:12:31

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

Учитывая строку: <span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>

Я хочу сопоставить <span>теги, которые не содержат подстроку «плохо».

/<span(?:(?!bad).)*?>будет соответствовать <span class=\"good\">и <span class=\"ugly\">.

Обратите внимание, что есть два набора (слоя) скобок:

  • Самый внутренний - для негативного взгляда (это не группа захвата)
  • Самое внешнее было интерпретировано Ruby как группа захвата, но мы не хотим, чтобы это была группа захвата, поэтому я добавил?: В начале, и он больше не интерпретируется как группа захвата.

Демо в Ruby:

s = '<span class="good">bar</span><span class="bad">foo</span><span class="ugly">baz</span>'
s.scan(/<span(?:(?!bad).)*?>/)
# => ["<span class=\"good\">", "<span class=\"ugly\">"]
1
25.04.2018 18:15:32

С ConyEdit вы можете использовать командную строку, cc.gl !/hede/чтобы получить строки, которые не содержат совпадения с регулярным выражением, или использовать командную строку cc.dl /hede/для удаления строк, которые содержат сопоставление с регулярным выражением. У них одинаковый результат.

0
9.07.2018 17:08:32

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

^(?!.*hede)

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

Конечно, возможно наличие нескольких требований отказа:

^(?!.*(hede|hodo|hada))

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

Якорь ^ в начале предназначен для обозначения начала строки. Инструмент grep сопоставляет каждую строку по одной за раз, в тех случаях, когда вы работаете с многострочной строкой, вы можете использовать флаг "m":

/^(?!.*hede)/m # JavaScript syntax

или

(?m)^(?!.*hede) # Inline flag
14
8.12.2018 20:18:06
Отличный пример с множественным отрицанием.
Peter Parada 11.07.2019 16:50:52
Одно из отличий от ответа на этот вопрос состоит в том, что это ничего не соответствует и соответствует всей строке, если без «хеде»
Z. Khullah 20.08.2019 19:33:04

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

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

2
26.03.2019 12:21:23

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

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

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


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


RegEx Circuit

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

введите описание изображения здесь

4
1.08.2019 02:36:22