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

вопрос создан в Wed, May 29, 2019 12:00 AM

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

Входной сигнал:

hoho
hihi
haha
hede

Код:

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

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

hoho
hihi
haha
    
3973
  1. Возможно, на пару лет позже, но что не так с: ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*? Идея проста. Продолжайте сопоставление, пока не увидите начало нежелательной строки, затем сопоставляйте только в N-1 случаях, когда строка не завершена (где N - длина строки). Эти случаи N-1: «h, сопровождаемый не-е», «он следует, не-d», и «hed, сопровождаемый не-e». Если вам удалось пройти эти случаи N-1, вы успешно не сопоставили нежелательную строку, поэтому вы можете снова начать поиск [^h]*
    2011-09-29 03: 44: 32Z
  2. @ stevendesu: попробуйте это для "очень-очень-длинного слова" или, что еще лучше, для половины предложения. Весело печатать. Кстати, это почти не читается. Не знаю о влиянии на производительность.
    2012-01-30 18: 45: 04Z
  3. @ PeterSchuetze: Конечно, это не очень красиво для очень длинных слов, но это жизнеспособное и правильное решение. Хотя я не проводил тесты производительности, я бы не подумал, что она слишком медленная, так как большинство последних правил игнорируются до тех пор, пока вы не увидите h (или первую букву слова, предложения и т. Д.). И вы можете легко сгенерировать строку регулярного выражения для длинных строк, используя итеративную конкатенацию. Если это работает и может генерироваться быстро, важна ли разборчивость? Для этого и нужны комментарии.
    2012-02-02 03: 14: 07Z
  4. @ stevendesu: я еще позже, но этот ответ почти полностью неверен. во-первых, он требует, чтобы субъект содержал «h», чего не должно быть, учитывая, что задание «сопоставить строки, которые [не содержат] конкретного слова». давайте предположим, что вы хотели сделать внутреннюю группу необязательной и что шаблон привязан: ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$ это не удается, когда экземплярам "hede" предшествуют частичные экземпляры "hede", например, в "hhede".
    2012-09-10 10: 41: 24Z
  5. Этот вопрос был добавлен в Часто задаваемые вопросы о регулярных выражениях переполнения стека в разделе «Продвинутый Regex-Fu».
    2014-04-10 01: 30: 10Z
28 ответов                              28                         

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

^((?!hede).)*$

Приведенное выше регулярное выражение будет соответствовать любой строке или строке без разрыва строки, not , содержащей (под) строку '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" впереди!).

    
5397
2017-05-08 20: 35: 49Z
  1. Я бы не сказал, что это плохо для регулярных выражений. Удобство этого решения довольно очевидно, и снижение производительности по сравнению с программным поиском часто оказывается несущественным.
    2016-03-03 16: 09: 36Z
  2. Строго говоря, отрицательная обратная связь делает регулярное выражение нерегулярным.
    2016-11-18 15: 03: 19Z
  3. @ PeterK, конечно, но это SO, а не MathOverflow или CS-Stackexchange. Люди, задающие вопрос здесь, обычно ищут практический ответ. Большинство библиотек или инструментов (например, grep, о которых упоминает OP) с поддержкой регулярных выражений имеют функции, которые делают их нерегулярными в теоретическом смысле.
    2016-11-18 15: 08: 29Z
  4. @ Барт Киерс, не обижайся на тебя, отвечай, только это злоупотребление терминологией меня немного раздражает. Действительно запутанная часть в том, что регулярные выражения в строгом смысле слова могут делать то, что хочет OP, но общий язык их написания этого не позволяет, что приводит к (математически некрасивым) обходным путям, таким как упреждающие просмотры. Пожалуйста, посмотрите этот ответ ниже и мой комментарий для (теоретически выровненного) правильного способа сделать это. Излишне говорить, что он работает быстрее на больших входах.
    2016-11-18 15: 33: 07Z
  5. Если вы когда-нибудь задумывались, как это сделать в vim: ^\(\(hede\)\@!.\)*$
    2016-11-24 11: 58: 11Z

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

