[regex] Памятка по regex

Онлайн песочница: regex101.

Квантификаторы

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

  • a|b - или a, или b
  • ? - ноль или один
  • + - один или больше
  • * - ноль или больше
  • {N} - ровно N раз (здесь N - число)
  • {N,} - N или больше раз (N - число)
  • {N,M} - от N до M раз (N и M - числа, при этом N < M)
  • *? - ноль или больше, но после первого совпадения поиск нужно прекратить

Например, следующее регулярное выражение соответствует и строке «Hello», и строке «Goodbye»:

Hello|Goodbye

В то время как

Hey?

Может означать как отсутствие y, так и одно вхождение y, и таким образом весь шаблон может соответствовать и «He», и «Hey».

Еще пример:

Hello{1,3}

Этот шаблон соответствует «Hello», «Hellooo», но не «Helloooo», потому что буква «о» может встречаться от 1 до 3 раз.

Квантификаторы можно комбинировать:

He?llo{2}

Здесь мы ищем строки, в которых «e» нет или встречается 1 раз, а «o» встречается ровно 2 раза. Таким образом, этот шаблон соответствует словам «Helloo» и «Hlloo».

Наборы

Квадратные скобки позволяют искать совпадение по целому набору символов, указанному в скобках. Например, шаблон

My favorite vowel is [aeiou]

Совпадет со строками:

My favorite vowel is a
My favorite vowel is e
My favorite vowel is i
My favorite vowel is o
My favorite vowel is u

И ни с чем другим. [aeiou] - это набор, в регулярном выражении означающий «любой из указанных символов».

Вот список самых распространенных наборов:

  • [A-Z] - совпадает с любой буквой в верхнем регистре, от «A» до «Z»
  • [a-z] - совпадает с любой буквой в нижнем регистре, от «a» до «z»
  • [0-9] -любая цифра
  • [asdf] - совпадает с «a», «s», «d» или «f»
  • [^asdf] - совпадает с любым символом кроме «a», «s», «d» или «f»

Эти наборы можно комбинировать:

  • [0-9A-Z] - любой символ, являющийся либо цифрой, либо буквой от A до Z
  • [^a-z] - любой символ, не являющийся буквой латинского алфавита в нижнем регистре

Символьные классы

Не каждый символ можно так легко идентифицировать. Скажем, найти буквы с использованием regex легко, а как насчет символа новой строки?

Примечание. Символ новой строки - это символ, который вы вводите, когда нажимаете Enter и переходите на новую строку.

  • . - любой символ
  • \n - символ новой строки
  • \t - символ табуляции
  • \s - пробельный символ (включая \t, \n и некоторые другие)
  • \S - не-пробельный символ
  • \w - любой «словообразующий» символ (буквы латинского алфавита в верхнем и нижнем регистре, цифры 0-9 и символ подчеркивания _)
  • \W - любой «несловообразующий» символ (класс символов, обратный классу \w)
  • \b - граница слова, разделяет \w и \W, т. е. словообразующие и несловообразующие символы. Граница слова соответствует позиции, где за символом слова не следует другой символ слова.
  • \B - несловообразующая граница (класс, обратный \b). Несловообразующая граница соответствует позиции, в которой предыдущий и следующий символы являются символами одного типа: либо оба должны быть словообразующими символами, либо несловообразующими. Начало и конец строки считаются несловообразующими символами.
  • ^ - начало строки
  • $ - конец строки
  • \\ - символ «\» в буквальном значении

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

\s.

А затем найденные символы можно заменить пустой строкой. Таким образом строка

Hello world how are you

Превратится в

Helloorldowreou

Комбинирование наборов

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

[A-Z]|\s

Но \s можно поместить и внутрь набора:

[A-Z\s]

Границы слова

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

Допустим, у вас есть строка «This is a string». Вы можете предположить, что символ границы слова соответствует пробелам между словами, но это не так. Он соответствует тому, что находится между буквой и пробелом.

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

\b\w+\b

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

Начало и конец строки

Еще два важных токена - ^ и $. Они означают начало и конец строки соответственно.

То есть, если вы хотите найти первое слово в строке, вы можете написать следующее выражение:

^\w+

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

Аналогично, если вы хотите найти последнее слово в строке, ваше регулярное выражение может выглядеть так:

\w+$

Но то, что символ $ обычно заканчивает строку, не означает, что после него не может идти других символов.

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

Мы можем написать следующее выражение, чтобы найти все пробелы после конца строки:

$\s+

Экранирование символов

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

Допустим, у вас есть строка в тексте статьи:

"Символ новой строки - '\n'"

Или вы хотите найти вообще все упоминания «\n» в тексте. Тогда в шаблоне символ \n нужно «экранировать»: поставить перед ним обратную косую черту:

\\n

Замена строк при помощи регулярных выражений

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

function  youSayHelloISayGoodbye(str)  {
    str = str.replace("Hello", "Goodbye");
    str = str.replace("Hi", "Goodbye");
    str = str.replace("Hey", "Goodbye"); str = str.replace("hello", "Goodbye");
    str = str.replace("hi", "Goodbye");
    str = str.replace("hey", "Goodbye");

    return str;
}

Но можно и проще, с использованием regex:

function  youSayHelloISayGoodbye(str)  {
    str = str.replace(/[Hh]ello|[Hh]i|[Hh]ey/, "Goodbye");

    return str;
}

Флаги в regex

Флаг - это модификатор существующего регулярного выражения. При определении regex флаги всегда добавляются после замыкающего слэша.

Вот небольшой список доступных флагов:

  • g - глобально, больше одного совпадения
  • m - заставляет $ и ^ соответствовать каждой новой строчке отдельно
  • i - делает regex нечувствительным к регистру

Мы можем взять наше регулярное выражение:

/[Hh]ello|[Hh]i|[Hh]ey/

и переписать его, применив флаг нечувствительности к регистру:

/Hello|Hi|Hey/i

Это регулярное выражение будет соответствовать следующим словам:

Hello
HEY
Hi
HeLLo

Также оно будет соответствовать любому другому варианту чередования заглавных и строчных букв.

Флаг глобального поиска для замены строк

Как уже говорилось, если вы производите замену при помощи regex без всяких флагов, заменен будет только первый результат поиска:

let str = "Hello, hi there!";
str = str.replace(/[Hh]ello|[Hh]i|[Hh]ey/, "Goodbye");
console.log(str); // В выводе будет "Goodbye, hi there"

Но если вы добавите флаг глобального поиска, будут найдены все соответствия шаблону:

let str = "Hello, hi there!";
str = str.replace(/[Hh]ello|[Hh]i|[Hh]ey/g, "Goodbye");
console.log(str); // В выводе будет "Goodbye, Goodbye there"