Регулярное выражение, которое никогда не будет соответствовать ничему


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

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

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

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

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

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

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

24 101

24 ответа:

это на самом деле довольно проста, хотя это зависит от реализации / флаги*:

$a

будет соответствовать характеру a после окончания строки. Удача.

предупреждение:
Это выражение дорого - оно будет сканировать всю строку, Найти конец строки якорь, и только тогда не найти a и вернуть отрицательное совпадение. (См. комментарий ниже для получения дополнительной информации деталь.)


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

использовать negative lookahead:

>>> import re
>>> x=r'(?!x)x'
>>> r=re.compile(x)
>>> r.match('')
>>> r.match('x')
>>> r.match('y')

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

Примечание:
В Python, re.match () неявно добавляет якорь начала строки (\A) до начала регулярного выражения. Этот якорь важен для производительности: без него будет сканироваться вся строка. Те, кто не использует Python, захотят добавить якорь явно:

\A(?!x)x

посмотреть вокруг:

(?=a)b

регулярные выражения начинающих: позитивные смотреть вперед (?=a) убедитесь, что следующий символ a, но не изменяет местоположение поиска (или включает 'a' в соответствующую строку). Теперь, что следующий символ подтверждается, чтобы быть a, оставшаяся часть регулярного выражения (b) совпадают, только если следующий символ b. Таким образом, это регулярное выражение соответствует только если персонаж является как a и b в то же время.

a\bc, где \b - это выражение нулевой ширины, которое соответствует границе слова.

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

тот, который был пропущен:

^\b$

он не может совпадать, потому что пустая строка не содержит границы слова. Испытания в Python 2.5.

$.

.^

$.^

(?!)

максимальное согласование

a++a

по крайней мере один a за которым следует любое число a ' s, без отступления. Тогда попробуйте сопоставить еще один a.

или независимое подвыражение

это эквивалентно putting a+ в независимом подвыражении, за которым следует другое a.

(?>a+)a

Perl 5.10 поддерживает специальные управляющие слова, называемые "глаголами", которые заключены в (*...) последовательности. (Сравните с (?...) специальные последовательности.) Среди них она включает (*FAIL) глагол который немедленно возвращается из регулярного выражения.

обратите внимание, что глаголы также реализованы в PCRE вскоре после этого, так что вы можете использовать их в PHP или других языках, используя библиотеку PCRE тоже. (Нельзя же в Python или Ruby. Они используют свой собственный движок.)

\B\b

\b соответствует границам слов-положение между буквой и не буквой (или Строковой границей).
\B является его дополнением-он соответствует позиции между двумя буквами или между не буквами.

вместе они не могут соответствовать любой позиции.

Читайте также:

Это, кажется, работает:

$.

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

Python не примет его, но Perl будет:

perl -ne 'print if /(ww)/'

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

быстрее будет:

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

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

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

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

var noMatch = /^[]/;

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

try {
    var matchPattern = new RegExp(someUserInput);
}
catch (e) {
    matchPattern = noMatch;
}

Я считаю, что

\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, время до отказа должно быть постоянным.

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

используя стандарт линии 275k @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)

в первый раз, когда я запустил это, я забыл raw последние 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

может это?

/$.+^/
(*FAIL)

или

(*F)

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

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

подобно ответу @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. Input:/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)

'[^0-9a-zA-Z...]*'

и заменить ... со всеми печатными символами ;). Это для текстового файла.

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

var willAlwaysFalse=false;
if(willAlwaysFalse)
{
}
else
{
}

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

cat /dev/urandom | hexdump | head -20
0000000 5d5d 3607 40d8 d7ab ce72 aae1 4eb3 ae47
0000010 c5e2 b9e8 910d a2d9 2eb3 fdff 6301 c85f
0000020 35d4 c282 e439 33d8 1c73 ca78 1e4d a569
0000030 8aca eb3c cbe4 aff7 d079 ca38 8831 15a5
0000040 818b 323f 0b02 caec f17f 387b 3995 88da
0000050 7b02 c80b 2d42 8087 9758 f56f b71f 0053
0000060 1501 35c9 0965 2c6e 03fe 7c6d f0ca e547
0000070 aba0 d5b6 c1d9 9bb2 fcd1 5ec7 ee9d 9963
0000080 6f0a 2c91 39c2 3587 c060 faa7 4ea4 1efd
0000090 6738 1a4c 3037 ed28 f62f 20fa 3d57 3cc0
00000a0 34f0 4bc2 3067 a1f7 9a87 086b 2876 1072
00000b0 d9e1 6b8f 5432 a60e f0f5 00b5 d9ef ed6f
00000c0 4a85 70ee 5ec4 a378 7786 927f f126 2ec2
00000d0 18c5 46fe b167 1ae6 c87c 1497 48c9 3c09
00000e0 8d09 e945 13ce 7da2 08af 1a96 c24c c022
00000f0 b051 98b3 2bf5 4d7d 5ec4 e016 a50d 355b
0000100 0e89 d9dd b153 9f0e 9a42 a51f 2d46 2435
0000110 ef35 17c2 d2aa 3cc7 e2c3 e711 d229 f108
0000120 324e 5d6a 650a d151 bc55 963f 41d3 66ee
0000130 1d8c 1fb1 1137 29b2 abf7 3af7 51fe 3cf4

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

new Regex(Guid.NewGuid().ToString())

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