Питання Регулярний вираз, що відповідає лінії, яка не містить слова?


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

Вхід:

hoho
hihi
haha
hede

Код:

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

Бажаний вихід:

hoho
hihi
haha

3567


походження


Напевно, пару років пізно, але що з ним: ([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*? Ідея проста. Зберігайте відповідність, доки ви не побачите початок небажаного рядка, тоді вони співпадають тільки в випадках N-1, де рядок незакінчений (де N - довжина рядка). Ці випадки N-1 "після чого слідують не-е", "він слідував не-D", а "Хед, а потім не-е". Якщо вам вдалося пройти ці випадки N-1, ви успішно не зробив зіставте небажану рядок, щоб ви могли почати шукати [^h]* знову - stevendesu
@stevendesu: спробуйте це за "дуже-дуже-довго слово" або ще краще половину пропозиції. Весело набравшись До речі, це майже нечитабельно. Не знаю про вплив роботи. - Peter Schuetze
@PeterSchuetze: Звичайно, це не досить для дуже довгих слів, але це життєздатне і правильне рішення. Хоча я не запускав тести на продуктивність, я б не думав, що це занадто повільне, оскільки більшість останніх правил ігноруються, поки ви не побачите h (або першу букву слова, пропозиції тощо). І ви можете легко створити рядок регулярних виразів для довгих рядків, використовуючи ітераційну конкатенацію. Якщо це працює і його можна швидко генерувати, чи важливо читати читання? Ось що є для коментарів. - stevendesu
@stevendesu: я ще пізніше, але ця відповідь майже повністю неправильна. з одного боку, він вимагає, щоб предмет містив "h", який йому не треба було б, оскільки завдання полягає в "відповідних рядках, які не містять конкретного слова". давайте припустимо, що ви мали на увазі, щоб зробити внутрішню групу необов'язковою, а шаблон закріплений: ^([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$))?)*$  це не вдається, коли для випадків "hede" передують часткові приклади "hede", наприклад "hhede". - jaytea
Це запитання було додано до Поширені питання регулярного вираження переповнення стека, під заголовком "Advanced Regex-Fu". - aliteralmind


Відповіді:


Поняття, що регулярний вираз не підтримує зворотне узгодження, не цілком справедливий. Ви можете імітувати цю поведінку, використовуючи негативні обрії:

^((?!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" вперед!)


4859



Я б не пішов так далеко, щоб сказати, що це щось регулярне вираження - це погано. Зручність цього рішення досить очевидна, а продуктивність в порівнянні з програмним пошуком часто не має значення. - Archimaredes
Глибоко кажучи, негативний вплив робить вас регулярним виразом не регулярним. - Peter K
@PeterK, звичайно, але це SO, а не MathOverflow або CS-Stackexchange. Люди, які задають тут питання, зазвичай шукають практичну відповідь. Більшість бібліотек або інструментів (наприклад grep, що згадує ОП) з підтримкою регулярної виправлення - все це має функції, які у теоретичному сенсі не є правильними. - Bart Kiers
@ Барт Кієр, вам не слід ображатись, адже це зловживання термінологією трохи дратує мене. Справжня заплутана частка полягає в тому, що регулярні вирази в строгому сенсі дуже багато можуть зробити те, що хоче ОП, але спільна мова для їх написання не дозволяє це, що призводить до (математично потворних) шляхів вирішення спорів, таких як перспективи. Будь ласка, дивіться ця відповідь нижче, і мій коментар там (теоретично вирівняний) правильний спосіб це зробити. Зрозуміло, що він працює швидше на великих входах. - Peter K
Якщо ви коли-небудь замислювалися про те, як це зробити в vim: ^\(\(hede\)\@!.\)*$ - baldrs


Зауважте, що рішення для не починати з "Хед":

^(?!hede).*$

зазвичай набагато ефективніше, ніж рішення не містити "Хед":

^((?!hede).)*$

Перша перевірка на "hede" лише на першій позиції вхідного рядка, а не на кожну позицію.


606



Спасибі, я використовував це для перевірки того, що рядок dosn't містити розкид цифр ^ ((?! \ D {5,}).) * - Samih A
^((?!hede).)*$ працював для мене за допомогою плагіна jQuery DataTable, щоб виключити рядок з набору даних - Alex
Привіт! Я не можу скласти не кінець з "хед" регулярний вираз Чи можете ви допомогти з цим? - Aleks Ya
@AleksYa: просто використовуйте версію "contain" та включіть кінцевий якоря в рядок пошуку: змініть рядок на "не збігається" з "hede" на "hede $" - Nyerguds
@AleksYa: версію, що не закінчується, може бути зроблена з використанням негативного вигляду: (.*)(?<!hede)$. Версія @Nyerguds буде працювати також, але повністю не відповідає точці виконання, про яку йде мова. - thisismydesign


Якщо ви просто використовуєте його для grep, ви можете використовувати grep -v hede щоб отримати всі лінії, які не містять хед.

ETA О, передивляючи питання, grep -v це, мабуть, те, що ви мали на увазі під «параметрами інструментів».


165



Порада: для поступового відфільтрування того, чого ти не хоче: grep -v "hede" | grep -v "hihi" | ... і т. д. - Olivier Lalonde
Або використовуючи лише один процес grep -v -e hede -e hihi -e ... - Olaf Dietsche
Або просто grep -v "hede\|hihi" :) - Putnik
Якщо у вас багато візерунків, які потрібно відфільтрувати, помістіть їх у файл і використовуйте grep -vf pattern_file file - codeforester
Або просто egrep або grep -Ev "hede|hihi|etc" щоб уникнути незграбного втечі. - Amit Naidu


Відповідь:

^((?!hede).)*$

Пояснення:

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

hedeтвій рядок

) кінець очікування . будь-який символ, окрім \ n
)* кінець \ 1 (Примітка: оскільки ви використовуєте квантор у цьому захопленні, в \ 1 буде збережено лише останнє повторення захопленого шаблону)
$ перед необов'язковим \ n і кінцем рядка


122



дивовижний, який працював для мене в піднесеному тексті 2, використовуючи кілька слів "^((?!DSAU_PW8882WEB2|DSAU_PW8884WEB2|DSAU_PW8884WEB).)*$' - Damodar Bashyal
@DamodarBashyal Я знаю, що я тут досить пізно, але ви можете повністю вилучити другий термін, і ви отримаєте точні результати. - forresthopkinsa


Дані відповіді - це чудово, просто академічна точка:

Регулярні вирази в значенні теоретичних комп'ютерних наук НЕ ГРОБИ роби це як це. Для них це повинно виглядати приблизно так:

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

Це робить лише FULL матчу. Робити це для підгруп буде навіть незручно.


90



Важливо відзначити, що це використовує лише основні регулярні вирази POSIX.2, і, таким чином, доки крапка більш портативна, коли PCRE недоступний. - Steve-o
Я згоден. Багато, якщо не більшість регулярних виразів, не є звичайними мовами і не можуть бути визнані кінцевими автоматами. - ThomasMcLeod
@ThomasMcLeod, Hades32: Це в рамках будь-якої можливої ​​звичайної мови, щоб бути в змозі сказати "ні"І"іА також "або'Такого виразу, як'(hede|Hihi)'? (Це, можливо, питання для КС.) - James Haigh
@ JohnAllen: Мені !!! ... Ну, а не фактичний регулярний вираз, але академічна довідка, яка також тісно пов'язана з обчислювальною складністю; PCRE принципово не може гарантувати таку ж ефективність як регулярні вирази POSIX. - James Haigh
Вибачте - цей відповідь просто не працює, він буде відповідати його і навіть матче хай частково (друга половина) - Falco


Якщо ви хочете, щоб тест регулярного виразу був тільки Помилка, якщо цілий рядок матчі, працюватиме наступне:

^(?!hede$).*

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

Звичайно, якщо ви перевіряєте точно краще загальне рішення в даному випадку полягає в тому, щоб перевірити рівність строк, тобто

myStr !== 'foo'

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

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

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


49



а про кінцевий пробіл? Наприклад, якщо я хочу провести тест на провал з рядком " hede "? - eagor
@еаґор \s Директива відповідає одному символу пробілу - Roy Tinker
дякую, але мені не вдалося оновити регулярний вираз, щоб зробити цю роботу. - eagor
@ eagor: ^(?!\s*hede\s*$).* - Roy Tinker


Ось гарне пояснення чому нелегко відмовитись від довільного регулярного виразу. Проте я повинен погодитися з іншими відповідями, хоча: якщо це не що інше, як гіпотетичне питання, то регулярний вираз не є правильним вибором тут.


48



Деякі інструменти, і зокрема mysqldumpslow, пропонують лише такий спосіб фільтрувати дані, тому в такому випадку знайти регулярний вираз, щоб зробити це, є найкращим рішенням, крім перезапису інструмента (різні патчі для цього не були включені MySQL AB / Sun / Oracle. - FGM
Точно аналогічно моїй ситуації. Шаблон швидкості двигуна використовує регулярні вирази, щоб вирішити, коли застосувати перетворення (Escape html), і я хочу, щоб він завжди працював, за винятком ситуації. - Henno Vermeulen
Яка альтернатива там? Я ніколи не зустрічав нічого, що могло б зробити точне узгодження рядків крім регулярного виразу. Якщо OP використовує мову програмування, існують інші інструменти, але якщо він / вона не використовує кодування, можливо, не існує іншого вибору. - kingfrito_5005
Один з багатьох не гіпотетичних сценаріїв, де регулярний вираз найкращий доступний вибір: я в IDE (Android Studio), що показує вихід журналу, і єдиними інструментами фільтрування є: прості рядки та регулярні вирази. Спроба зробити це звичайними рядками буде повним невдачею. - LarsH


FWIW, оскільки регулярні мови (окрім раціональних мов) закриті під час доповнення, завжди можна знайти регулярне вираження (ака раціональне вираження), яке заперечує інший вираз. Але це не так багато інструментів.

Vcsn підтримує цей оператор (який він позначає {c}, postfix)

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

У Python:

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

тоді ви вводите свій вираз:

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

перетворити це вираз в автомат:

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

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 в Інтернеті там.


44



Правда, але потворно, і лише для маленьких наборів символів. Ви не хочете робити це за допомогою рядків Юнікоду :-) - reinierpost
Існує більше інструментів, які дозволяють це, одне з найбільш вражаючих істот Рагель. Там він буде записаний як (будь-який * - ('hehe' будь-який *) для початкового вирівнювання матчу або (будь-який * - ('hehe' будь-який *) для unaligned. - Peter K
@reinierpost: чому це некрасиво і яка проблема з Unicode? Я не можу погодитися на обидва. (Я не маю досвіду з vcsn, але маю з DFA). - Peter K
Регулярний вираз ()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).* не працював для мене egrep. Він збігається hede. Я також намагався закріпити його на початку і в кінці, і це все одно не спрацювало. - Pedro Gimeno
@PedroGimeno Коли ви закріпили, перш за все ви вставили цей регулярний вираз в parens? В іншому випадку передує між якорями і | не буде добре грати. '^(()|h(ed?)?|([^h]|h([^e]|e([^d]|d([^e]|e.)))).*)$'. - akim