Регулярное выражение, которое никогда не сравнится ни с чем - PullRequest
112 голосов
/ 12 ноября 2009

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

Итак, о чем ты думаешь - как выглядит Regex, который никогда не будет совпадать ни с одной строкой!

Редактировать : Почему я этого хочу? Ну, во-первых, потому что мне интересно думать о таком выражении, а во-вторых, потому что он мне нужен для сценария.

В этом сценарии я определяю словарь как Dictionary<string, Regex>. Как вы видите, она содержит строку и выражение.

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

Если выражение соответствует, добавляется другое Dictionary<string, long> значение, возвращаемое выражением. Итак, чтобы перехватить любые сообщения журнала, которые не соответствуют выражению в словаре, я создал новую группу под названием «unknown».

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

Таким образом, у вас есть причина для этого "ненастоящего вопроса" ...

Ответы [ 25 ]

8 голосов
/ 12 ноября 2009

Как насчет $^ или, может быть, (?!)?

4 голосов
/ 04 декабря 2009

Python не примет это, но Perl:

perl -ne 'print if /(w\1w)/'

Это регулярное выражение должно (теоретически) пытаться сопоставить бесконечное (четное) число w с, потому что первая группа (() с) возвращается в себя. Perl, похоже, не выдает никаких предупреждений, даже под use strict; use warnings;, поэтому я предполагаю, что это по крайней мере допустимо, и мое (минимальное) тестирование не соответствует никому, поэтому я отправляю его для вашей критики.

4 голосов
/ 05 декабря 2009

Самый быстрый будет:

r = re.compile(r'a^')
r.match('whatever')

'a' может быть любым не специальным символом ('x', 'y'). Реализация Knio может быть немного более чистой, но эта будет быстрее для всех строк, начинающихся не с того символа, который вы выбрали вместо «a», потому что в этих случаях он не будет совпадать после первого символа, а не после второго.

4 голосов
/ 19 декабря 2009

[^\d\D] или (?=a)b или a$a или a^a

4 голосов
/ 14 августа 2014

Это не будет работать для Python и многих других языков, но в регулярном выражении Javascript [] является допустимым классом символов, который не может быть сопоставлен. Таким образом, следующее, должно произойти сбой немедленно, независимо от того, что ввод:

var noMatch = /^[]/;

Мне нравится это лучше, чем /$a/, потому что для меня это ясно говорит о его намерениях. А что касается того, когда вам это когда-нибудь понадобится, мне это нужно, потому что мне нужен запасной вариант для динамически скомпилированного шаблона, основанного на пользовательском вводе. Когда шаблон недействителен, мне нужно заменить его на шаблон, который ничего не соответствует. Упрощенно это выглядит так:

try {
    var matchPattern = new RegExp(someUserInput);
}
catch (e) {
    matchPattern = noMatch;
}
2 голосов
/ 16 марта 2018

Так много хороших ответов!

Подобно ответу @ nivk, я хотел бы поделиться сравнением производительности для Perl для различных вариантов несоответствующего регулярного выражения.

  1. Ввод: псевдослучайные строки ascii (25 000 различных строк, длина 8-16):

Скорость регулярного выражения:

Total for   \A(?!x)x: 69.675450 s, 1435225 lines/s
Total for       a\bc: 71.164469 s, 1405195 lines/s
Total for    (?>a+)a: 71.218324 s, 1404133 lines/s
Total for       a++a: 71.331362 s, 1401907 lines/s
Total for         $a: 72.567302 s, 1378031 lines/s
Total for     (?=a)b: 72.842308 s, 1372828 lines/s
Total for     (?!x)x: 72.948911 s, 1370822 lines/s
Total for       ^\b$: 79.417197 s, 1259173 lines/s
Total for         $.: 88.727839 s, 1127041 lines/s
Total for       (?!): 111.272815 s, 898692 lines/s
Total for         .^: 115.298849 s, 867311 lines/s
Total for    (*FAIL): 350.409864 s, 285380 lines/s
  1. Ввод: / usr / share / dict / words (100 000 английских слов).

Скорость регулярного выражения:

Total for   \A(?!x)x: 128.336729 s, 1564805 lines/s
Total for     (?!x)x: 132.138544 s, 1519783 lines/s
Total for       a++a: 133.144501 s, 1508301 lines/s
Total for    (?>a+)a: 133.394062 s, 1505479 lines/s
Total for       a\bc: 134.643127 s, 1491513 lines/s
Total for     (?=a)b: 137.877110 s, 1456528 lines/s
Total for         $a: 152.215523 s, 1319326 lines/s
Total for       ^\b$: 153.727954 s, 1306346 lines/s
Total for         $.: 170.780654 s, 1175906 lines/s
Total for       (?!): 209.800379 s, 957205 lines/s
Total for         .^: 217.943800 s, 921439 lines/s
Total for    (*FAIL): 661.598302 s, 303540 lines/s

(Ubuntu на Intel i5-3320M, ядро ​​Linux 4.13, Perl 5.26)

2 голосов
/ 04 ноября 2017

Увидев некоторые из этих замечательных ответов, комментарий @ arantius (относительно времени $x против x^ против (?!x)x) относительно принятого в настоящее время ответа заставил меня задуматься над некоторыми из приведенных решений. пока что.

Используя 275-килобайтный стандарт @ arantius, я запустил следующие тесты в Python (v3.5.2, IPython 6.2.1).