^(?!hede).*$

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

^((?!hede).)*$

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

    
677
2013-08-27 16: 58: 47Z
  1. Спасибо, я использовал его для проверки того, что строка не содержит последовательность цифр ^ ((?! \d {5,}).) *
    2015-05-10 10: 42: 31Z
  2. Здравствуйте! Я не могу сочинить не заканчивается регулярным выражением "hede" . Вы можете помочь с этим?
    2015-10-18 21: 33: 02Z
  3. @ AleksYa: просто используйте версию "includes" и включите конечный якорь в поисковикch string: изменить строку на "not match" с "hede" на "hede $"
    2016-05-04 10: 42: 45Z
  4. @ AleksYa: не конечная версия может быть выполнена с использованием отрицательного lookbehind как: (.*)(?<!hede)$. Версия @Nyerguds также будет работать, но полностью упускает точку в производительности, ответ упоминает.
    2017-09-14 16: 53: 33Z
  5. Почему так много ответов говорят ^((?!hede).)*$? Не эффективнее ли использовать ^(?!.*hede).*$? Он делает то же самое, но за меньшее количество шагов
    2019-01-15 10: 53: 41Z

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

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

    
190
2009-01-02 07: 41: 22Z
  1. Совет: для постепенной фильтрации того, что вам не нужно: grep -v "hede" | grep -v "хихи" | ... и т.д.
    2014-05-05 22: 08: 19Z
  2. Или используя только один процесс grep -v -e hede -e hihi -e ...
    2015-04-26 05: 42: 15Z
  3. Или просто grep -v "hede\|hihi":)
    2016-12-09 15: 29: 17Z
  4. Если у вас есть много шаблонов, которые вы хотите отфильтровать, поместите их в файл и используйте grep -vf pattern_file file
    2018-03-11 18: 35: 45Z
  5. Или просто egrep или grep -Ev "hede|hihi|etc", чтобы избежать неудобного побега.
    2018-06-03 10: 54: 02Z

Ответ:

^((?!hede).)*$

Объяснение:

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

hede вашей строки,

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

    
141
2017-12-06 11: 23: 51Z
  1. здорово, что сработало для меня в возвышенном тексте 2, используя несколько слов «^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$»
    2015-08-11 02: 07: 31Z
  2. @ DamodarBashyal Я знаю, что я довольно поздно здесь, но вы можете полностью удалить второй член там, и вы получите точно такие же результаты
    2017-06-12 16: 19: 18Z

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

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

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

Это только ПОЛНОЕ совпадение. Делать это для под-матчей woТы даже будешь более неловким.

    
96
2011-09-02 15: 53: 15Z
  1. Важно отметить, что здесь используются только базовые регулярные выражения POSIX.2, и, следовательно, краткость более переносима, когда PCRE недоступен.
    2014-02-19 17: 25: 48Z
  2. Я согласен. Многие, если не большинство регулярных выражений, не являются регулярными языками и не могут быть распознаны конечными автоматами.
    2014-03-22 21: 36: 09Z
  3. @ ThomasMcLeod, Hades32: В пределах любого возможного регулярного языка можно сказать « not » и « и ', а также' или 'выражения, такого как' (hede|Hihi) '? (Это может быть вопрос для CS.)
    2014-06-13 16: 54: 33Z
  4. @ JohnAllen: ME !!! ... Ну, не фактическое регулярное выражение, а академическая справка, которая также тесно связана с вычислительной сложностью; PCRE принципиально не может гарантировать ту же эффективность, что и регулярные выражения POSIX.
    2014-06-13 17: 04: 37Z
  5. Извините, этот ответ просто не работает, он будет совпадать с хе-хе и даже частично совпадать с хе-хе (вторая половина)
    2014-08-13 12: 57: 05Z

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

^(?!hede$).*

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

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

