Глава 30. Регулярні вирази :: Шаблони пошуку

  1. Символи і метасимволу
  2. Символьні класи
  3. Анкери (прив'язки)
  4. квантіфікатори
  5. Угруповання і захоплення
  6. Зворотні посилання
  7. Чутливість до регістру

Наведемо кілька прикладів регулярних виразів.

  • карова - очевидно, шаблон, під який підходить слово карова;

  • \ B (shift | unshift | pop | push | splice) \ b - будь-яка з перерахованих слів;

  • ^ \ S + - один або кілька пробілів чи знаків табуляції, що стоять на початку рядка.

Символи і метасимволу

У регулярних виразах алфавітно-цифрові символи зазвичай позначають самі себе. Наприклад, шаблон Hello вказує шукати символ H, за яким слід e, потім l і т. Д.

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

Є й інші символи, які в шаблонах отримують особливий сенс замість того, щоб позначати самих себе. Такі символи називаються метасимвол. Наведемо кілька прикладів метасимволов, що не вказуючи поки їх особливий сенс (список не є вичерпним): \ .- [] () {}? * + ^ $ |.

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

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

Символьні класи

Шаблон [abcdef] позначає один із символів, перерахованих в квадратних дужках. Якщо, наприклад, нас цікавить слово Hello, неважливо, з великою або маленькою літери, шаблон буде таким: [Hh] ello. Ось шаблон, що позначає маленьку голосну букву англійського алфавіту: [aeiouy]. Ще один приклад - символьний клас, що складається з обох квадратних дужок: [\ [\]].

Якщо символьний клас включає символи, що йдуть підряд в кодової таблиці, досить вказати перший і останній символи, вставивши між ними дефіс. Наприклад, клас, що позначає будь-яку десяткову цифру, можна задати як [0-9]. Буква англійського алфавіту позначається як [A-Za-z] (тут ми покладаємося на той факт, що в будь-який кодової таблиці заголовні і маленькі англійські букви йдуть безперервними блоками в алфавітному порядку, а проте блок маленьких букв не слід відразу за блоком заголовних).

Можна визначити символьний клас, що складається з усіх символів за винятком перерахованих - так зване заперечення символьного класу. Для цього відразу за відкриває квадратної дужкою перед перерахуванням вставляється знак циркумфлекс ^. Будь-який символ, який не є цифрою, можна позначити як [^ 0-9].

Для деяких популярних класів символів є спеціальні позначення:

Анкери (прив'язки)

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

Найбільш часто використовувані анкери - прив'язка до початку (^) і кінця ($) рядки. Прив'язка до початку рядка повинна міститися на початку шаблону, а прив'язка до кінця - в кінці.

Наприклад, до шаблону ^ анти підходять такі слова російської мови, як антидот, антисемітизм або античастинка. Без прив'язки також підійшли б рядки, які не начанающіеся з «анти-», але містять це буквосполучення всередині, наприклад, меркантилізм. Для пошуку слів, що закінчуються на «ться» потрібен шаблон ться $ (ми впевнені майже на 100%, що всі такі слова - це зворотні дієслова в інфінітиві). Ніщо не заважає застосовувати в шаблоні обидві ці прив'язки.

Інший корисний анкер - прив'язка до кордону між словами \ b. Він відповідає місцю в рядку, що знаходиться між символами, один з яких належить класу \ w, а інший - \ W (в будь-якому порядку). Ця прив'язка може відповідати також початку або кінця рядка (в цьому випадку вважається, що терміну як би оточена уявними символами з класу \ W).

Якщо ми шукаємо фрагмент, який повинен підійти під один з декількох шаблонів, потрібно перерахувати ці шаблони, розділивши вертикальної рисою |. Наприклад, понедельник | неділя | Середовище | неділя | Вівторок | вівторок | неділю. Для того, щоб зробити список альтернатив самостійною одиницею і відокремити від сусідніх, його потрібно укласти в дужки. Наприклад, шаблон Шануймо (ий | а) означає рядок Шануймо, за якою слідує одна з рядків ий або ая. Без дужок шаблон Шановний | а позначав би одну з рядків Шановний або ая. У дужок є важливе побічна дія, про який буде сказано в розділі «Угруповання і захоплення» .

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

Для того, щоб вказати, скільки разів може повторюватися шаблон, після нього ставлять так звані квантіфікатори (від латинського слова quantum - скільки):

Квантіфікатори *, + і? є надлишковими, оскільки вони можуть бути виражені інакше за допомогою фігурних дужок. А саме, * рівносильний {0,}, + рівносильний {1,}, а? - те ж саме, що і {0,1}. Але дані квантіфікатори дуже часто використовуються, і цим заслужили окремих позначень.

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

Ось кілька прикладів:

  • ^ \ D + $ - послідовність з однієї або декількох десяткових цифр (шаблон для цілих невід'ємних чисел в десяткового запису);

  • ^ \ -? \ D + $ - те ж саме, але для всіх (можливо, негативних) цілих чисел;

  • ^ \ -? (\ D + (\. \ D *)? | \. \ D +) $ - шаблон для дійсних чисел;

Розберемо останній приклад докладніше. Крім необов'язкового мінуса на початку, в шаблоні присутня група з двома альтернативами: \ d + (\. \ D *)? і \. \ d +. Перша альтернатива включає обов'язкову цілу частину \ d + (як мінімум одна цифра), і наступну за нею необов'язкову дробову (\. \ D *) ?. У дробової частини, якщо вона є, є десяткова точка, і, можливо, кілька цифр. Таким чином, цю альтернативу відповідають рядки 15, 15., 15.487. Інша альтернатива потрібна для рядків виду .618 з відсутньою цілою частиною - в багатьох комп'ютерних мовах цей запис має право на існування.

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

Примітка

Було б помилкою спробувати об'єднати ці альтернативи: \ d * \. \ D *. Тоді самотня точка як запис числа з нульовою цілою і дробовою частиною була б законною. Між іншим, у вбудованому мові програмування Basic старовинного радянського мікрокомп'ютера МК-85 було можливо використовувати точку для позначення числа нуль.

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

Якщо найпростіші елементи регулярного виразу - символи, символьні класи і анкери, записуються підряд, це означає, що при пошуку в рядку за шаблоном ці елементи будуть зіставлятися з частинами рядка послідовно, в тій же послідовності. Цей порядок порушується, якщо застосовуються альтернативи. Можна уявляти собі, що складене регулярний вираз складається з найпростіших за допомогою двох операцій: послідовного з'єднання (композиції) і альтернативи. Композиція - аналог операції множення в арифметиці. Альтернатива - аналог складання. Перше схожість з арифметикою полягає в тому, що операція альтернативи має більш низький пріоритет, ніж композиція, тому можуть знадобитися дужки для угруповання, як в прикладі Шануймо (ий | а).

Перше схожість з арифметикою полягає в тому, що операція альтернативи має більш низький пріоритет, ніж композиція, тому можуть знадобитися дужки для угруповання, як в прикладі Шануймо (ий | а)

Примітка

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

коммутативность альтернативи x | y = y | x; асоціативність альтернативи x | y | z = x | y | z; асоціативність композиції x ⁣ y ⁣ z = x ⁣ y ⁣ z; дистрибутивность альтернативи щодо композиції (ліва і права) x ⁣ y | z = x ⁣ y | x ⁣ z, x | y ⁣ z = x ⁣ z | y ⁣ z.

У цій дивній арифметиці регулярних виразів не має місце закон коммутативности для композиції. Крім того, відсутня аналог нуля через очевидного співвідношення x | x = x. Роль одиниці (правої і лівої) для композиції виконує порожній шаблон (позначимо його 𝟙): 𝟙 ⁣ x = x ⁣ 𝟙 = x. Квантіфікатори виду {n} грають роль зведення в n -у ступінь.

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

Розглянемо приклад, в якому в тексті відшукуються згадки різних кислот. Наші шкільні спогади з хімії навели нас на думку, що назви кислот закінчуються або на вая, або на ная, або на тая, а потім, після пропуску, слід слово кислота. Складаємо шаблон: \ S + [ВНТ] ая кислота. Порівнюємо з шаблоном текст. Удача! Але, питається, згадка який саме кислоти знайшлося в тексті? Соляний? Сірчаної? Азотної? Плавиковою? Хлорного? Хлорноватої? Хлорнуватисту? Лимонної? Синильної? Дезоксирибонуклеїнової?

Ось тут стане в нагоді захоплення. Ту частину шаблону, який, за нашим задумом, повинен відповідати назві, укладемо в дужки: (\ S + [ВНТ] а) кислота. Тоді машина, знайшовши в тексті згадка кислоти, збереже її назва (то, що відповідає укладеним в дужки фрагменту шаблону) в спеціальній змінній - буфері захоплення.

Регулярний вираз може містити кілька груп захоплення. Такі групи можуть не тільки слідувати один за одним, але і вкладатися одна в іншу. Іншими словами, регулярний вираз має бути збалансовано по відношенню до круглих дужках в тому ж сенсі, який обговорювався в главі 23. «Перевірка балансу дужок» (Звичайно, це відноситься тільки до круглих дужках, службовцям мети угруповання і захоплення; дужки, яким передує бекслеш, не позначаються на балансі груп). При успішному пошуку кожна група захопить якусь частину тексту: перша - в перший буфер, друга - в другій, і так далі. Як же нумеруються групи в разі, коли вони вкладені одна в одну? Нумерація йде в тому порядку, в якому з'являються відкривають дужки:

2 4 5 ┝┑ ┝┑┝┑

(() (() ())) │ ┝━━━━┙│ │ 3 │ ┝━━━━━━━━┙ 1

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

Використання цифрових груп захоплення не завжди зручно, особливо в великих регулярних виразах. Варто тільки вставити в шаблон нову групу захоплення, як нумерація збивається. Тоді доведеться у всіх місцях в програмі, де відбувається звернення до буферам захоплення за номерами, вносити виправлення. Але можна пов'язати з групою ім'я, яке дозволить звернутися до відповідного буферу з цього імені. Для створення іменованої групи використовуються обмежувачі (? <Name> ⋯), де замість name підставляється потрібне ім'я.

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

Зворотні посилання

Розглянемо задачу пошуку слів, що містять три однакові голосних букви підряд. Наївне рішення [аеёіоуеюя] {3}, що використовує квантіфікатори, не працюватиме, оскільки таким шаблоном відповідають рядки з трьома поспіль йдуть голосними, але необов'язково однаковими. Дивовижне рішення з повним перерахуванням альтернатив, ааа | еее | yoёё | ІІІ | ооо | ууу | еее | ююю | яяя, ми з обуренням відкидаємо: адже варто взяти інший, більш великий символьний клас, або замінити трійку в квантіфікатор на більше значення, як розмір шаблону катастрофічно виросте.

При цьому може бути елегантне рішення, яке використовує групи захоплення. Захопимо голосну в групу, а потім пошлемося на вміст буфера захоплення. Посилання на перший, другий, третій буфери записуються в регулярному виразі як \ g1, \ g2, \ g3. Отже, рішенням буде шаблон ([аеёіоуеюя]) \ g1 {2}. Зверніть увагу, що посилання на буфер захоплення повинна слідувати в регулярному виразі строго після відповідної групи.

Зворотні посилання можуть посилатися не тільки на нумеровані буфери, а й на іменовані. Такі посилання мають вигляд \ k <name>, де, знову ж таки, замість name варто конкретне ім'я. Наш приклад можна переписати, застосовуючи іменовані групи: (? <Vowel> [аеёіоуеюя]) \ k <vowel> {2} (vowel - голосна).

Чутливість до регістру

Іноді виникає необхідність в пошуку, при якому не робиться відмінностей між малими та великими літерами. Такий пошук називається нечуствітельним до регістру (case-insensitive). Замість того, щоб усюди в шаблоні замінювати літери на дволітерні класи (a → [Aa], b → [Bb], ...), Просто укладемо шаблон в спеціальну групу, що включає режим case-insensitive пошуку: (? I: ⋯). Така група не є групою захоплення. Якщо case-insensitive пошук повинен бути реалізований тільки в частині регулярного виразу, в групу слід помістити тільки потрібну частину.

Навпаки, якщо якась частина регулярного виразу, в якій здійснюється case-insensitive пошук, потребує відключення цього Режіна, то повернутися до звичайного, case-sensitive пошуку можна, використовуючи групу (? -I: ⋯).

Режими чутливості / нечуствітельності до регістру впливають лише на букви. Що вважається буквою, а що ні, залежить від мови, як і правила соответсвия між великими та малими літерами. З точки зору англійської мови, наприклад, буквою не є символ Щ. У німецькій мові є буква ß (між іншим, заголовний варіант цієї букви складається з двох букв SS: Carl Friedrich Gauß → CARL FRIEDRICH GAUSS).

«Ненажерливість» шаблонів

А саме, * рівносильний {0,}, + рівносильний {1,}, а?
D + (\. \ D *)?
Крім необов'язкового мінуса на початку, в шаблоні присутня група з двома альтернативами: \ d + (\. \ D *)?
Перша альтернатива включає обов'язкову цілу частину \ d + (як мінімум одна цифра), і наступну за нею необов'язкову дробову (\. \ D *) ?
Головним результатом зіставлення рядки з шаблоном є відповідь на питання: чи підходить рядок під шаблон?
Але, питається, згадка який саме кислоти знайшлося в тексті?
Соляний?
Сірчаної?
Азотної?
Плавиковою?