#regex #repeat
#регулярное выражение #повторять
Вопрос:
Я хочу, чтобы оно соответствовало строке из 2-15 символов, содержащей только заглавные / строчные буквы и цифры; дефисы разрешены только между ними.
/^[a-z0-9][a-z0-9-]{0,13}[a-z0-9]$/i
Например: a-b-c
ab
a-b
a-bcdef-g-h-i
Как мне убедиться, что никакие два дефиса не отображаются подряд?
Ответ №1:
Вы могли бы использовать отрицательное прогнозное утверждение:
/^(?!.*--)[a-z0-9][a-z0-9-]{0,13}[a-z0-9]$/i
(?!.*--)
гарантирует невозможность сопоставления --
в любом месте строки (без фактического использования каких-либо символов в сопоставлении).
Кроме того, нет необходимости экранировать тире, если это первый или последний символ в классе символов.
Если вы не заинтересованы в прогнозах с неопределенными кванторами (например, Donal Fellows), другим способом было бы
/^[a-z0-9](?:[a-z0-9]|-(?!-)){0,13}[a-z0-9]$/i
(?:[a-z0-9]|-(?!-)){0,13}
соответствует либо буквенно-цифровому символу, либо тире, если за ним не следует другое тире, повторяется до 13 раз.
Что касается производительности (проверено в Python 3.2.2):
>>> import timeit
>>> timeit.timeit(stmt='r.match("a--bcdefghijklmop-qrstuvwxyz")',
... setup='import re; r=re.compile(r"^(?!.*--)[a-z0-9][a-z0-9-]{0,13}[a-z0-9]$")')
0.699529247317531
>>> timeit.timeit(stmt='r.match("a--bcdefghijklmop-qrstuvwxyz")',
... setup='import re; r=re.compile(r"^[a-z0-9](?:[a-z0-9]|-(?!-)){0,13}[a-z0-9]$")')
0.6518945164968741
>>> timeit.timeit(stmt='r.match("a-bcdefghijklmop-qrstuvwxy--z")',
... setup='import re; r=re.compile(r"^(?!.*--)[a-z0-9][a-z0-9-]{0,13}[a-z0-9]$")')
0.5857406334929749
>>> timeit.timeit(stmt='r.match("a-bcdefghijklmop-qrstuvwxy--z")',
... setup='import re; r=re.compile(r"^[a-z0-9](?:[a-z0-9]|-(?!-)){0,13}[a-z0-9]$")')
2.2273210211646415
Таким образом, (?!.*--)
в худшем случае это происходит немного медленнее ( --
в начале строки, поэтому много возвратов), но в лучшем случае это в четыре раза быстрее ( --
в конце строки, поэтому возврата почти нет).
Комментарии:
1. Я не очень заинтересован в использовании отрицательных прогнозных утверждений с
*
квантификатором внутри.2. @DonalFellows: Могу я (возможно, наивно) спросить, почему?
3. @ean: Я подозреваю, что они дорогие, и они определенно не соответствуют тому, как я думаю. Мне нравится, чтобы в моих утверждениях было очень строго ограниченное количество символов, которые необходимо искать.
4. @DonalFellows: Звучит разумно. В этом случае, не
*
можно было бы опустить и все еще иметь желаемый эффект?5. @ean5533: В этом случае вы не можете опустить . * потому что «—» может встречаться в любом месте строки длиной 15.