Тест: Мастерство регулярных выражений
Сможете приручить дикий RegEx?
Готовы схватиться с регулярными выражениями? 🤼♂️
Проверьте свои знания RegEx с вопросами, охватывающими базовые шаблоны, квантификаторы, группы и эти хитрые look‑around‑утверждения. От простого сопоставления строк до сложной валидации шаблонов — сможете ли вы найти правильное регулярное выражение?
Что совпадает?
'cat CAT Cat'.match(/cat/g)Этот шаблон использует g, но не i:
gищет все совпадения- Без
iсопоставление чувствительно к регистру
Без флага i совпадает только нижний регистр “cat”.
Это особенно полезно при работе с вводом пользователя или HTML, где регистр может различаться.
Что вернёт этот код?
const words = ['cat', 'hat', 'what', 'bat'];words.filter(word => word.match(/^[ch]at/))Шаблон /^[ch]at/ сопоставляет строки, которые:
- Начинаются (
^) с символа ‘c’ или ‘h’ (это значит, что[ch]— класс символов, соответствующий одному символу) - За ними следует буквальное ‘at’
Поэтому только “cat” и “hat” подходят под этот шаблон. Метод filter() оставляет только подходящие элементы.
Что это будет находить?
'<div>Hello</div><div>World</div>'.match(/<div>.*?<\/div>/g)Шаблон /<div>.*?<\/div>/g использует нежадное сопоставление с *?, что означает:
- Совпадение
<div> - Совпадение любого символа (
.*), но как можно меньше (?) - До нахождения
</div> - Флаг
gзаставляет искать все вхождения
Без ? жадный .* захватил бы всё от первого <div> до последнего </div>, получив одно большое совпадение. С ? он сопоставляет каждую пару отдельно.
Что это вернёт?
'hello\nworld'.match(/\w+/g)Шаблон \w+ соответствует одному или более символьным символам слова. Даже если в строке есть перевод строки, \w совпадает с:
- Буквами (a-z, A-Z)
- Цифрами (0-9)
- Подчёркиванием (_)
Таким образом, перевод строки действует как граница слова, и мы получаем два совпадения. Если бы мы использовали .*, он бы по умолчанию не совпадал с переводом строки (нужен флаг s).
Что это будет находить?
'$100 and €50'.match(/\d+(?=[\$€])/g)Эта схема ничего не найдёт, потому что просмотр вперёд направлен назад! Если вам нужны цифры, предшествующие $ или €, используйте просмотр назад: /(?<=[\$€])\d+/g.
Просмотры вперёд проверяют, что идёт после текущей позиции. Записанный шаблон ищет:
- Одна или более цифр (
\d+) - За которыми следует (
(?=...)) либо $ либо € ([\$€])
Поскольку нет чисел, за которыми идут символы валют (они идут перед ними), совпадений нет.
Что будет совпадать?
'cat cats category'.match(/\bcat\b/g)\b обозначает границу слова. Она совпадает:
- Между символом слова и не-символом слова
- В начале или конце строки, если рядом есть символ слова
Поэтому /\bcat\b/ находит “cat” только как отдельное слово, а не как часть другого слова.
- ✅ “cat” (окружено пробелами)
- ❌ “cats” (после “cat” нет границы слова)
- ❌ “category” (после “cat” нет границы слова)
Каков вывод?
'banana'.match(/a/g)Флаг g (global, глобальный) меняет поведение match():
- Без
g: возвращает первое совпадение вместе с группами захвата - С
g: возвращает массив всех найденных строк
В этом случае выражение находит все вхождения “a” в “banana”.
Примечание: если вам нужны и все совпадения, и группы захвата, используйте matchAll() или метод exec() в цикле.
Что соответствует этому шаблону?
'abc123 def456'.match(/(?<!abc)\d+/g)Отрицательный обратный просмотр (?<!abc) гарантирует, что цифры не предшествуют “abc”:
- ❌ “123” (предшествует “abc”)
- ✅ “23” (предшествует “abc1”)
- ✅ “456” (предшествует “def”)
JavaScript поддерживает утверждения обратного просмотра в современных движках. Этот пример использует фиксированную длину обратного просмотра: abc всегда три символа. Обратный просмотр переменной длины — более сложный, зависящий от движка случай.
Примечание: поддержка обратного просмотра в JavaScript относительно недавняя. Проверьте совместимость браузеров, если нужно поддерживать старые браузеры.
Что это вернёт?
'2029-12-31'.match(/(\d{4})-(\d{2})-(\d{2})/).slice(1)Шаблон использует три группы захвата:
(\d{4})захватывает год(\d{2})захватывает месяц(\d{2})захватывает день
match() без флага g возвращает:
- Индекс 0: Полное совпадение
- Индекс 1+: Группы захвата
slice(1) — распространённый приём, чтобы получить только группы захвата.
Каков будет результат?
"123aBc".match(/^\d+(?![a-z])/ig)Отрицательное просмотр вперёд (?![a-z]) гарантирует отсутствие строчных букв после цифр. Поскольку в части “3aBc” после цифр есть строчная буква, эта часть не совпадает. Поэтому совпадает только начало “12”.
Что возвращается?
'a,b,c'.split(/(?<=,)/)Шаблон /(?<=,)/ — это look‑behind, который совпадает после запятой:
a,(после запятой)b,(после запятой)c(нет запятой после)
Look‑behind не поглощает запятую, поэтому запятая остаётся прикреплённой к предыдущему сегменту в результате split.
Это полезно, когда нужно разбить строку по тому, что стоит перед ней не теряя разделительный символ(ы).
Что совпадает?
'$100'.match(/$\d+/)Специальные символы нужно экранировать с помощью \\, чтобы сопоставлять их буквально:
$— специальный символ (конец строки)- Чтобы сопоставить буквальный знак доллара, экранируйте его:
\\$
Общие символы, требующие экранирования:
. * + ? ^ $ [ ] \ ( ) { } |Без экранирования многие специальные символы имеют значения в regex, которые могут не соответствовать вашим ожиданиям.
Что будет найдено?
'$100'.match(/(?<=\$)\d+/)Положительный просмотр назад (?<=\$) гарантирует, что цифры идут после знака доллара:
(?<=\$): просмотр назад для знака доллара\d+: соответствует одной или более цифрам
Утверждения просмотра назад не поглощают символы; они лишь проверяют, что стоит перед ними. Это полезно, когда нужно найти что‑то, опираясь на предшествующий контекст, не включая его в результат.
Что будет найдено?
'<b>bold</b>'.match(/<b>(.*?)<\/b>/).slice(1)Шаблон использует ленивое совпадение с *?:
<b>: Совпадает открывающий тег(.*?): Захватывает любые символы (лениво)</b>: Совпадает закрывающий тег
? после * делает квантификатор ленивым, он захватывает как можно меньше символов.
Без ? он был бы жадным и захватывал бы как можно больше.
slice(1) возвращает только захваченную группу.
Что совпадает?
'😀 🙂'.match(/\p{Emoji}/gu)u‑флаг включает:
- Unicode‑свойства (
\p{...}) - Правильную обработку суррогатных пар
Без u эмодзи и другие Unicode‑символы могут не совпадать корректно.
Шаблон \p{Emoji} соответствует символам с Unicode‑свойством Emoji. В этой строке это два эмодзи‑пиктографа.
Примечание: Unicode‑свойства требуют флага u.
Извиняюсь заранее! 😈
Какой пароль соответствует этому шаблону?
/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!@#$%^&*]).{8,}$/Не пишите ничего подобного в продакшене! 😅
Этот шаблон использует несколько позитивных look-ahead, чтобы обеспечить:
- хотя бы одну заглавную букву:
(?=.*[A-Z]) - хотя бы одну строчную букву:
(?=.*[a-z]) - хотя бы одну цифру:
(?=.*\d) - хотя бы один специальный символ:
(?=.*[!@#$%^&*]) - минимальную длину 8:
.{8,}
Look-ahead идеальны для проверки пароля, потому что они могут проверять несколько условий, не потребляя символы.
Как у вас получилось? 🧐
Регулярные выражения могут быть диким зверем, но они невероятно мощные, как только вы освоите их (и весь новый синтаксис). Продолжайте практиковаться, и вы станете мастером RegEx в кратчайшие сроки! 🧙♂️
Нужен перерыв после всей этой RegEx?
Пфф, помните: перерыв после навыков!
Загляните в мой зал, чтобы решить ещё несколько задач! 💪