Інтернет Windows Android

Регулярні вирази. Регулярні вирази (об'єкт RegExp) Перевірка форми регулярні вирази javascript приклади

Регулярні вирази - це мова, яка описує шаблони рядків, засновані на метасимвол. Метасимвол - це символ в регулярному виразі, який описує деякий клас символів рядка, вказує на положення підрядка, вказує кількість повторень або групує символи в подстроку. Наприклад, метасимвол \\ d описує цифри, а $ позначає кінець рядка. У регулярному виразі можуть бути присутніми і звичайні символи, які описують самих себе. Набір і значення метасимволов в регулярних виразах описує стандарт PCRE, більшість можливостей якого підтримується в JS.

Область застосування регулярних виразів

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

  • зіставлення. Метою цього завдання буде з'ясувати, чи відповідає певний текст заданому регулярному виразу.
  • Пошук. За допомогою регулярних виразів зручно знаходити відповідні їм підрядка і витягувати їх з тексту.
  • заміна. Регулярні вирази часто допомагають не тільки знайти, а й замінити в тексті подстроку, відповідну регулярному виразу.

В кінцевому рахунку за допомогою регулярних виразів можна, наприклад:

  • Перевірити правильність заповнення призначених для користувача даних в формі.
  • Знайти під вводиться користувачем тексті посилання на зображення, для подальшого його автоматичного прикріплення до повідомлення.
  • Прибрати з тексту html-теги.
  • Перевіряти код до компіляції на наявність простих синтаксичних помилок.

Особливості регулярних виразів в JS. Літерали регулярних виразів

Головною особливістю регулярних виразів в JS є те, що для них існує окремий вид литералов. Так само як рядкові літерали обрамляются лапками, літерали регулярних виразів обрамляются Слеш (/). Таким чином JS-код може містити вирази виду:

console.log (typeof / tcoder /); // object

Справді регулярний вираз, яке визначається в рядку

var pattern \u003d new RegExp ( "tcoder");

Цей спосіб створення зазвичай використовується, коли необхідно використовувати в регулярному виразі змінні, або створювати регулярний вираз динамічно. У всіх інших випадках використовують літерали регулярних виразів через більш короткого синтаксису і відсутності необхідності додатково екранувати деякі символи.

Символи в регулярних виразах

Все алфавітно-цифрові символи в регулярних виразах не є метасимвол і описують самих себе. Це означає, що регулярному виразу / Tcoder / буде відповідати подстрока tcoder. У регулярних виразах так само можна вказувати не алфавітні символи, такі як: новий рядок (\\ n), табуляція (\\ t) і так далі. Всі ці символи так само відповідають самі собі. Поставлений перед алфавітним символом зворотний слеш (\\) зробить його метасимволом, якщо такий є. Наприклад, алфавітний символ «d» стане метасимволом, що описує цифри, якщо його випередити слешем (\\ d).

класи символів

Поодинокі символи в регулярних виразах можна групувати в класи за допомогою квадратних дужок. Створеному таким чином класу відповідає будь-якій з включених в нього символів. Наприклад, регулярному виразу // будуть відповідати букви «t», «c», «o», «d», «e», «r».

У класах також можна задавати діапазон символів за допомогою дефіса. Наприклад, класу відповідає клас. Зауважимо, що деякі метасимволи в регулярних виразах вже описують класи символів. Наприклад, метасимвол \\ d еквівалентний класу. Зауважимо, що метасимволу, що описують класи символів, також можуть включатися в класи. Наприклад, класу [\\ da-f] відповідають цифри і букви «a», «b», «d», «e», «f», тобто будь-який шістнадцятковий символ.

Існує, також, можливість описати клас символів, вказавши символи, які не повинні в нього входити. Робиться це за допомогою метасимвола ^. Наприклад, класу [^ \\ d] буде відповідати будь-який символ окрім цифри.

повторення

Тепер ми можемо описати, скажімо, десяткове число будь-якої заданої довжини, просто написавши поспіль стільки метасимволов \\ d, скільки цифр в цьому числі. Погодьтеся, що такий підхід не дуже зручний. До того ж, ми не можемо описати діапазон необхідної кількості повторень. Наприклад, ми не можемо описати число з однієї або двох цифр. На щастя, в регулярних виразах існує можливість описувати діапазони повторень за допомогою метасимволов. Для цього після символу досить просто вказати діапазон повторень в фігурних дужках. Наприклад, регулярному виразу / Tco (1, 3) der / відповідатимуть рядки «tcoder», «tcooder» і «tcooоder». Якщо опустити максимальну кількість повторень, залишивши кому і мінімальна кількість повторень, то можна вказати кількість повторень більше заданого. Наприклад, регулярному виразу / Bo (2,) bs / відповідатимуть рядки «boobs», «booobs», «boooobs» і так далі з будь-якою кількістю букв «о» не менш двох.

Якщо в фігурних дужках опустити і кому, просто вказавши одне число, то воно буде позначати точну кількість повторень. Наприклад, регулярному виразу / \\ D (5) / відповідають п'ятизначні числа.

Деякі діапазони повторень використовуються досить часто і для їх позначень є свої метасимволу.

жадібні повторення

Наведений вище синтаксис описує максимальну кількість повторень, тобто з усіх можливих кількостей повторень, кількість яких лежить в зазначеному діапазоні - вибирається максимальне. Такі повторення називають жадібними. Це означає, що регулярному виразу / \\ d + / в рядку yeah !! 111 відповідатиме подcтрока «111», а не «11» або «1», хоча метасимвол «+» описує одне і більш повторень.

Якщо ви хочете реалізувати нежадібні повторення, тобто вибирати мінімальну можливу кількість повторень із зазначеного діапазону, то просто поставте символ «?» після діапазону повторень. Наприклад, регулярному виразу / \\ D +? / в рядку «yeah !! 111» буде відповідати подстрока «1», а регулярному виразу / \\ D (2,) / в тому ж рядку буде відповідати подстрока «11».

Варто звернути увагу на важливу особливість нежадібні повторення. Розглянемо регулярний вираз / Bo (2,)? Bs /. У рядку «i like big boooobs» йому буде відповідати, як і при жадібному повторенні, подстрока boooobs, а не boobs, як можна було подумати. Справа в тому, що регулярному виразу при одному зіставленні не може відповідати кілька подстрок, розташованих в різних місцях рядка. Тобто, нашому регулярному виразу не можуть відповідати підрядка «boo» і «bs», склеєні в один рядок.

альтернативи

У регулярних виразах так само можна використовувати альтернативи - описувати безліч рядків, яке відповідає або однієї, або іншої частини регулярного виразу. Такі частини і називаються альтернативами і розділяються за допомогою вертикальної риси. Наприклад, регулярному виразу / Two | twice | \\ 2 / може відповідати або підрядок «two», або підрядок «twice», або підрядок «2». Ланцюжок альтернатив обробляється зліва на право до першого збігу і їй може відповідати тільки подстрока, яку описує тільки одна альтернатива. Наприклад, регулярному виразу / Java | script / в рядку «I like javascript» буде відповідати тільки подстрока «java».

угруповання

Щоб розглядати кілька символів як єдине ціле при використанні діапазонів повторень, класів символів і всього іншого, досить просто взяти їх в круглі дужки. Наприклад, регулярному виразу / True (coder)? / відповідатимуть рядки «truecoder» і «true».

посилання

Крім того, що круглі дужки об'єднують символи в регулярному виразі в єдине ціле, на соответствующею їм подстроку можна посилатися, просто вказавши після слеша номер лівої дужки з пари обрамляють його дужок. Дужки нумеруються зліва на право починаючи з одиниці. Наприклад, в регулярному виразі / (One (two) (three)) (four) / \\ 1 посилається на one, \\ 2 на «two», \\ 3 на «three», \\ 4 на «four». Як приклад використання таких посилань наведемо регулярний вираз / (\\ D) \\ 1 /, Якому відповідають двозначні числа з однаковими цифрами. Важливим обмеженням використання зворотних посилань є неможливість їх використання в класах, тобто, наприклад, описати двозначним числом з різними цифрами регулярним виразом / (\\ D) [^ \\ 1] / не можна.

незапомінающійся дужки

Часто буває необхідно просто згрупувати символи, але немає необхідності створювати посилання. В цьому випадку можна відразу після лівої групуючій дужки можна написати?:. Наприклад, в регулярному виразі / (One) (?: two) (three) / \\ 2 буде вказувати на «three».

Такі дужки іноді називають не запам'ятовуються. Вони мають ще одну важливу особливість, про яку ми поговоримо в наступному уроці.

вказівка \u200b\u200bпозиції

У регулярних виразах так само існують метасимволу, які вказують на деяку позицію в рядку. Частіше за всіх інших, використовуючи не ^, $ вказують на початок і кінець рядка. Наприклад, регулярному виразу /\..+$/ відповідатимуть розширення в назвах файлів, а регулярному виразу / ^ \\ D / перша цифра в рядку, якщо вона є.

Позитивна і негативна випереджаючі перевірки

За допомогою регулярних виразів так само можна описати подстроку, за якою слідує або не слід подстрока, описана іншим шаблоном. Наприклад, нам необхідно знайти слово java тільки якщо за ним слід «script». Це завдання можна вирішити за допомогою регулярного виразу / Java (? \u003d Script) /. Якщо ж нам потрібно описати подстроку «java» за якою не слід script можна скористатися регулярним виразом / Java (?! Script) /.

Зберемо все те, про що ми говорили вище в одну табличку.

символ значення
a | b Відповідає або а, або і.
(…) Групуються дужки. Так само на подстроку, соотвествующих шаблоном в дужках можна посилатися.
(?:…) Тільки угруповання, без можливості посилатися.
\\ n Посилання на подстроку, соответствующею n-ому шаблоном.
^ Початок вхідних даних або початок рядка.
$ Кінець вхідних даних або кінець рядка.
a (? \u003d b) Відповідає підрядку, яку описує шаблон a, тільки якщо за нею йде подстрока, описана шаблоном b.
a (?! b) Відповідає підрядку, яку описує шаблон a, тільки якщо за нею нЕ слід подстрока, описана шаблоном b.

прапори

І, нарешті, останній елемент синтаксису регулярних виразів. Прапори задають правила відповідності, які відносяться до всього регулярному виразу. На відміну від всіх інших елементів, синтаксис регулярних виразів вони пишуться відразу після литерала регулярного виразу, або передаються в рядку в якості другого параметра конструктору об'єкта RegExp.

В JavaScript існують всього три прапори регулярних виразів:

i - при вказівці цього прапора регістр не враховується, тобто, наприклад, регулярному виразу \\ Javascript \\ i відповідатимуть рядки «javascript», «JavaScript», «JAVASCRIPT», «jAvAScript» і т.д.

m - цей прапор включає багатостроковий пошук. Це означає, що якщо в тексті є символи розриву рядків і цей прапор поставлений, то символи ^ і $ крім початку і кінця всього тексту будуть відповідати так само ще початку і кінця кожного рядка в тексті. Наприклад, регулярному виразу / Line $ / m відповідає подстрока «line», як в рядку «first line», так і в рядку «one \\ nsecond line \\ ntwo».

g - включає глобальний пошук, тобто регулярному виразу, якщо цей прапор включений, будуть відповідати всі збіглися з ним підрядка, а не тільки перша, як у випадку, якщо цього прапора немає.

Прапори можна комбінувати між собою в довільному порядку, тобто \\ Tcoder \\ mig, \\ Tcоder \\ gim, \\ Tocder \\ gmi і т.д., це одне і теж. Порядок прапорів так само не має значення, якщо їх передавати в рядку в якості другого аргументу конструктору об'єкта RegExp, тобто new RegExp ( «tcoder», «im») і new RegExp ( «tcoder», «im») так само одне і теж.

З.И.

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

Регулярні вирази ( RegExp) - це дуже ефективний спосіб роботи з рядками.

Склавши регулярний вираз за допомогою спеціального синтаксису ви можете:

  • шукати текст в рядку
  • замінювати подстроки в рядку
  • витягувати інформацію з рядка

Майже у всіх мовах програмування є регулярні вирази. Є невеликі відмінності в реалізації, але загальні концепції застосовуються практично скрізь.

Регулярні вирази відносяться до 1950-х років, коли вони були формалізовані як концептуальний шаблон пошуку для алгоритмів обробки рядків.

Регулярні вирази реалізовані в UNIX, таких як grep, sed і популярних текстових редакторах, почали набирати популярність і були додані в мову програмування Perl, а пізніше і в безліч інших мов.

JavaScript, поряд з Perl, це один з мов програмування в якому підтримка регулярних виразів вбудована безпосередньо в мову.

Складно, по корисно

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

Регулярні вирази складно писати, складно читати і складно підтримувати / змінювати.

Але іноді регулярні вирази це єдиний розумний спосіб виконати якісь маніпуляції над рядками, тому вони є дуже цінним інструментом.

Це керівництво націлене на те щоб найпростішим способом дати вам деяке уявлення про регулярні вирази в JavaScript і надати інформацію про те як читати і створювати регулярні вирази.

Емпіричне правило полягає в тому, що прості регулярні вирази прості для читання і запису, в той час як складні регулярні вирази можуть швидко перетворитися в безлад, Якщо ви не глибоко розумієте основи.

Як виглядають регулярні вирази

В JavaScript регулярні вирази це об'єкт, який може бути визначений двома способами.

Перший спосіб полягає в створенні нового об'єкта RegExp за допомогою конструктора:

Const re1 \u003d new RegExp ( "hey")

Другий спосіб полягає у використанні литералов регулярних виразів:

Const re1 \u003d / hey /

Ви знаєте що в JavaScript є літерали об'єктів і літерали масивів? У ньому також є літерали regexp.

У наведеному вище прикладі hey називається шаблоном. У літеральної формі він знаходиться між двома Слеш, а у випадку з конструктором об'єкта, немає.

Це перша важлива відмінність між двома способами визначення регулярних виразів, решту ми побачимо пізніше.

Як вони працюють?

Регулярний вираз, яке ми визначили вище як re1, дуже просте. Воно шукає рядок hey без каки-яких обмежень: рядок може містити багато тексту, а слово hey перебувати десь в середині і регулярний вираз спрацює. Рядок може містити тільки слово hey і регулярка знову спрацює.

Це досить просто.

Ви можете спробувати протестувати регулярний вираз за допомогою методу RegExp.test (String), який повертає логічне (boolean) значення:

Re1.test ( "hey") // ✅ re1.test ( "blablabla hey blablabla") // ✅ re1.test ( "he") // ❌ re1.test ( "blablabla") // ❌

В наведеному вище прикладі ми просто перевірили чи задовольняє "hey" шаблоном регулярного виразу, який зберігатися в re1.

Це простіше простого, але ви вже знаєте багато про регулярні вирази.

закріплення

/ Hey /

спрацює незалежно від того де знаходиться hey всередині рядка.

Якщо ви хочете знайти рядки, які починаються з hey, то використовуйте оператор ^:

/^hey/.test("hey ") // ✅ /^hey/.test("bla hey") // ❌

Якщо ви хочете знайти рядки, які закінчуються на hey, то використовуйте оператор $:

/hey$/.test("hey ") // ✅ /hey$/.test("bla hey") // ✅ /hey$/.test("hey you ") // ❌

Об'єднуючи два попередніх оператора ви можете знайти рядок, яка повністю збігається з hey:

/^hey$/.test("hey ") // ✅

Щоб знайти рядок починається з одного підрядка, а закінчується інший підрядком ви можете використовувати. *, Який буде збігатися з будь-яким символом повторюваним 0 або більше разів:

/^hey.*joe$/.test("hey joe ") // ✅ /^hey.*joe$/.test("heyjoe") // ✅ /^hey.*joe$/.test("hey how are you joe ") // ✅ /^hey.*joe$/.test("hey joe!") // ❌

Пошук елементів по діапазону

Замість того щоб шукати певну рядок, ви можете вказати діапазон символів, наприклад:

// // a, b, c, ..., x, y, z // // A, B, C, ..., X, Y, Z // // a, b, c // / / 0, 1, 2, 3, ..., 8, 9

Ці регулярні вирази шукають рядки, які містять хоча б один символ з обраного діапазону:

//.test("a ") // ✅ //.test("1") // ❌ //.test("A ") // ❌ //.test("d") // ❌ // .test ( "dc") // ✅

Діапазони можна комбінувати:

// //.test("a ") // ✅ //.test("1") // ✅ //.test("A ") // ✅

Пошук багаторазових збігів елемента діапазону

Ви можете перевірити чи містить рядок тільки один символ з діапазону за допомогою символу -:

/ ^ $ / /^$/.test("A ") // ✅ /^$/.test("Ab") // ❌

інверсія шаблону

Символ ^ на початку шаблону прив'язує його до початку рядка.

Використання цього символу всередині діапазону інвертує діапазон, тому:

/[^A-Za-z0-9]/.test("a ") // ❌ /[^A-Za-z0-9]/.test("1") // ❌ / [^ A-Za -z0-9] /. test ( "A") // ❌ /[^A-Za-z0-9]/.test("@ ") // ✅

метасимволи

  • \\ D збігається з будь-яким числом, еквівалентно
  • \\ D збігається з будь-яким символом, який не є числом, еквівалентно [^ 0-9]
  • \\ W збігається з будь-яким буквено-числовим символом, еквівалентно
  • \\ W збігається з будь-яким символом, який не є буквено-числовим значенням, еквівалентно [^ A-Za-z0-9]
  • \\ S збігається з будь-яким пробільним символом: пробіл, табуляція, символ нового рядка і прогалини Unicode
  • \\ S збігається з будь-яким символом, який не є пропуском
  • \\ 0 збігається з null
  • \\ N збігається з символом нового рядка
  • \\ T збігається з символом табуляції
  • \\ UXXXX збігається з символом Unicode з кодом XXXX (потрібно прапор u)
  • . збігається з будь-яким сімволовом, крім символу нового рядка (таким як \\ n) (якщо ви не використовуєте прапор s, пояснимо пізніше)
  • [^] Збігається з будь-яким символом, включаючи символ нового рядка. Корисно при роботі з багаторядковими рядками

Вибір в регулярних виразах

Якщо ви хочете вибрати одну або інший рядок, використовуйте оператор | .

/hey|ho/.test("hey ") // ✅ /hey|ho/.test("ho") // ✅

квантіфікатори

Уявіть що у вас є регулярний вираз, яке перевіряє рядок на те щоб вона складалася лише з однієї цифри:

Ви можете використовувати квантіфікатор ? , Який зробить цей символ необов'язковим. У нашому випадку цифра повинна зустрічатися 0 або 1 раз:

але що якщо ми хочемо щоб регулярний вираз спрацьовувало на кілька цифр?

Ви можете зробити це 4 способами, використовуючи +, *, (n) і (n, m).

+

Збігається з одним або більше (\u003e \u003d 1) елементами:

/ ^ \\ D + $ / /^\\d+$/.test("12 ") // ✅ /^\\d+$/.test("14") // ✅ /^\\d+$/.test("144343 " ) // ✅ / ^ \\ d + $ /. test ( "") // ❌ /^\\d+$/.test("1a ") // ❌

*

Збігається з 0 або більше (\u003e \u003d 0) елементами:

/ ^ \\ D + $ / /^\\d*$/.test("12 ") // ✅ /^\\d*$/.test("14") // ✅ / ^ \\ d * $ /. Test ( "144343") // ✅ / ^ \\ d * $ /. test ( "") // ✅ /^\\d*$/.test("1a ") // ❌

(N)

Збігається точно з n кількістю елементів:

/ ^ \\ D (3) $ / /^\\d(3)$/.test("123 ") // ✅ /^\\d(3)$/.test("12") // ❌ / ^ \\ (N, m)

Збігається з діапазоном від n до m елементів:

/ ^ \\ D (3,5) $ / /^\\d(3,5)$/.test("123 ") // ✅ /^\\d(3,5)$/.test("1234") // ✅ /^\\d(3,5)$/.test("12345 ") // ✅ /^\\d(3,5)$/.test("123456") // ❌

m можна опустити і залишити другий межа без обмежень, щоб було мінімум n елементів:

/ ^ \\ D (3,) $ / /^\\d(3,)$/.test("12 ") // ❌ /^\\d(3,)$/.test("123") // ✅ /^\\d(3,)$/.test("12345 ") // ✅ /^\\d(3,)$/.test("123456789") // ✅

опціональні елементи

Наступний за елементом знак? , Зробить його необов'язковим:

/ ^ \\ D (3) \\ w? $ / /^\\d(3)\\w?$/.test("123 ") // ✅ / ^ \\ d (3) \\ w? $ /. Test (" 123a ") // ✅ /^\\d(3)\\w?$/.test("123ab") // ❌

Групи

Використовуючи круглі дужки, ви можете створювати групи символів (...).

Приклад нижче шукає точний збіг з 3 цифр за яким слід один або більше буквено-числові символів:

/ ^ (\\ D (3)) (\\ w +) $ / /^(\\d(3))(\\w+)$/.test("123 ") // ❌ / ^ (\\ d (3)) ( \\ w +) $ /. test ( "123s") // ✅ /^(\\d(3))(\\w+)$/.test("123something ") // ✅ / ^ (\\ d (3)) ( \\ w +) $ /. test ( "1234") // ✅

Повторювані символи, які знаходяться після закриває групу дужки, відносяться до всієї групи:

/ ^ (\\ D (2)) + $ / /^(\\d(2))+$/.test("12 ") // ✅ / ^ (\\ d (2)) + $ /. Test (" 123 ") // ❌ /^(\\d(2))+$/.test("1234") // ✅

захоплення груп

До сих пір ми бачили, як тестувати рядки і перевіряти, чи містять вони певний шаблон.

Крута можливість регулярних виразів полягає в тому, що можна захоплювати певні частини рядка і складати їх в масив.

Ви можете робити це за допомогою груп, а точніше за допомогою захоплення груп.

За замовчуванням, групи тож захоплюються. Тепер замість використання RegExp.test (String), який просто повертає логічне значення, ми будемо використовувати один з наступних методів:

  • String.match (RegExp)
  • RegExp.exec (String)

Вони абсолютно однакові і обидва повертають масив з перевіряється рядком в якості першого елемента, а в інших елементах збігу для кожної знайденої групи.

Якщо збігів, не знайдено, то він повертає null.

"123s" .match (/ ^ (\\ d (3)) (\\ w +) $ /) // Array [ "123s", "123", "123s"] / ^ (\\ d (3)) (\\ w + ) $ /. exec ( "123s") // Array [ "123s", "123", "s"] "hey" .match (/ (hey | ho) /) // Array [ "hey", "hey "] /(hey|ho)/.exec("hey") // Array [ "hey", "hey"] /(hey|ho)/.exec("ha! ") // null

Коли група збігається кілька разів, то тільки останнім знайдене значення буде додано в повертається масив.

"123456789" .match (/ (\\ d) + /) // Array [ "123456789", "9"]

опціональні групи

Захоплення груп можна зробити опціональним за допомогою (...)? . Якщо нічого не буде знайдено, то в повертається масив буде додано елемент undefined:

/^(\\d(3))(\\s)?(\\w+)$/.exec("123 s ") // Array [" 123 s "," 123 "," "," s "] / ^ (\\ d (3)) (\\ s)? (\\ w +) $ /. exec ( "123s") // Array [ "123s", "123", undefined, "s"]

Посилання на знайдену групу

Кожної такої групи присвоюється число. $ 1 посилається на перший елемент, $ 2 на другий, і так далі. Це корисно, коли ми буде говорити про заміну частини рядка.

Іменований захоплення груп

Це нова можливість ES2018.

Групі можна призначити ім'я, а не просто слот в повернутому масиві:

Const re \u003d / (? \\ D (4)) - (? \\ D (2)) - (? \\ D (2)) / const result \u003d re.exec ( "2015-01-02") // result.groups.year \u003d\u003d\u003d "2015»; // result.groups.month \u003d\u003d\u003d "01"; // result.groups.day \u003d\u003d\u003d "02";

Використання match і exec без груп

Існує різниця при використанні match і exec без груп: в першому елементі масиву буде знаходиться в повному обсязі знайдена рядок, а пряме збіг:

/hey|ho/.exec("hey ") // [" hey "] /(hey).(ho)/.exec("hey ho") // [ "hey ho", "hey", "ho "]

Незахвативаемие групи

Так як за замовчуванням групи є захоплюючим, нам потрібен спосіб ігнорувати деякі групи в повернутому масиві. Це можливо за допомогою незахвативаемих груп, Які починаються з (?: ...).

"123s" .match (/ ^ (\\ d (3)) (?: \\ S) (\\ w +) $ /) // null "123 s" .match (/ ^ (\\ d (3)) (?: \\ s) (\\ w +) $ /) // Array [ "123 s", "123", "s"]

прапори

Ви можете використовувати наступні прапори на будь-яких регулярних виразах:

  • g: шукає збіги глобально
  • i: робить регулярне вираження не чутливим до регістру
  • m: включає багатостроковий режим. В цьому режимі ^ і $ збігаються з початком і кінцем всього рядка. Без цього прапора, з багаторядковими рядками вони збігаються з початком і кінцем кожного рядка.
  • u: включає підтримку Unicode (додано в ES6 / ES2015)
  • s: (нове в ES2018) скорочення від "single line", він дозволяє. збігатися з символами нового рядка

Прапори можна комбінувати, а також вони додаються в кінець рядка литерала:

/hey/ig.test("HEy ") // ✅

або передаються другим параметром в конструктор об'єкта RegExp:

New RegExp ( "hey", "ig"). Test ( "HEy") // ✅

Інспектування регулярних виразів

Ви можете перевіряти властивості регулярних виразів:

  • source - рядок шаблону
  • multiline - приймається значення true якщо встановлений прапор m
  • global - приймається значення true якщо встановлений прапор g
  • ignoreCase - приймається значення true якщо встановлений прапор i
  • lastIndex
/^(\\w(3))$/i.source // "^ (\\\\ d (3)) (\\\\ w +) $" /^(\\w(3))$/i.multiline // false /^(\\w(3))$/i.lastIndex // 0 /^(\\w(3))$/i.ignoreCase // true /^(\\w(3))$/i.global // false

екранування

Спеціальні символи:

Це спеціальні символи бо вони є керуючими символами при складанні шаблонів регулярних виразів, тому якщо ви хочете використовувати їх для пошуку збігів усередині шаблону, то вам потрібно екранувати їх за допомогою символу зворотного слеша:

/ ^ \\\\ $ / / ^ \\ ^ $ / // /^\\^$/.test("^ ") ✅ / ^ \\ $$ / // /^\\$$/.test("$") ✅

межі рядків

\\ B і \\ B дозволяють визначити чи знаходиться рядок на початку або кінці слова:

  • \\ B збігається якщо набір символів знаходиться на початку або кінці слова
  • \\ B збігається якщо набір символів чи не знаходиться на початку або кінці слова

"I saw a bear" .match (/ \\ bbear /) // Array [ "bear"] "I saw a beard" .match (/ \\ bbear /) // Array [ "bear"] "I saw a beard" .match (/ \\ bbear \\ b /) // null "cool_bear" .match (/ \\ bbear \\ b /) // null

Заміна за допомогою регулярних виразів

Ми вже бачили як потрібно перевіряти рядки на збіг з шаблоном.

Також ми бачили як можна витягати частина рядків відповідні шаблоном в масив.

Тепер давайте розглянемо як замінювати частини рядка на основі шаблону.

У об'єкта String в JavaScript є метод replace (), який можна використовувати без регулярних виразів для однієї заміни в рядку:

"Hello world!". Replace ( "world", "dog") // Hello dog! "My dog \u200b\u200bis a good dog!". Replace ( "dog", "cat") // My cat is a good dog!

Цей метод також може приймати і регулярний вираз як аргумент:

"Hello world!". Replace (/ world /, "dog") // Hello dog!

Використання прапора g - це єдиний спосіб замінити кілька входжень в рядку на ванільному JavaScript:

"My dog \u200b\u200bis a good dog!". Replace (/ dog / g, "cat") // My cat is a good cat!

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

"Hello, world!". Replace (/ (\\ w +), (\\ w +)! /, "$ 2: $ 1 !!!") // "world: Hello !!!"

Замість рядка можна використовувати функцію, щоб робити ще більш цікаві речі. У неї буде переданий ряд аргументів, таких як повертають методи String.match (RegExp) або RegExp.exec (String), де кількість аргументів залежить від кількості груп:

"Hello, world!". Replace (/ (\\ w +), (\\ w +)! /, (MatchedString, first, second) \u003d\u003e (console.log (first); console.log (second); return `$ ( second.toUpperCase ()): $ (first) !!! `)) //" WORLD: Hello !!! "

жадібність

Регулярні вирази називаються жадібними за замовчуванням.

Що це означає?

Візьмемо наприклад це регулярний вираз:

/\\$(.+)\\s?/

Передбачається, що нам потрібно витягти з рядка суму в доларах:

/\\$(.+)\\s?/.exec("This costs $ 100 ") // 0

але що якщо у нас є більше слів після числа, це відволікає

/\\$(.+)\\s?/.exec("This costs $ 100 and it is less than $ 200 ") // 100 and it is less than $ 200

Чому? Тому що регулярний вираз після знака $ збігається з будь-яким символом. + Та не зупиняється поки не досягне кінця рядка. Потім він зупиняється, тому що \\ s? робить кінцеве простір необов'язковим.

Щоб виправити це, нам потрібно вказати що регулярний вираз має бути ледачим і знайти найменшу кількість збігів. Ми можемо зробити це за допомогою символу? після квантіфікатора:

/\\$(.+?)\\s/.exec("This costs $ 100 and it is less than $ 200 ") // 100

Отже, символ? може означати різні речі в залежності від їхнього місця, тому він може бути і квантіфікатор і індикатором ледачого режиму.

Випередження: відповідність рядка в залежності від того що за нею йде

Іспользуйет? \u003d Для пошуку збігів в рядку за якою слідує певна подстрока

/ Roger (? \u003d Waters) / / Roger (? \u003d Waters) /. Test ( "Roger is my dog") // false / Roger (? \u003d Waters) /. Test ( "Roger is my dog \u200b\u200band Roger Waters is a famous musician ") // true

Виконує зворотну операцію і знаходить збігів в рядку за якими нЕ слід певна подстрока:

/ Roger (?! Waters) / / Roger (?! Waters) /. Test ( "Roger is my dog") // true / Roger (?! Waters) /. Test ( "Roger is my dog \u200b\u200band Roger Waters is a famous musician ") // false

Ретроспектива: відповідність рядка в залежності від того що їй передує

Це нова можливість ES2018.

Випередження використовує символ? \u003d. Ретроспектива використовує?<= :

/(?<=Roger) Waters/ /(?<=Roger) Waters/.test("Pink Waters is my dog") //false /(?<=Roger) Waters/.test("Roger is my dog and Roger Waters is a famous musician") //true

Інверсія ретроспективи використовує?

/(?

Регулярні вирази і Unicode

Прапор u є обов'язковим при роботі з Unicode рядками, зокрема коли може знадобиться обробляти рядки в астральних площинах, які не включені в перші 1600 символів Unicode.

Наприклад емодзі, але і тільки вони.

/^.$/.test("a ") // ✅ /^.$/. test ("? ") // ❌ /^.$/ u.test ("? ") // ✅

Тому, завжди використовуйте прапор u.

Unicode, як і звичайні символи, може обробляти діапазони:

//.test("a ") // ✅ //.test("1") // ✅ / [? -?] / u.test ( "?") // ✅ / [? -?] / u .test ( "?") // ❌

JavaScript перевіряє внутрішні коди уявлення, тому?< ? < ? на самом деле \u1F436 < \u1F43A < \u1F98A . Посмотрите полный список эмодзи чтобы увидеть коды и узнать их порядок.

Екранування властивостей Unicode

Як ми говорили вище, в шаблоні регулярного виразу ви можете використовувати \\ d щоб знайти збіг на будь-яку цифру, \\ s щоб знайти збіг на будь-який символ окрім пробілу, \\ w щоб знайти збіг на будь-який буквено-числовий символ і т. Д.

Екранування властивостей Unicode - це можливість ES2018, яка додає дуже круту функцію, розширюючи цю концепцію на всіх Unicode символи і додаючи \\ p () і \\ P ().

У будь-якого Unicode символу є набір властивостей. Наприклад Script визначає сімейство мов, ASCII - це логічне значення рівне true для ASCII символів і т.д. Ви можете покласти це властивість в фігурні дужки і регулярний вираз буде перевіряти щоб його значення було істинним:

/^\\p(ASCII)+$/u.test("abc ") // ✅ / ^ \\ p (ASCII) + $ / u.test (" [Email protected]") // ✅ /^\\p(ASCII)+$/u.test("ABC?") // ❌

ASCII_Hex_Digit - це ще одне логічне властивість, яке перевіряє чи містить рядок тольк валідниє шістнадцятиричні цифри:

/^\\p(ASCII_Hex_Digit)+$/u.test("0123456789ABCDEF ") // ✅ /^\\p(ASCII_Hex_Digit)+$/u.test("h")

Існує багато інших логічних властивостей, які ви можете перевірити просто додавши їх ім'я в фігурні дужки, включаючи Uppercase, Lowercase, White_Space, Alphabetic, Emoji та інші:

/^\\p(Lowercase)$/u.test("h ") // ✅ /^\\p(Uppercase)$/u.test("H") // ✅ / ^ \\ p (Emoji) + $ / u.test ( "H") // ❌ / ^ \\ p (Emoji) + $ / u.test ( "??") // ✅

У доповненні до цих бінарним властивостями, ви можете перевірити будь-яку властивість символу Unicode щоб відповідало конкретним значенням. У прикладі нижче я перевіряю, записана рядок в грецькому або латинському алфавіті:

/^\\p(Script\u003dGreek)+$/u.test("ελληνικά ") // ✅ /^\\p(Script\u003dLatin)+$/u.test("hey") // ✅

приклади

Витяг числа з рядка

Припустимо, що є рядок містить лише одне число, яке потрібно витягти. / \\ D + / повинен зробити це:

"Test 123123329" .match (/ \\ d + /) // Array [ "123123329"]

Пошук E-mail адреси:

Найпростіший підхід полягає в перевірці безпробельних символів до і після знаку @, за допомогою \\ S:

/(\\S+)@(\\S+)\\.(\\S+)/ / (\\ S +) @ (\\ S +) \\. (\\ S +) /. Exec ( " [Email protected]") //["[Email protected]"," Copesc "," gmail "," com "]

Однак, це спрощений приклад, так як під нього потрапляє множина не валідних E-mail адрес.

Захоплення тексту між подвійними лапками

Уявімо, що у вас є рядок, яка містить текст укладений в подвійні лапки і вам потрібно витягти цей текст.

Кращий спосіб зробити це - використовувати захоплення груп, Тому то ми знаємо що наше збіг має починатися і закінчуватися символом ", тому ми можемо легко налаштувати шаблон, але також ми хочемо видалити ці лапки з результату.

Ми знайдемо те що нам потрібно в result:

Const hello \u003d "Hello" nice flower "" const result \u003d /"([^"]*)"/.exec(hello) // Array [ "\\" nice flower \\ "", "nice flower"]