myStr !== 'foo'

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

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

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

    
54
2018-11-07 21: 51: 32Z
  1. как насчет конечных пробелов? Например, если я хочу выполнить тест со строкой " hede "?
    2017-05-12 09: 45: 27Z
  2. @ eagor директива \s соответствует одному пробелу
    2017-05-12 21: 07: 42Z
  3. спасибо, но мне не удалось обновить регулярное выражение, чтобы сделать эту работу.
    2017-05-13 19: 22: 41Z
  4. @ eagor: ^(?!\s*hede\s*$).*
    2017-05-15 17: 33: 04Z

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

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

Сначала вы определяете тип своих выражений: например, метки - это буквы (lal_char), которые можно выбирать в диапазоне от a до z (определение алфавита при работе с дополнением, конечно, очень важно для импортаant), и значение, вычисленное для каждого слова, является просто логическим: 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 онлайн там .

    
53
2018-01-08 23: 58: 21Z
  1. Верно, но некрасиво и выполнимо только для небольших наборов символов. Вы не хотите делать это со строками Unicode: -)
    2015-11-08 23: 43: 24Z
  2. Есть больше инструментов, которые позволяют это, одним из самых впечатляющих является Ragel . Там это будет записано как (any * - ('hehe' any *)) для начального совпадения или (any * - ('hehe' any *)) для невыровненного.
    2016-11-18 15: 09: 05Z
  3. @ reinierpost: почему это некрасиво и в чем проблема с юникодом? Я не могу согласиться с обоими. (У меня нет опыта работы с vcsn, но есть опыт работы с DFA).
    2016-11-18 15: 39: 42Z
  4. Регулярное выражение ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).* не работает для меня с использованием egrep. Это соответствует hede. Я также пытался привязать его к началу и концу, но он все еще не работал.
    2016-12-06 23: 18: 38Z
  5. @ PedroGimeno Когда вы поставили на якорь, вы обязательно сначала поместили это регулярное выражение в скобки? В противном случае приоритеты между якорями и | не будут играть хорошо. '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'.
    2016-12-08 09: 03: 41Z

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

    
50
2009-01-02 08: 03: 06Z
  1. Некоторые инструменты, в частности mysqldumpslow, предлагают только этот способ фильтрации данных, поэтому в таком случае поиск регулярного выражения для этого является лучшим решением, кроме переписывания инструмент (различные исправления для этого не включены в MySQL AB /Sun /Oracle.
    2012-08-07 12: 21: 12Z
  2. Точно аналогично моей ситуации. Шаблонный движок Velocity использует регулярные выражения, чтобы решить, когда применять преобразование (escape html), и я хочу, чтобы оно всегда работало EXCEPT в одной ситуации.
    2013-10-18 14: 43: 00Z
  3. Какая альтернатива есть? Я никогда не сталкивался с чем-то, что могло бы сделать точное сопоставление строк, кроме регулярных выражений. Если OP использует язык программирования, могут быть доступны другие инструменты, но если он /она использует не код, то, вероятно, нетдругой выбор.
    2016-10-20 18: 32: 10Z
  4. Один из многих негипотетических сценариев, где регулярное выражение является наилучшим доступным выбором: я нахожусь в IDE (Android Studio), которая показывает вывод журнала и единственную фильтрацию предоставленные инструменты: простые строки и регулярные выражения. Попытка сделать это с простыми строками будет полным провалом.
    2016-12-05 16: 11: 39Z

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

Тем не менее, с ответом Барта Киерса, опережающая часть будет тестировать от 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()
);
    
42
2014-07-14 18: 21: 55Z
  1. так, чтобы просто проверить, не содержит ли данная строка str1 и str2: ^(?!.*(str1|str2)).*$
    2017-03-01 07: 20: 19Z
  2. Да, или вы можете использовать ленивый квантификатор: ^(?!.*?(?:str1|str2)).*$ в зависимости от ваших данных. Добавлен ?:, поскольку нам не нужно его захватывать.
    2017-03-02 09: 59: 57Z
  3. Это, безусловно, лучший ответ с коэффициентом 10xms. Если вы добавили свой код jsfiddle и результаты в ответ, люди могут заметить это. Интересно, почему ленивая версия быстрее жадной версии, когда нет хеде. Разве они не должны занимать одинаковое количество времени?
    2017-07-23 09: 06: 13Z
  4. Да, они занимают одинаковое количество времени, поскольку оба проверяют весь текст.
    2017-08-03 03: 50: 51Z