TL; DR: 'x^' и 'x\by' являются самыми быстрыми с коэффициентом не менее ~ 16, и вопреки выводу @ arantius, (?!x)x был среди самых медленных ( ~ В 37 раз медленнее). Таким образом, вопрос скорости, безусловно, зависит от реализации. Протестируйте его на своей системе, прежде чем совершать, если скорость важна для вас.

ОБНОВЛЕНИЕ: По-видимому, существует большое расхождение между временем 'x^' и 'a^'. Пожалуйста, смотрите этот вопрос для получения дополнительной информации и предыдущее редактирование для более медленного времени с a вместо x.

In [1]: import re

In [2]: with open('/tmp/longfile.txt') as f:
   ...:     longfile = f.read()
   ...:     

In [3]: len(re.findall('\n',longfile))
Out[3]: 275000

In [4]: len(longfile)
Out[4]: 24733175

In [5]: for regex in ('x^','.^','$x','$.','$x^','$.^','$^','(?!x)x','(?!)','(?=x)y','(?=x)(?!x)',r'x\by',r'x\bx',r'^\b$'
    ...: ,r'\B\b',r'\ZNEVERMATCH\A',r'\Z\A'):
    ...:     print('-'*72)
    ...:     print(regex)
    ...:     %timeit re.search(regex,longfile)
    ...:     
------------------------------------------------------------------------
x^
6.98 ms ± 58.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
.^
155 ms ± 960 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$x
111 ms ± 2.12 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$.
111 ms ± 1.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$x^
112 ms ± 1.14 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$.^
113 ms ± 1.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
$^
111 ms ± 839 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
(?!x)x
257 ms ± 5.03 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
(?!)
203 ms ± 1.56 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
(?=x)y
204 ms ± 4.84 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
(?=x)(?!x)
210 ms ± 1.66 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
x\by
7.41 ms ± 122 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
x\bx
7.42 ms ± 110 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
------------------------------------------------------------------------
^\b$
108 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
\B\b
387 ms ± 5.77 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
------------------------------------------------------------------------
\ZNEVERMATCH\A
112 ms ± 1.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
------------------------------------------------------------------------
\Z\A
112 ms ± 1.38 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

При первом запуске я забыл r aw с последними 3 выражениями, поэтому '\b' интерпретировалось как '\x08', символ возврата на одну позицию. Однако, к моему удивлению, 'a\x08c' оказался быстрее предыдущего самого быстрого результата! Чтобы быть справедливым, он все равно будет соответствовать этому тексту, но я подумал, что это все же стоит отметить, потому что я не уверен, почему это быстрее.

In [6]: for regex in ('x\by','x\bx','^\b$','\B\b'):
    ...:     print('-'*72)
    ...:     print(regex, repr(regex))
    ...:     %timeit re.search(regex,longfile)
    ...:     print(re.search(regex,longfile))
    ...:     
------------------------------------------------------------------------
y 'x\x08y'
5.32 ms ± 46.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
None
------------------------------------------------------------------------
x 'x\x08x'
5.34 ms ± 66.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
None
------------------------------------------------------------------------
$ '^\x08$'
122 ms ± 1.05 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
None
------------------------------------------------------------------------
\ '\\B\x08'
300 ms ± 4.11 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
None

Мой тестовый файл был создан с использованием формулы для "... читаемое содержимое без дублирующих строк" (в Ubuntu 16.04):

$ ruby -e 'a=STDIN.readlines;275000.times do;b=[];rand(20).times do; b << a[rand(a.size)].chomp end; puts b.join(" "); end' < /usr/share/dict/words > /tmp/longfile.txt

$ head -n5 /tmp/longfile.txt 
unavailable speedometer's garbling Zambia subcontracted fullbacks Belmont mantra's
pizzicatos carotids bitch Hernandez renovate leopard Knuth coarsen
Ramada flu occupies drippings peaces siroccos Bartók upside twiggier configurable perpetuates tapering pint paralyzed
vibraphone stoppered weirdest dispute clergy's getup perusal fork
nighties resurgence chafe
2 голосов
/ 10 декабря 2009

Я верю, что

\Z RE FAILS! \A

охватывает даже случаи, когда регулярное выражение включает флаги, такие как MULTILINE, DOTALL и т. Д.

>>> import re
>>> x=re.compile(r"\Z RE FAILS! \A")
>>> x.match('')
>>> x.match(' RE FAILS! ')
>>>

Я считаю (но я не проверял это), что независимо от длины (> 0) строки между \Z и \A время до отказа должно быть постоянным.

1 голос
/ 10 мая 2019

Пустое регулярное выражение

Лучшее регулярное выражение, которое никогда ничего не соответствует, - пустое регулярное выражение. Но я не уверен, что все движки регулярных выражений примут это.

Невозможное регулярное выражение

Другое решение - создать невозможное регулярное выражение. Я обнаружил, что $-^ занимает всего два шага для вычисления независимо от размера вашего текста (https://regex101.com/r/yjcs1Z/1).

Для справки:

  • $^ и $. выполнить 36 шагов для вычисления -> O (1)
  • \b\B делает 1507 шагов на моем примере и увеличивается с количеством символов в вашей строке -> O (n)

Более популярная тема по этому вопросу:

1 голос
/ 07 июня 2014
(*FAIL)

или

(*F)

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

...