Отримання вмісту з HTML тега

Наприклад отримати вміст з тега span, допускаючи при цьому будь-яку кількість аргументів у тега:

/]*>(.*?)<\/span>/ /]*>(.*?)<\/span>/.exec("test ") // null / ]*>(.*?)<\/span>/.exec("test ") // [" test "," test "] / ]*>(.*?)<\/span>/.exec ( " test") // ["test"," Test "]

Клас RegExp в JavaScript являє собою регулярні вирази - об'єкт, що описує символьний шаблон. Об'єкти RegExp, як правило, створюються за допомогою спеціального синтаксису литералов, представленого нижче, але так само можуть бути створені за допомогою конструктора RegExp ().

синтаксис

// за допомогою спеціального синтаксису литералов var regex \u003d / pattern / flags; // за допомогою конструктора var regex \u003d new RegExp ( "pattern", "flags"); var regex \u003d new RegExp (/ pattern /, "flags");

Значення параметрів:

Прапори регулярних виразів

прапоропис
g Дозволяє знайти всі збіги, а не зупинятися після першого збігу ( global match flag).
iДозволяє виконати зіставлення без урахування регістру ( ignore case flag).
mЗіставлення проводиться за кількома рядками. Обробка початкових і кінцевих символів (^ і $) проводиться в кількох рядках, тобто зіставлення відбувається з початком або кінцем кожного рядка (роздільники \\ n або \\ r), а не тільки з початком, або кінцем всього рядка ( multiline flag).
uШаблон буде розцінений як послідовність кодових точок Юникода ( unicode flag).
yЗіставлення відбувається за індексом на який вказує властивість lastIndex цього регулярного виразу, при цьому порівняння не проводитися по більш пізнього, або раннього індексу ( sticky flag).