Бенчмарки

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

Результаты:

Результаты - это число итераций в секунду, при этом медиана трех запусков - Большое число = Лучше

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
2014-08-13 14: 58: 27Z
  1. Вы должны также время ^(?!.*hede). ///Кроме того, вероятно, лучше ранжировать выражения для совпадающего и несовпадающего корпусов по отдельности, потому что это обычно тот случай, когда большинство совпадений строк или большинство строк этого не делают.
    2016-08-23 00: 07: 23Z

Не regex, но я нашел логичным и полезным использовать последовательные 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
2011-03-17 20: 19: 12Z
  1. Я думаю, что он запрашивает версию регулярного выражения grep -v
    2011-07-12 15: 27: 15Z
  2. Это опасно. Также пропускает такие строки, как good_stuff #comment_stuff
    2013-03-01 19: 54: 47Z

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

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

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

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

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

/^(?>[^h]+|h+(?!ede))*$/
    
29
2018-06-04 10: 00: 13Z
  1. Хороший вопрос; Я удивлен, что никто не упомянул этот подход раньше. Однако это конкретное регулярное выражение склонно к катастрофическому откату применительно к тексту, который не матч. Вот как бы я это сделал: /^[^h]*(?:h+(?!ede)[^h]*)*$/
    2013-04-14 05: 26: 42Z
  2. ... или вы можете просто сделать все квантификаторы притяжательными. ;)
    2013-04-15 15: 17: 58Z
  3. @ Алан Мур - я тоже удивлен. Я увидел твой комментарий (и лучшее из регулярных выражений в этой куче) здесь только после публикации того же шаблона в ответе ниже.
    2013-12-20 03: 08: 40Z
  4. @ ridgerunner, не обязательно должен быть лучшим. Я видел тесты, где лучший ответ работает лучше. (Я был удивлен этим.)
    2014-02-20 13: 10: 10Z

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

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

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

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

    
19
2013-12-20 03: 03: 57Z

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

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

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

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

Мне иногда приходится делать это при редактировании файла с использованием Textpad .

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

Если я хочу сохранить все строки, которые НЕ содержат строку 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. Когда я закончу, я удаляю уникальный «тег» из всех строк (строка замены пуста):

р>

    Search string:<@#-unique-#@>
    Replace string:<nothing>  
    Replace-all  
    
13
2013-04-26 22: 46: 03Z
  1. хаха - я использовал замену всего, это простой трюк.
    2018-11-12 02: 50: 28Z

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

от официального doc

(?~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"]
    
10
2017-03-23 ​​13: 42: 32Z

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

ЧАСТЬ 2

^.*$

Объяснение:

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

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

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

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

9
2014-10-09 07: 51: 42Z

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

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

grep "Regex for doesn't contain hede" Input

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

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

В 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?)?)?$

, что эквивалентно приведенному выше.

    
9
2018-05-25 00: 03: 23Z

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

^(?!.*hede)

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

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

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

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

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

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

или р>

(?m)^(?!.*hede) # Inline flag
    
9
2018-12-08 20: 18: 06Z

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

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

    
7
2016-09-13 13: 55: 32Z

Язык 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
2014-06-25 01: 23: 45Z
  1. Обратите внимание, что это также решение для регулярного выражения на основе ElasticSearch Lucene.
    2018-02-19 07: 30: 27Z

Следующая функция поможет вам получить желаемый результат

<?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
2017-03-11 01: 42: 05Z

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

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

/.*hede(*COMMIT)^|/

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

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

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

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

    
1
2017-10-11 10: 12: 31Z

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

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

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

/<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
2018-04-25 18: 15: 32Z

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

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

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

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

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

    
0
2016-09-13 14: 06: 10Z

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

    
0
2018-07-09 17: 08: 32Z
ДIV>

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

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

    
0
2019-03-26 12: 21: 23Z
источник размещен Вот