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

StackOverflow https://stackoverflow.com/questions/406230

Вопрос

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

Вход:

hoho
hihi
haha
hede

Код:

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

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

hoho
hihi
haha
Это было полезно?

Решение

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

^((?!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's - пустые строки.регулярное выражение (?!hede). смотрит вперед, чтобы увидеть, нет ли подстроки "hede" быть увиденным, и если это так (и видно что-то еще), то . (точка) будет соответствовать любому символу, кроме разрыва строки.Осмотры еще называют утверждения нулевой ширины потому что они этого не делают потреблять любые персонажи.Они только что-то утверждают/подтверждают.

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

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

Другие советы

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

^(?!hede).*$

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

^((?!hede).)*$

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

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

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

Отвечать:

^((?!hede).)*$

Объяснение:

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

hede твоя струна,

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

Данные ответы совершенно хороши, просто академический момент:

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

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

Это дает только ПОЛНОЕ совпадение.Делать это для дополнительных совпадений было бы еще более неудобно.

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

^(?!hede$).*

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

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

myStr !== 'foo'

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

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

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

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

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

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

В Питоне:

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

The corresponding automaton

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

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 онлайн там.

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

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

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

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

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

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

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

Для получения дополнительной информации о прогнозировании прочтите замечательную статью: Освоение просмотра вперед и назад.

Также, пожалуйста, ознакомьтесь 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()
);

Тесты

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

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

например.найдите файл конфигурации 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

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

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

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

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

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

<?
$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"

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

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

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

Если я хочу сохранить все строки, которые Не содержать строку 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  

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

^(?!.*hede)

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

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

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

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

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

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

или

(?m)^(?!.*hede) # Inline flag

С момента появления Ruby-2.4.1 мы можем использовать новый Отсутствующий оператор в регулярных выражениях 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 и соответствует всем остальным строкам.

ДЕМО

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

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

  1. Часть перед | символ.Часть не должно совпадать.

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

    ^.*$
    

ЧАСТЬ 1

Механизм Regex начнет выполнение с первой части.

^hede$(*SKIP)(*F)

Объяснение:

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

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

ЧАСТЬ 2

^.*$

Объяснение:

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

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

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

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

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

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

grep "Regex for doesn't contain hede" Input

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

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

С GNU grep, это будет что-то вроде:

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

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

Вы также можете использовать инструмент, который реализует Расширенные регулярные выражения, нравиться 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

Обновлять: Недавно я нашел превосходную книгу Кендалла Хопкинса. ФормальнаяТеория библиотека, написанная на 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?)?)?$

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

Возможно, будет удобнее использовать два регулярных выражения в вашем коде: одно для первого совпадения, а затем, если оно соответствует, запустить второе регулярное выражение, чтобы проверить наличие исключений, которые вы хотите, например, заблокировать. ^.*(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

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

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

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


?>

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

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

/.*hede(*COMMIT)^|/

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

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

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

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

Возможно, вы найдете это в 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\">"]

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

Твой если оператор должен будет соответствовать «содержит», а не «исключать».

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

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

Я считаю, что разработчики RegEx предусмотрели использование операторов not.

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

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

^(?!.\bhede\b)(?=.\бхаха\б)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top