набори символів

метасимволи

символопис
. Дозволяє знайти один символ, крім символу нового рядка, або символу кінця рядка (\\ n, \\ r, \\ u2028 або \\ u2029).
\\ dДозволяє знайти символ цифри в базовому латинському алфавіті. Еквівалентін використанню набору символів.
\\ DДозволяє знайти будь-який символ, який не є цифрою в базовому латинському алфавіті. Еквівалентний набору символів [^ 0-9].
\\ sДозволяє знайти одиночний символ пробілу. Під пробільним символом розуміється пробіл, табуляція, переклад сторінки, переклад рядка і інші пробільні символи Юнікоду. Еквівалентний набору символів [\\ f \\ n \\ r \\ t \\ v \\ u00a0 \\ u1680 \\ u180e \\ u2000 \\ u2001 \\ u2002 \\ u2003 \\ u2004 \\ u2005 \\ u2006 \\ u2007 \\ u2008 \\ u2009 \\ u200a \\ u2028 \\ u2029 \\ u202f \\ u205f \\ u3000].
\\ SДозволяє знайти одиночний символ, який не є пробільним. Під пробільним символом розуміється пробіл, табуляція, переклад сторінки, переклад рядка і інші пробільні символи Юнікоду. Еквівалентний набору символів [^ \\ f \\ n \\ r \\ t \\ v \\ u00a0 \\ u1680 \\ u180e \\ u2000 \\ u2001 \\ u2002 \\ u2003 \\ u2004 \\ u2005 \\ u2006 \\ u2007 \\ u2008 \\ u2009 \\ u200a \\ u2028 \\ u2029 \\ u202f \\ u205f \\ u3000].
[\\ B]Дозволяє знайти символ backspace (спеціальний символ \\ b, U + 0008).
\0 Дозволяє знайти символ 0 (нуль).
\\ nДозволяє знайти символ нового рядка.
\\ fДозволяє знайти символ перекладу сторінки.
\\ rДозволяє знайти символ повернення каретки.
\\ tДозволяє знайти символ горизонтальної табуляції.
\\ vДозволяє знайти символ вертикальної табуляції.
\\ wДозволяє знайти будь-який буквено-цифровий символ базового латинського алфавіту, включаючи підкреслення. Еквівалентний набору символів.
\\ WДозволяє знайти будь-який символ, який не є символом з базового латинського алфавіту. Еквівалентний набору символів [^ a-Za-z0-9_].
\\ cXДозволяє знайти контрольний символ в рядку. Де X - буква від A до Z. Наприклад, / \\ cM / позначає символ Ctrl-M.
\\ xhhДозволяє знайти символ, використовуючи шістнадцяткове значення (hh - двозначне шістнадцяткове значення).
\\ uhhhhДозволяє знайти символ, використовуючи кодування UTF-16 (hhhh - чотиризначний шістнадцяткове значення).
\\ U (hhhh) або
\\ U (hhhhh)
Дозволяє знайти Символ із Юникода U + hhhh або U + hhhhh (шістнадцяткове значення). Тільки коли заданий прапор u.
\ Вказує, що наступний символ є спеціальним і не повинен інтерпретуватися буквально. Для символів, які зазвичай трактуються спеціальним чином, вказує, що наступний символ не є спеціальним і повинен інтерпретуватися буквально.

