24 травня 2023 р.

Екранування, спеціальні символи

Як ми бачили, бекслеш \ використовується для позначення класів символів, напр. \d. Це спеціальний символ у регулярних виразах (як і у звичайних рядках).

Існують також інші спеціальні символи, які мають особливе значення в регулярних виразах, наприклад [ ] { } ( ) \ ^ $ . | ? * +. Вони використовуються, щоб посилити можливості пошуку.

Не намагайтеся запам’ятати список – незабаром ми розберемо кожен з них окремо, і ви їх легко запам’ятаєте.

Екранування

Скажімо, ми хочемо знайти саме крапку. Не “будь-який символ”, а просто крапку.

Щоб використовувати спеціальний символ як звичайний, додайте перед ним бекслеш: \..

Це також називається “екранування символу”.

Наприклад:

alert( "Chapter 5.1".match(/\d\.\d/) ); // 5.1 (співпадіння!)
alert( "Chapter 511".match(/\d\.\d/) ); // null (шукаємо справжню крапку \.)

Дужки також є спеціальними символами, тому, якщо нам потрібно їх знайти, ми повинні використовувати \(. У прикладі нижче шукаємо рядок "g()":

alert( "function g()".match(/g\(\)/) ); // "g()"

Якщо ми шукаємо бекслеш \, як ви пам’ятаєте, це спеціальний символ як у звичайних рядках, так і в регулярних виразах, ми повинні подвоїти його (екранувати).

alert( "1\\2".match(/\\/) ); // '\'

Слеш

Символ слешу '/' не є спеціальним символом, але в JavaScript він використовується для відкриття та закриття регулярного виразу: /...pattern.../, тому ми також повинні екранувати його.

Ось як виглядає пошук слешу '/':

alert( "/".match(/\//) ); // '/'

З іншого боку, якщо ми не використовуємо /.../, а створюємо регулярний вираз за допомогою new RegExp, тоді нам не потрібно його екранувати:

alert( "/".match(new RegExp("/")) ); // знаходить /

new RegExp

Якщо ми створюємо регулярний вираз за допомогою new RegExp, тоді нам не потрібно екранувати /, але потрібно зробити інше екранування.

Наприклад, розглянемо наступний код:

let regexp = new RegExp("\d\.\d");

alert( "Chapter 5.1".match(regexp) ); // null

Подібний пошук в одному з попередніх прикладів працював із /\d\.\d/, але new RegExp("\d\.\d") не працює, чому?

Причина полягає в тому, що рядок “поглинає” бекслеши. Як ми можемо пам’ятати, звичайні рядки мають власні спеціальні символи, такі як \n, а бекслеш використовується для їх екранування.

Ось як сприймається “\d.\d”:

alert("\d\.\d"); // d.d

Рядкові лапки “поглинають” зворотні слеши та інтерпретують їх самостійно, наприклад:

  • \n – стає символом нового рядка,
  • \u1234 – стає символом Unicode з таким кодом,
  • …А коли немає особливого значення: як-от \d або \z, тоді бекслеш просто видаляється.

Тому new RegExp отримує рядок без бекслешів, і пошук не працює!

Щоб це виправити, нам потрібно подвоїти бекслеши, оскільки лапки рядка автоматично перетворюють \\ на \:

let regStr = "\\d\\.\\d";
alert(regStr); // \d\.\d (тепер правильно)

let regexp = new RegExp(regStr);

alert( "Chapter 5.1".match(regexp) ); // 5.1

Підсумки

  • Для пошуку спеціальних символів [ \ ^ $ . | ? * + ( ), нам потрібно додати перед ними \ (“екранувати їх”).
  • Нам також потрібно екранувати /, якщо ми знаходимося всередині /.../ (але не всередині new RegExp).
  • При передачі рядка в new RegExp, нам необхідно подвоїти бекслеши \\, тому що лапки рядка поглинають один з них.
Навчальна карта