обмеження

квантіфікатори

символопис
n *Сопостовленія відбувається з будь-рядком, що містить нуль або більше входжень символу n.
n +Сопостовленія відбувається з будь-рядком, що містить хоча б один символ n.
n?Сопостовленія відбувається з будь-рядком з попереднім елементом n нуль або один раз.
n (x)Відповідає будь-якому рядку, що містить послідовність символів n певну кількість разів x. X
n (x,) x входжень попереднього елемента n. X має бути цілим позитивним числом.
n (x, y)Відповідає будь-якому рядку, що містить принаймні x, Але не більше, ніж з y входженнями попереднього елемента n. X і y повинні бути цілими позитивними числами.
n *?
n +?
n ??
n (x)?
n (x,)?
n (x, y)?
Сопостовленія відбувається за аналогією з квантіфікаторамі *, +,? і (...), однак при цьому пошук йде мінімально можливого зіставлення. За замовчуванням використовується "жадібний" режим,? в кінці квантіфікатора дозволяє задати "нежадібна" режим при якому повторення зіставлення відбувається мінімально можливу кількість разів.
x (? \u003d y)дозволяє зіставити x, Тільки якщо за x слід y.
x (?! y)дозволяє зіставити x, Тільки якщо за x не слід y.
x | yЗіставлення відбувається з будь-якої з зазначених альтернатив.

Угруповання і зворотні посилання

символопис
(X)Дозволяє знайти символ x і запам'ятати результат зіставлення ( "захоплюючі дужки"). Зіставлена \u200b\u200bподстрока може бути викликана з елементів результуючого масиву ..., [n], або з властивостей визначеного об'єкта RegExp $ 1 ..., $ 9.
(?: X)Дозволяє знайти символ x, Але не запам'ятовувати результат зіставлення ( "незахвативающіе дужки"). Зіставлена \u200b\u200bподстрока не може бути викликана з елементів результуючого масиву ..., [n], або з властивостей визначеного об'єкта RegExp $ 1 ..., $ 9.
\\ nЗворотній посилання на останню подстроку, збігається з n-ой за рахунком в круглих дужках в регулярному виразі (нумерація дужок йде зліва направо). n має бути цілим позитивним числом.

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

синтаксис:

// Перший варіант створення регулярного виразу var regexp \u003d new RegExp ( шаблон,модифікатори); // Другий варіант створення регулярного виразу var regexp \u003d / шаблон/модифікатори;

шаблон дозволяє задати шаблон символів для пошуку.

модифікатори дозволяють налаштувати поведінку пошуку:

  • i - пошук без урахування регістру букв;
  • g - глобальний пошук (будуть знайдені всі збіги в документі, а не тільки перше);
  • m - багаторядковий пошук.

Пошук слів і виразів

Найпростішим застосуванням регулярних виразів є пошук слів і виразів в різних текстах.

Наведемо приклад використання пошуку із застосуванням модифікаторів:

// Задамо регулярний вираз rv1 rv1 \u003d / Росія /; // Задамо регулярний вираз rv2 rv2 \u003d / Росія / g; // Задамо регулярний вираз rv3 rv3 \u003d / Росія / ig; // Жирним шрифтом виділено, де в тексті будуть знайдені збіги при використанні // вираження rv1: Росія є найбільшою державою світу. Росія межує з 18 країнами. РОСІЯ є державою-продовжувачкою СРСР. // Жирним шрифтом виділено, де в тексті будуть знайдені збіги при використанні // вираження rv2: Росія є найбільшою державою світу. Росія межує з 18 країнами. РОСІЯ є державою-продовжувачкою СРСР. "; // Жирним шрифтом виділено, де в тексті будуть знайдені збіги при використанні // вираження rv3: Росія є найбільшою державою світу. Росія межує з 18 країнами. РОСІЯ є державою - продовжувачкою СРСР.";

Спеціальні символи

Крім звичайних символів в шаблонах регулярних виразів можуть використовуватися спеціальні символи (Метасимволу). Спеціальні символи з описами наведені в таблиці нижче:

спеціальний символ опис
. Збігається з будь-яким символом, крім символу кінця рядка.
\\ w Збігається з будь-яким буквеним символом.
\\ W Збігається з будь-яким не літерним символом.
\\ d Збігається з символами, які є цифрами.
\\ D Збігається з символами, які не є цифрами.
\\ s Збігається з пробільними символами.
\\ S Збігається з не пробільними символами.
\\ b Збіги будуть шукатися тільки на кордонах слів (на початку або наприкінці).
\\ B Збіги будуть шукатися тільки не на кордонах слів.
\\ n Збігається з символом переведення рядка.

/ * Вираз reg1 знайде все слова починаються на дві довільні букви і закінчуються на "вет". Так як слова в реченні розділяються пропуском, то на початку і в кінці додамо спецсимвол \\ s) * / reg1 \u003d / \\ s..вет \\ s / g; txt \u003d "привіт заповіт вельвет клозет"; document.write (txt.match (reg1) + "
"); / * Вираз reg2 знайде все слова починаються на три довільні букви і закінчуються на" вет "* / reg2 \u003d / \\ s ... вет \\ s / g; document.write (txt.match (reg2) +"
"); Txt1 \u003d" прі2вет привіт прі1вет "; / * Вираз reg3 знайде все слова, які починаються на" при "в яких потім слід 1 цифра і закінчуються на" вет "* / var reg3 \u003d / при \\ dвет / g; document .write (txt1.match (reg3) + "
"); // Вираз reg4 знайде всі цифри в тексті var reg4 \u003d / \\ d / g; txt2 \u003d" 5 років навчання, 3 роки плавання, 9 років стрільби. "Document.write (txt2.match (reg4) +"
");

Швидкий перегляд

Символи в квадратних дужках

Використовуючи квадратні дужки [Кейу] Ви можете вказати групу символів, пошук яких потрібно зробити.

Символ ^ перед групою символів в квадратних дужках [^ КВГ] говорить про те, що потрібно зробити пошук всіх символів алфавіту крім заданих.

Використовуючи тире (-) між символами в квадратних дужках [А-з] Ви можете задати діапазон символів, пошук яких потрібно зробити.

За допомогою квадратних дужок Ви можете також шукати числа.

// Задамо регулярний вираз reg1 reg1 \u003d / \\ sко [ТДМ] \\ s / g; // Задамо рядок тексту txt1 txt1 \u003d "кіт коса код комод кому килим"; // Зробимо за допомогою регулярного виразу reg1 пошук по рядку txt1 document.write (txt1.match (reg1) + "
"); Reg2 \u003d / \\ sсло [^ тг] / g; txt2 \u003d" слот слон склад "; document.write (txt2.match (reg2) +"
"); Reg3 \u003d // g; txt3 \u003d" 5 років навчання, 3 роки плавання, 9 років стрільби "; document.write (txt3.match (reg3));

Швидкий перегляд

квантіфікатори

квантіфікатор - це конструкція дозволяє задати скільки разів попередній їй символ або група символів повинна зустрічатися в збіг.

синтаксис:

// Попередній символ повинен зустрічатися x - раз (X) // Попередній символ повинен зустрічатися від x до у раз включно (X, y) // Попередній символ повинен зустрічатися не менше x раз (X,) // Вказує, що попередній символ повинен зустрічатися 0 або більше разів * // Вказує що попередній символ повинен зустрічатися 1 або більше разів + // Вказує що попередній символ повинен зустрічатися 0 або 1 раз ?


// Задамо регулярний вираз rv1 rv1 \u003d / ко (5) шка / g // Задамо регулярний вираз rv2 rv2 \u003d / ко (3,) шка / g // Задамо регулярний вираз rv3 rv3 \u003d / до + шка / g // Задамо регулярний вираз rv4 rv4 \u003d / до? шка / g // Задамо регулярний вираз rv5 rv5 \u003d / до * шка / g // Жирним шрифтом показано, де в тексті будуть знайдені збіги при використанні // вираження rv1: кшка кішка коошка кооошка коооошка кооооошка коооооошка кооооооошка // Жирним шрифтом показано, де в тексті будуть знайдені збіги при використанні // вираження rv2: кшка кішка коошка кооошка коооошка кооооошка коооооошка кооооооошка // Жирним шрифтом показано, де в тексті будуть знайдені збіги при використанні // вираження rv3: кшка кішка коошка кооошка коооошка кооооошка коооооошка кооооооошка // Жирним шрифтом показано, де в тексті будуть знайдені збіги при використанні // вираження rv4: кшка кішка коошка кооошка коооошка кооооошка коооооошка кооооооошка // Жирним шрифтом показано, де в тексті будуть знайдені збіги при використанні // вираження rv5: кшка кішка коошка кооошка коооошка кооооошка коооооошка кооооооошка

Зверніть увагу: якщо Ви хочете використовувати будь-який спеціальний символ (такий як. * +? або ()) як звичайний Ви повинні поставити перед ним \\.

Використання круглих дужок

Укладаючи частина шаблону регулярного виразу у круглих дужках Ви вказуєте висловом запам'ятати збіг, знайдене цією частиною шаблону. Збережене збіг може використовуватися пізніше в Вашому коді.

Наприклад регулярний вираз / (Дмитро) \\ sВасільев / знайде рядок "Дмитро Васильєв" і запам'ятає подстроку "Дмитро".

У прикладі нижче ми використовуємо метод replace (), щоб змінити порядок слів у тексті. Для звернення до збережених збігів ми використовуємо $ 1 і $ 2.

Var regexp \u003d / (Дмитро) \\ s (Васильєв) /; var text \u003d "Дмитро Васильєв"; var newtext \u003d text.replace (regexp, "$ 2 $ 1"); document.write (newtext);

Швидкий перегляд

Круглі дужки можуть використовуватися для групування символів перед квантіфікаторамі.

Деякі люди, зіткнувшись з проблемою, думають: «О, а використовую-ка я регулярні вирази». Тепер у них є дві проблеми.
Джеймі Завінського

Юан-Ма сказав: «Потрібна велика сила, щоб різати дерево поперек структури деревини. Потрібно багато коду, щоб програмувати поперек структури проблеми.
Майстер Юан-Ма, «Книга програмування»

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

У цьому розділі ми обговоримо такий інструмент - регулярні вирази. Це спосіб описувати шаблони в строкових даних. Вони створюють невеликий окрема мова, який входить в JavaScript і в безліч інших мов і інструментів.

Регулярки одночасно дуже дивні і вкрай корисні. Їх синтаксис загадковий, а програмний інтерфейс в JavaScript для них незграбний. Але це потужний інструмент для дослідження і обробки рядків. Розібравшись з ними, ви станете більш ефективним програмістом.

Створюємо регулярний вираз

Регулярки - тип об'єкту. Її можна створити, викликавши конструктор RegExp, або написавши потрібний шаблон, оточений Слеш.

Var re1 \u003d new RegExp ( "abc"); var re2 \u003d / abc /;

Обидва цих регулярних вислови один шаблон: символ "a", за яким слід символ "b", за яким слід символ "c".

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

Другий запис, де шаблон знаходиться між Слеш, обробляє зворотні слеші по-іншому. По-перше, тому що шаблон закінчується прямим слешем, то потрібно ставити зворотний слеш перед прямим слешем, який ми хочемо включити в наш шаблон. Крім того, зворотні слеші, які не є частиною спеціальних символів типу \\ n, будуть збережені (а не проігноровані, як в рядках), і змінять зміст шаблону. У деяких символів, таких, як знак питання чи плюс, є особливе значення в регулярки, і якщо вам потрібно знайти такий символ, його також треба випереджати зворотним слешем.

Var eighteenPlus \u003d / eighteen \\ + /;

Щоб знати, які символи треба випереджати слешем, вам треба вивчити список всіх спеціальних символів в регулярки. Поки це нереально, тому в разі сумнівів просто ставте зворотний слеш перед будь-яким символом, які не є буквою, числом або пропуском.

Перевіряємо на збіги

У регулярок є кілька методів. Найпростіший - test. Якщо передати йому рядок, він поверне булевское значення, повідомляючи, чи містить рядок входження заданого шаблону.

Console.log (/abc/.test ( "abcde")); // → true console.log (/abc/.test ( "abxde")); // → false

Регулярки, що складається тільки з неспеціальних символів, просто є послідовність цих символів. Якщо abc є десь в рядку, яку ми перевіряємо (не тільки на початку), test поверне true.

Шукаємо набір символів

З'ясувати, чи містить рядок abc, можна було б і за допомогою indexOf. Регулярки дозволяють пройти далі і складати більш складні шаблони.

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

Обидва вирази знаходяться в рядках, що містить цифру.

Console.log (//. Test ( "in тисячі дев'ятсот дев'яносто дві")); // → true console.log (//. Test ( "in +1992")); // → true

У квадратних дужках тире між двома символами використовується для завдання діапазону символів, де послідовність задається кодуванням Unicode. Символи від 0 до 9 знаходяться там просто поспіль (коди з 48 до 57), тому захоплює їх все і збігається з будь-якою цифрою.

У кількох груп символів є свої вбудовані скорочення.

\\ D Будь-яка цифра
\\ W Алфавітно-цифровий символ
\\ S Пробільний символ (пробіл, табуляція, переклад рядка, і т.п.)
\\ D не цифри
\\ W НЕ алфавітно-цифровий символ
\\ S НЕ символ пробілу
. будь-який символ, крім перекладу рядки

Таким чином можна задати формат дати і часу на кшталт 30-01-2003 15:20 наступним виразом:

Var dateTime \u003d / \\ d \\ d- \\ d \\ d- \\ d \\ d \\ d \\ d \\ d \\ d: \\ d \\ d /; console.log (dateTime.test ( "30-01-2003 15:20")); // → true console.log (dateTime.test ( "30-jan-2003 15:20")); // → false

Виглядає жахливо, чи не так? Занадто багато зворотних слешів, які ускладнюють розуміння шаблону. Пізніше ми злегка покращимо його.

Зворотні слеші можна використовувати і в квадратних дужках. Наприклад, [\\ d.] Означає будь-яку цифру або точку. Зауважте, що точка всередині квадратних дужок втрачає своє особливе значення і перетворюється просто в точку. Те ж стосується і інших спеціальних символів, типу +.

Інвертувати набір символів - тобто, сказати, що вам треба знайти будь-який символ, крім тих, що є в наборі - можна, поставивши знак ^ відразу після відкриває квадратної дужки.

Var notBinary \u003d / [^ 01] /; console.log (notBinary.test ( "1100100010100110")); // → false console.log (notBinary.test ( "1100100010200110")); // → true

Повторюємо частини шаблону

Ми знаємо, як знайти одну цифру. А якщо нам треба знайти число цілком - послідовність з однієї або більше цифр?

Якщо поставити після чого-небудь в регулярке знак +, це буде означати, що цей елемент може бути повторений більш ніж один раз. / \\ D + / означає одну або кілька цифр.

Console.log (/ "\\ d +" /. Test ( "" 123 "")); // → true console.log (/ "\\ d +" /. Test ( "" "")); // → false console.log (/ "\\ d *" /. Test ( "" 123 "")); // → true console.log (/ "\\ d *" /. Test ( "" "")); // → true

У зірочки * значення майже таке ж, але вона дозволяє шаблоном присутнім нуль раз. Якщо після чогось варта зірочка, то воно ніколи не перешкоджає знаходженню шаблону в рядку - воно просто перебуває там нуль раз.

Знак питання робить частину шаблону необов'язковою, тобто вона може зустрітися нуль або один раз. У наступному прикладі символ u може зустрічатися, але шаблон збігається і тоді, коли його немає.

Var neighbor \u003d / neighbou? R /; console.log (neighbor.test ( "neighbour")); // → true console.log (neighbor.test ( "neighbor")); // → true

Щоб задати точну кількість разів, яке шаблон повинен зустрітися, використовуються фігурні дужки. (4) після елемента означає, що він повинен зустрітися в рядку 4 рази. Також можна задати проміжок: (2,4) означає, що елемент повинен зустрітися не менше 2 і не більше 4 разів.

Ще одна версія формату дати і часу, де дозволені дні, місяці і годинник з однієї або двох цифр. І ще вона трохи більше читана.

Var dateTime \u003d / \\ d (1,2) - \\ d (1,2) - \\ d (4) \\ d (1,2): \\ d (2) /; console.log (dateTime.test ( "30-1-2003 8:45")); // → true

Можна використовувати проміжки з відкритим кінцем, опускаючи одне з чисел. (, 5) означає, що шаблон може зустрітися від нуля до п'яти разів, а (5,) - від п'яти і більше.

угруповання подвираженій

Щоб використовувати оператори * або + на кількох елементах відразу, можна використовувати круглі дужки. Частина регулярки, укладена в дужки, вважається одним елементом з точки зору операторів.

Var cartoonCrying \u003d / boo + (hoo +) + / i; console.log (cartoonCrying.test ( "Boohoooohoohooo")); // → true

Перший і другий плюси відносяться тільки до других буквах про в словах boo і hoo. Третій + відноситься до цілої групи (hoo +), знаходячи одну або кілька таких послідовностей.

Буква i в кінці виразу робить регулярку нечутливою до регістру симолів - так, що B збігається з b.

Збіги і групи

Метод test - найпростіший метод перевірки регулярок. Він тільки повідомляє, чи було знайдено збіг, чи ні. У регулярок є ще метод exec, який поверне , якщо нічого не було знайдено, а в іншому випадку поверне об'єкт з інформацією про збіг.

Var match \u003d /\\d+/.exec("one two 100 "); console.log (match); // → [ "100"] console.log (match.index); // → 8

У повертається exec об'єкта є властивість index, де міститься номер символу, з якого сталося збіг. А взагалі об'єкт виглядає як масив рядків, де перший елемент - рядок, яку перевіряли на збіг. У нашому прикладі це буде послідовність цифр, яку ми шукали.

У рядків є метод match, який працює приблизно так само.

Console.log ( "one two 100" .match (/ \\ d + /)); // → [ "100"]

Коли в регулярке містяться подвираженія, згруповані круглими дужками, текст, який співпав з цими групами, теж з'явиться в масиві. Перший елемент завжди збіг цілком. Другий - частина, яка збіглася з першою групою (тієї, у кого круглі дужки зустрілися раніше всіх), потім з другою групою, і так далі.

Var quotedText \u003d / "([^"] *) "/; console.log (quotedText.exec (" she said "hello" ")); // → [" "hello" "," hello "]

Коли група не знайдена взагалі (наприклад, якщо за нею стоїть знак питання), її позиція в масиві містить undefined. Якщо група збіглася кілька разів, то в масиві буде тільки останній збіг.

Console.log (/ bad (ly)? /. Exec ( "bad")); // → [ "bad", undefined] console.log (/ (\\ d) + /. Exec ( "123")); // → [ "123", "3"]

Групи корисні для вилучення частин рядків. Якщо нам не просто треба перевірити, чи є в рядку дата, а витягти її та створити представляє дату об'єкт, ми можемо зробити висновок послідовності цифр в круглі дужки і вибрати дату з результату exec.

Але для початку невеликий відступ, в якому ми дізнаємося кращий спосіб зберігання дати та часу в JavaScript.

Тип дати

В JavaScript є стандартний тип об'єкта для дат - а точніше, моментів у часі. Він називається Date. Якщо просто створити об'єкт дати через new, ви отримаєте поточну дату і ремя.

Console.log (new Date ()); // → Sun Nov 09 2014 00:07:57 GMT + 0300 (CET)

Також можна створити об'єкт, що містить заданий час

Console.log (new Date (2015-го, 9, 21)); // → Wed Oct 21 2015 00:00:00 GMT + 0300 (CET) console.log (new Date (2009, 11, 9, 12, 59, 59, 999)); // → Wed Dec 09 2009 12:59:59 GMT + 0300 (CET)

JavaScript використовує угоду, в якому номери місяців починаються з нуля, а номери днів - з одиниці. Це нерозумно і безглуздо. Побережіться.

Останні чотири аргументи (години, хвилини, секунди і мілісекунди) необов'язкові, і в разі відсутності прирівнюються до нуля.

Мітки часу зберігаються як кількість мілісекунд, що пройшли з початку 1970 року. Для часу до 1970 року використовуються негативні числа (це пов'язано з угодою по Unix time, яке було створено приблизно в той час). Метод getTime об'єкта дати повертає це число. Воно, звичайно, велике.
console.log (new Date (2013, 11, 19) .getTime ()); // → 1387407600000 console.log (new Date (1387407600000)); // → Thu Dec 19 2013 00:00:00 GMT + 0100 (CET)

Якщо задати конструктору Date один аргумент, він сприймається як ця кількість мілісекунд. Можна отримати поточне значення мілісекунд, створивши об'єкт Date і викликавши метод getTime, або ж викликавши функцію Date.now.

У об'єкта Date для вилучення його компонентів є методи getFullYear, getMonth, getDate, getHours, getMinutes, і getSeconds. Є також метод getYear, який повертає досить даремний двозначний код, типу 93 або 14.

Уклавши потрібні частини шаблону в круглі дужки, ми можемо створити об'єкт дати прямо з рядка.

Function findDate (string) (var dateTime \u003d / (\\ d (1,2)) - (\\ d (1,2)) - (\\ d (4)) /; var match \u003d dateTime.exec (string); return new Date (Number (match), Number (match) - 1, Number (match));) console.log (findDate ( "30-1-2003")); // → Thu Jan 30 2003 00:00:00 GMT + 0100 (CET)

Межі слова і рядки

На жаль, findDate так само радісно витягне безглузду дату 00-1-3000 з рядка «100-1-30000». Збіг може трапитися в будь-якому місці рядка, так що в даному випадку він просто почне з другого символу і закінчить на передостанньому.

Якщо нам треба примусити збіг взяти всю рядок цілком, ми використовуємо мітки ^ і $. ^ Збігається з початком рядка, а $ по всьому. Тому / ^ \\ d + $ / збігається з рядком, що складається тільки з однієї або декількох цифр, / ^! / Збігається зі сторокой, що починається зі знаку оклику, а / x ^ / не збігається з жодною рядком (перед початком рядка не може бути x).

Якщо, з іншого боку, нам просто треба переконатися, що дата починається і закінчується на кордоні слова, ми використовуємо мітку \\ b. Кордоном слова може бути початок або кінець рядка, або будь-яке місце рядка, де з одного боку стоїть алфавітно-цифровий символ \\ w, а з іншого - не алфавітно-цифровий.

Console.log (/cat/.test ( "concatenate")); // → true console.log (/ \\ bcat \\ b / .test ( "concatenate")); // → false

Відзначимо, що мітка кордону не представляє з себе символ. Це просто обмеження, що позначає, що збіг відбувається лише тоді, коли виконується певну умову.

Шаблони з вибором

Припустимо, треба з'ясувати, чи містить текст не просто номер, а номер, за яким слід pig, cow, або chicken в єдиному або множині.

Можна було б написати три регулярки і перевірити їх по черзі, але є спосіб краще. символ | позначає вибір між шаблонами зліва і праворуч від нього. І можна сказати наступне:

Var animalCount \u003d / \\ b \\ d + (pig | cow | chicken) s? \\ B /; console.log (animalCount.test ( "15 pigs")); // → true console.log (animalCount.test ( "15 pigchickens")); // → false

Дужки обмежують частину шаблону, до якої застосовується |, і можна поставити багато таких операторів один за одним, щоб позначити вибір з більш ніж двох варіантів.

механізм пошуку

Регулярні вирази можна розглядати як блок-схеми. Наступна діаграма описує останній тваринницький приклад.

Вираз збігається з рядком, якщо можна знайти шлях з лівій частині діаграми в праву. Ми запам'ятовуємо поточний стан в рядку, і кожен раз, проходячи прямокутник, перевіряємо, що частина рядки відразу за нашим становищем в ній збігається з вмістом прямокутника.

Значить, перевірка збігу нашої регулярки в рядку «the 3 pigs» при проходженні по блок-схемі виглядає так:

На позиції 4 є межа слова, і проходимо перший прямокутник
- починаючи з 4 позиції знаходимо цифру, і проходимо другий прямокутник
- на позиції 5 один шлях замикається тому перед другим прямокутником, а другий проходить далі до прямокутника з пропуском. У нас пропуск, а не цифра, і ми вибираємо другий шлях.
- тепер ми на позиції 6, початок "pigs", і на потрійному розгалуженні шляхів. У рядку немає "cow" або "chicken", зате є "pig", тому ми вибираємо цей шлях.
- на позиції 9 після потрійного розгалуження, один шлях обходить "s" і прямує до останнього прямокутника з кордоном слова, а другий проходить через "s". У нас є "s", тому ми йдемо туди.
- на позиції 10 ми в кінці рядка, і збігтися може тільки межа слова. Кінець рядка вважається кордоном, і ми проходимо через останній прямокутник. І ось ми успішно знайшли наш шаблон.

В принципі, працюють регулярні вирази в такий спосіб: алгоритм починає на початку рядка і намагається знайти збіг там. У нашому випадку там є межа слова, тому він проходить перший прямокутник - але там немає цифри, тому на другому прямокутнику він спотикається. Потім він рухається до другого символу в рядку, і намагається знайти збіг там ... І так далі, поки він не знаходить збіг або не доходить до кінця рядка, в якому випадку збіг на знайдено.

відкати

Регулярки / \\ b (+ b | \\ d + | [\\ da-f] h) \\ b / збігається або з двійковим числом, за яким слід b, або з десятковим числом без суфікса, або шістнадцятковим (цифри від 0 до 9 або символи від a до h), за яким йде h. Відповідна діаграма:

У пошуках збігу може трапитися, що алгоритм пішов по верхньому шляху (двійкове число), навіть якщо в рядку немає такого числа. Якщо там є рядок "103", наприклад, зрозуміло, що тільки досягнувши цифри 3 алгоритм зрозуміє, що він на неправильному шляху. Взагалі рядок збігається з регулярки, просто не в цій гілці.

Тоді алгоритм робить відкат. На розвилці він запам'ятовує поточний стан (в нашому випадку, це початок рядка, відразу після кордону слова), щоб можна було повернутися назад і спробувати інший шлях, якщо обраний не спрацьовує. Для рядка "103" після зустрічі з трійкою він повернеться і спробує пройти шлях для десяткових чисел. Це спрацює, тому збіг буде знайдено.

Алгоритм зупиняється, як тільки знайде повний збіг. Це означає, що навіть якщо кілька варіантів можуть підійти, використовується тільки один з них (в тому порядку, в якому вони з'являються в регулярке).

Відкати трапляються при використанні операторів повторення, таких, як + і *. Якщо ви шукаєте /^.*x/ в рядку «abcxe», частина регулярки. * Спробує поглинути всю рядок. Алгоритм потім зрозуміє, що йому потрібен ще й "x". Так що ніякого "x" після кінця рядка немає, алгоритм спробує пошукати збіг, відкотившись на один символ. Після abcx теж немає x, тоді він знову відкочується, вже до підрядку abc. І після рядки він знаходить x і доповідає про успішне збігу, на позиціях з 0 по 4.

Можна написати регулярку, яка приведе до множинним відкатів. Така проблема виникає, коли шаблон може збігтися з вхідними даними безліччю різних способів. Наприклад, якщо ми помилимося при написанні регулярки для двійкових чисел, ми можемо випадково написати щось на зразок / (+) + b /.

Якщо алгоритм буде шукати такий шаблон в довгій рядку з нулів і одиниць, що не містить в кінці "b", він спочатку пройде по внутрішній петлі, поки у нього не закінчаться цифри. Тоді він помітить, що в кінці немає "b", зробить відкат на одну позицію, пройде по зовнішньої петлі, знову здасться, спробує відкотитися на ще одну позицію по внутрішній петлі ... І буде далі шукати таким чином, задіюючи обидві петлі. Тобто, кількість роботи з кожним символом рядка буде подвоюватися. Навіть для кількох десятків символів пошук збігу займе дуже довгий час.

метод replace

У рядків є метод replace, який може замінювати частину рядка іншим рядком.

Console.log ( "тато" .replace ( "п", "м")); // → мапа

Перший аргумент може бути і регулярної, в якому випадку замінюється перше входження регулярки в рядку. Коли до регулярке додається опція "g" (global, загальний), замінюються всі входження, а не тільки перше

Console.log ( "Borobudur" .replace (//, "a")); // → Barobudur console.log ( "Borobudur" .replace (// g, "a")); // → Barabadar

Мало б сенс передавати опцію «замінити все» через окремий аргумент, або через окремий метод типу replaceAll. Але на жаль, опція передається через саму регулярку.

Вся сила регулярок розкривається, коли ми використовуємо посилання на знайдені в рядку групи, задані в регулярке. Наприклад, у нас є рядок, що містить імена людей, одне ім'я на рядок, у форматі «Прізвище, Ім'я». Якщо нам треба поміняти їх місцями і прибрати кому, щоб вийшло «Ім'я Прізвище», ми пишемо наступне:

Console.log ( "Hopper, Grace \\ nMcCarthy, John \\ nRitchie, Dennis" .replace (/ ([\\ w] +), ([\\ w] +) / g, "$ 2 $ 1")); // → Grace Hopper // John McCarthy // Dennis Ritchie

$ 1 і $ 2 в рядку на заміну посилаються на групи символів, укладені в дужки. $ 1 замінюється текстом, який збігся з першою групою, $ 2 - з другою групою, і так далі, до $ 9. Все збіг цілком міститься в змінній $ &.

Також можна в якості другого аргументу передавати і функцію. Для кожної заміни буде викликана функція, аргументами якої будуть знайдені групи (і вся збігається частина рядка цілком), а її результат буде вставлений в новий рядок.

Простий приклад:

Var s \u003d "the cia and fbi"; console.log (s.replace (/ \\ b (fbi | cia) \\ b / g, function (str) (return str.toUpperCase ();))); // → the CIA and FBI

А ось більш цікавий:

Var stock \u003d "1 lemon, 2 cabbages, and 101 eggs"; function minusOne (match, amount, unit) (amount \u003d Number (amount) - 1; if (amount \u003d\u003d 1) // залишився тільки один, видаляємо "s" в кінці unit \u003d unit.slice (0, unit.length - 1); else if (amount \u003d\u003d 0) amount \u003d "no"; return amount + "" + unit;) console.log (stock.replace (/ (\\ d +) (\\ w +) / g, minusOne)); // → no lemon, 1 cabbage, and 100 eggs

Код приймає рядок, знаходить все входження чисел, за якими йде слово, і повертає рядок, де кожне число зменшено на одиницю.

Група (\\ d +) потрапляє в аргумент amount, а (\\ w +) - в unit. Функція перетворює amount в число - і це завжди спрацьовує, тому що наш шаблон якраз \\ d +. І потім вносить зміни в слово, на випадок якщо залишився всього 1 предмет.

жадібність

Нескладно за допомогою replace написати функцію, що прибирає всі коментарі з коду JavaScript. Ось перша спроба:

Function stripComments (code) (return code.replace (/\\/\\/.* | \\ / \\ * [^] * \\ * \\ // g, "");) console.log (stripComments ( "1 + / * 2 * / 3 ")); // → 1 + 3 console.log (stripComments ( "x \u003d 10; // ten!")); // → x \u003d 10; console.log (stripComments ( "1 / * a * / + / * b * / 1")); // → 1 + 1

Частина перед оператором «або» збігається з двома Слеш, за якими йдуть будь-яку кількість символів, крім символів перекладу рядка. Частина, що прибирає багаторядкові коментарі, більш складна. Ми використовуємо [^], тобто будь-який символ, який не є порожнім, як спосіб знайти будь-який символ. Ми не можемо використовувати точку, тому що блокові коментарі тривають і на новому рядку, а символ перекладу рядка не збігається з точкою.

Але висновок попереднього прикладу неправильний. Чому?

Частина [^] * спочатку спробує захопити стільки символів, скільки може. Якщо через це наступна частина регулярки не знайдете собі збіги, відбудеться відкат на один символ і спробує знову. У прикладі, алгоритм намагається захопити всю рядок, і потім відкочується. Відкотившись на 4 символу назад, він знайде в рядку * / - а це не те, чого ми домагалися. Ми-то хотіли захопити тільки один коментар, а не пройти до кінця рядка і знайти останній коментар.

Через це ми говоримо, що оператори повторення (+, *,?, And ()) жадібні, тобто вони спочатку захоплюють, скільки можуть, а потім йдуть назад. Якщо ви помістіть питання після такого оператора (+ ?, * ?, ??, ()?), Вони перетворяться в нежадібних, і почнуть знаходити найменші з можливих входжень.

І це те, що нам потрібно. Змусивши зірочку знаходити збіги в мінімально можливій кількості символів рядки, ми поглинаємо тільки один блок коментарів, і не більше того.

Function stripComments (code) (return code.replace (/\\/\\/.* | \\ / \\ * [^] *? \\ * \\ // g, "");) console.log (stripComments ( "1 / * a * / + / * b * / 1 ")); // → 1 + 1

Безліч помилок виникає при використанні жодних операторів замість нежадібних. При використанні оператора повтору спочатку завжди розглядайте варіант нежадібні оператора.

Динамічне створення об'єктів RegExp

У деяких випадках точний шаблон невідомий під час написання коду. Наприклад, вам треба буде шукати ім'я користувача в тексті, і укладати його в підкреслення. Так як ви дізнаєтеся ім'я тільки після запуску програми, ви не можете використовувати запис зі Слеш.

Але ви можете побудувати рядок і використовувати конструктор RegExp. Ось приклад:

Var name \u003d "гаррі"; var text \u003d "А у Гаррі на лобі шрам."; var regexp \u003d new RegExp ( "\\\\ b (" + name + ") \\\\ b", "gi"); console.log (text.replace (regexp, "_ $ 1_")); // → А у _Гаррі_ на лобі шрам.

При створенні кордонів слова доводиться використовувати подвійні слеші, тому що ми пишемо їх в нормальній рядку, а не в регулярке з прямими Слеш. Другий аргумент для RegExp містить опції для регулярок - в нашому випадку "gi", тобто глобальний і регістру незалежний.

Але що, якщо ім'я буде «dea + hlrd» (якщо наш користувач - кульхацкер)? В результаті ми отримаємо безглузду регулярку, що не знайде в рядку збігів.

Ми можемо додати зворотних слешів перед будь-яким символом, який нам не подобається. Ми не можемо додавати зворотні слеші перед буквами, тому що \\ b або \\ n - це спецсимволи. Але додавати слеші перед будь-якими не алфавітно-цифровими символами можна без проблем.

Var name \u003d "dea + hlrd"; var text \u003d "Цей dea + hlrd всіх дістав."; var escaped \u003d name.replace (/ [^ \\ w \\ s] / g, "\\\\ $ &"); var regexp \u003d new RegExp ( "\\\\ b (" + escaped + ") \\\\ b", "gi"); console.log (text.replace (regexp, "_ $ 1_")); // → Цей _dea + hlrd_ всіх дістав.

метод search

Метод indexOf не можна використовувати з регулярки. Зате є метод search, який якраз очікує регулярку. Як і indexOf, він повертає індекс першого входження, або -1, якщо його не сталося.

Console.log ( "word" .search (/ \\ S /)); // → 2 console.log ( "" .search (/ \\ S /)); // → -1

На жаль, ніяк не можна задати, щоб метод шукав збіг, починаючи з конкретного зміщення (як це можна зробити з indexOf). Це було б корисно.

властивість lastIndex

Метод exec теж не дає зручного способу почати пошук із заданою позиції в рядку. Але незручний спосіб дає.

У об'єкта регулярок є властивості. Одне з них - source, що містить рядок. Ще одне - lastIndex, що контролює, в деяких умовах, де почнеться наступний пошук входжень.

Ці умови включають необхідність присутності глобальної опції g, і те, що пошук повинен йти з застосуванням методу exec. Більш розумним рішенням було б просто допустити додатковий аргумент для передачі в exec, але розумність - основна риса в інтерфейсі регулярок JavaScript.

Var pattern \u003d / y / g; pattern.lastIndex \u003d 3; var match \u003d pattern.exec ( "xyzzy"); console.log (match.index); // → 4 console.log (pattern.lastIndex); // → 5

Якщо пошук був успішним, виклик exec оновлює властивість lastIndex, щоб воно вказувало на позицію після знайденого входження. Якщо успіху не було, lastIndex встановлюється в нуль - як і lastIndex у щойно створеного об'єкта.

При використанні глобальної змінної-регулярки і декількох викликів exec ці автоматичні оновлення lastIndex можуть привести до проблем. Ваша регулярка може почати пошук з позиції, що залишилася з попереднього виклику.

Var digit \u003d / \\ d / g; console.log (digit.exec ( "here it is: 1")); // → [ "1"] console.log (digit.exec ( "and now: 1")); // → null

Ще один цікавий ефект опції g в тому, що вона змінює роботу методу match. Коли він викликається з цією опцією, замість повернення масиву, схожого на результат роботи exec, він знаходить все входження шаблону в рядку і повертає масив з знайдених подстрок.

Console.log ( "Банан" .match (/ ан / g)); // → [ "ан", "ан"]

Так що обережніше з глобальними змінними-регулярки. У випадках, коли вони необхідні - виклики replace або місця, де ви спеціально використовуєте lastIndex - мабуть і все випадки, в яких їх слід застосовувати.

Цикли по входженням

Типова задача - пройти по всім входженням шаблону в рядок так, щоб мати доступ до об'єкта match в тілі циклу, використовуючи lastIndex і exec.

Var input \u003d "Рядок з 3 числами в ній ... 42 і 88."; var number \u003d / \\ b (\\ d +) \\ b / g; var match; while (match \u003d number.exec (input)) console.log ( "Знайшов", match, "на", match.index); // → Знайшов 3 на 14 // Знайшов 42 на 33 // Знайшов 88 на 40

Використовується той факт, що значенням присвоєння є привласнюється значення. Використовуючи конструкцію match \u003d re.exec (input) в якості умови в циклі while, ми проводимо пошук на початку кожної ітерації, зберігаємо результат у змінній, і закінчуємо цикл, коли всі збіги знайдені.

Розбір INI файли

На закінчення глави розглянемо задачу з використанням регулярок. Уявіть, що ми пишемо програму, котра збирає відомості про наших ворогів через інтернет в автоматичному режимі. (Всю програму писати не будемо, лише ту частину, яка читає файл з настройками. Вибачте.) Файл виглядає так:

Searchengine \u003d http: //www.google.com/search? Q \u003d $ 1 spitefulness \u003d 9.7; перед коментарями ставиться крапка з комою; кожна секція відноситься до окремого ворогові fullname \u003d Larry Doe type \u003d бичара з дитсадка website \u003d http: //www.geocities.com/CapeCanaveral/11451 fullname \u003d Gargamel type \u003d злий чарівник outputdir \u003d / home / marijn / enemies / gargamel

Точний формат файлу (який досить широко використовується, і зазвичай називається INI), наступний:

Порожні рядки і рядки, що починаються з крапки з комою, ігноруються
- рядки, ув'язнені в квадратні дужки, починають нову секцію
- рядки, що містять алфавітно-цифровий ідентифікатор, за яким слід \u003d, додають настройку в даній секції

Все інше - невірні дані.

Наше завдання - перетворити такий рядок в масив об'єктів, кожен з властивістю name і масивом налаштувань. Для кожної секції потрібен один об'єкт, і ще один - для загальних параметрів зверху файлу.

Так як файл треба розбирати через підрядник, непогано почати з розбиття файлу на рядки. Для цього в розділі 6 ми використовували string.split ( "\\ n"). Деякі операційки використовують для перекладу рядка не один символ \\ n, а два - \\ r \\ n. Так як метод split приймає регулярки як аргумент, ми можемо ділити лінії за допомогою виразу / \\ r? \\ N /, який дозволить і поодинокі \\ n і \\ r \\ n між рядками.

Function parseINI (string) (// Почнемо з об'єкта, що містить налаштування верхнього рівня var currentSection \u003d (name: , fields:); var categories \u003d; string.split (/ \\ r? \\ N /). ForEach (function (line ) (var match; if (/^\\s*(;.*)?$/.test(line)) (return;) else if (match \u003d line.match (/^\\[(.*)\\]$ /)) (currentSection \u003d (name: match, fields:); categories.push (currentSection);) else if (match \u003d line.match (/ ^ (\\ w +) \u003d (. *) $ /)) (currentSection. fields.push ((name: match, value: match));) else (throw new Error ( "Рядок" "+ line +" "містить невірні дані.");))); return categories;)

Код проходить всі рядки, оновлюючи об'єкт поточної секції "current section". Спочатку він перевіряє, чи можна ігнорувати рядок, за допомогою регулярки /^\\s*(;.*)?$/. Мислите, як це працює? Частина між дужок збігається з коментарями, а? робить так, що регулярка співпаде і з рядками, що складаються з одних прогалин.

Якщо рядок - НЕ коментар, код перевіряє, починає вона нову секцію. Якщо так, він створює новий об'єкт для поточної секції, до якої додаються наступні настройки.

Остання осмислена можливість - рядок є звичайною налаштуванням, і в цьому випадку вона додається до поточного об'єкту.

Якщо жоден варіант не спрацював, функція видає помилку.

Зауважте, як часте використання ^ і $ піклується про те, що вираз збігається з усією рядком цілком, а не з частиною. Якщо їх не використовувати, код в цілому буде працювати, але іноді видаватиме дивні результати, і таку помилку буде важко відстежити.

Конструкція if (match \u003d string.match (...)) схожа на трюк, який використовує привласнення як умова в циклі while. Часто ви не знаєте, що виклик match буде успішним, тому ви можете отримати доступ до результуючому об'єкту тільки всередині блоку if, який це перевіряє. Щоб не розбивати красиву ланцюжок перевірок if, ми присвоюємо результат пошуку змінної, і відразу використовуємо це привласнення як перевірку.

міжнародні символи

Через спочатку простий реалізації мови, і подальшої фіксації такої реалізації «в граніті», регулярки JavaScript туплять з символами, що не зустрічаються в англійській мові. Наприклад, символ «букви» з точки зору регулярок JavaScript, може бути одним з 26 букв англійського алфавіту, і чомусь ще підкресленням. Букви типу é або β, однозначно є літерами, не збігаються з \\ w (і співпадуть з \\ W, тобто з не-буквою).

За дивним збігом обставин, історично \\ s (пропуск) збігається з усіма символами, які в Unicode вважаються пробільними, включаючи такі штуки, як нерозривний пробіл або монгольський роздільник голосних.

У деяких реалізацій регулярок в інших мовах є особливий синтаксис для пошуку спеціальних категорій символів Unicode, типу «все прописні букви», «все знаки пунктуації» або «керуючі символи». Є плани по додаванню таких категорій і в JavaScript, але вони, мабуть, будуть реалізовані не скоро.

підсумок

Регулярки - це об'єкти, що представляють шаблони пошуку в рядках. Вони використовують свій синтаксис для вираження цих шаблонів.

/ Abc / Послідовність символів
// Будь-який символ зі списку
/ [^ Abc] / Будь-який символ, крім символів зі списку
// Будь-який символ з проміжку
/ X + / Одне або більше входжень шаблону x
/ X +? / Одне або більше входжень, нежадібні
/ X * / Нуль або більше входжень
/ X? / Нуль або одне входження
/ X (2,4) / Від двох до чотирьох входжень
/ (Abc) / Група
/ A | b | c / Будь-який з декількох шаблонів
/ \\ D / Будь-яка цифра
/ \\ W / Будь алфавітно-цифровий символ ( «буква»)
/ \\ S / Будь-символ пробілу
/./ Будь-який символ, крім переказів рядка
/ \\ B / Кордон слова
/ ^ / Початок рядка
/ $ / Кінець рядка

У регулярки є метод test, для перевірки того, чи є шаблон в рядку. Є метод exec, який повертає масив, що містить всі знайдені групи. У масиву є властивість index, де міститься номер символу, з якого сталося збіг.

У рядків є метод match для пошуку шаблонів, і метод search, який повертає тільки початкову позицію входження. Метод replace може заміняти входження шаблону на інший рядок. Крім цього, ви можете передати в replace функцію, яка буде будувати рядок на заміну, грунтуючись на шаблоні і знайдених групах.

У регулярок є налаштування, які пишуть після закриває слеша. Опція i робить регулярку регістронезавісімого, а опція g робить її глобальної, що, крім іншого, змушує метод replace замінювати всі знайдені входження, а не тільки перше.

Конструктор RegExp можна використовувати для створення регулярок з рядків.

Регулярки - гострий інструмент з незручною ручкою. Вони сильно спрощують одні завдання, і можуть стати некерованими при вирішенні інших, складних завдань. Частина вміння користуватися регулярки полягає в тому, щоб уміти чинити опір спокусі запхати в них завдання, для якої вони не призначені.

вправи

Неминуче при вирішенні задач у вас виникнуть незрозумілі випадки, і ви можете іноді впадати у відчай, бачачи непередбачувана поведінка деяких регулярок. Іноді допомагає вивчити поведінку регулярки через онлайн-сервіс типу debuggex.com, де можна подивитися її візуалізацію і порівняти з бажаним ефектом.
Регулярний гольф
«Гольфом» в коді називають гру, де потрібно висловити задану програму мінімальною кількістю символів. Регулярний гольф - практична вправа з написання найменших можливих регулярок для пошуку заданого шаблону, і тільки його.

Для кожної з подстрочек напишіть регулярку для перевірки їх знаходження в рядку. Регулярки повинна знаходити тільки ці зазначені підрядка. Не хвилюйтеся щодо меж слів, якщо це не згадано особливо. Коли у вас вийде працює регулярка, спробуйте її зменшити.

Car і cat
- pop і prop
- ferret, ferry, і ferrari
- Будь-яке слово, що закінчується на ious
- Пропуск, за яким йде точка, кома, двокрапка або крапка з комою.
- Слово довше шести букв
- Слово без букв e

// Впишіть свої регулярки verify (/.../, [ "my car", "bad cats"], [ "camper", "high art"]); verify (/.../, [ "pop culture", "mad props"], [ "plop"]); verify (/.../, [ "ferret", "ferry", "ferrari"], [ "ferrum", "transfer A"]); verify (/.../, [ "how delicious", "spacious room"], [ "ruinous", "consciousness"]); verify (/.../, [ "bad punctuation."], [ "escape the dot"]); verify (/.../, [ "hottentottententen"], [ "no", "hotten totten tenten"]); verify (/.../, [ "red platypus", "wobbling nest"], [ "earth bed", "learning ape"]); function verify (regexp, yes, no) (// Ignore unfinished exercises if (regexp.source \u003d\u003d "...") return; yes.forEach (function (s) (if (! regexp.test (s)) console .log ( "Не знайшлося" "+ s +" "");)); no.forEach (function (s) (if (regexp.test (s)) console.log ( "Несподіване входження" "+ s +" "");));)

Лапки в тексті
Припустимо, ви написали розповідь, і всюди для позначення діалогів використовували одинарні лапки. Тепер ви хочете замінити лапки діалогів на подвійні, і залишити одинарні в скороченнях слів типу are not.

Придумайте шаблон, що розрізняє два цих використання лапок, і напишіть виклик методу replace, який робить заміну.

знову числа
Послідовності цифр можна знайти простий регулярки / \\ d + /.

Напишіть вираз, що знаходить тільки числа, записані в стилі JavaScript. Воно повинно підтримувати можливий мінус або плюс перед числом, десяткову точку, і експонентну запис 5e-3 або 1E10 - знову-таки з можливими плюсом чи мінусом. Також зауважте, що до або після точки не обов'язково можуть стояти цифри, але при цьому число не може складатися з однієї точки. Тобто, .5 або 5. - допустимі числа, а одна точка сама по собі - ні.

// Впишіть сюди регулярку. var number \u003d /^...$/; // Tests: [ "1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4", "1e + 12"] .forEach (function (s) (if (! number.test (s)) console.log ( "Не знайшла" "+ s +" "");)); [ "1a", "+ 1", "1.2.3", "1 + 1", "1e4.5", ".5.", "1f5", "."]. ForEach (function (s) (if (number.test (s)) console.log ( "Неправильно прийнято" "+ s +" "");));