# Формальні вирази

    У цьому додатку наведено короткий, але, як сподіваються автори, достатній і інформативний вступ до світу формальних виразів. Тут наведено документацію щодо формальних виразів у тому вигляді, у якому вони використовуються у &kappname;, і який не є сумісним ні з формальними виразами у perl, ні з формальними виразами, наприклад, у grep.

## Вступ

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

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

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

За допомогою формальних виразів завдання з пошуку виконується в одну дію і з вищим рівнем точності.

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

`^[ \t]{0,4}(Іван|Франко) говори(ть|в)`

У наведеному вище прикладі продемонстровано всі чотири компоненти сучасних формальних виразів, а саме:

- Шаблони

- Умовні вирази

- Лічильники

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

Символ каретки (`^`) на початку виразу є умовним виразом, який визначає, що наступний рядок має розпочинати рядок у тексті.

Рядки `[ \t]` і `(Іван|Франко) говори(ть|в)` є шаблонами. Перший з них визначає клас символів, до якого належить пробіл або символ горизонтальної табуляції; у другому шаблоні міститься спочатку підшаблон, який визначає рядок `Іван` або `Франко`, потім рядка `говори` і нарешті підшаблону, що визначає закінчення: `ть` або `ив`

Рядок `{0,4}` є лічильником, який повідомляє інструментові пошуку, що рядок знаходиться будь-де, за від 0 до 4 рядками, вказаними раніше.

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

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

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

## Шаблони

Шаблони складаються з рядків і класів символів. У шаблонах можуть міститися підшаблони, тобто шаблони, взяті у круглі дужки.

### Символи керування

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

Це завдання можна виконати за допомогою додавання перед символом зворотної навскісної риски (`\`).

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

Екранування, звичайно ж, можна застосовувати і до самого символу зворотної навскісної риски, щоб програма сприймала введену вами зворотну навскісну риску як зворотну навскісну риску, вам слід ввести `\\`.

### Класи символів і абревіатури

Клас символів — це вираз, якому відповідає один символ з вказаного набору символів. У формальних виразах, класи символів визначаються зазначенням дозволених символів класу у квадратних дужках, `[]`, або за допомогою абревіатур класів, які описано нижче.

Прості класи символів містять один або декілька буквених символів, наприклад, `[abc]` (це означає одну з літер: a, b або c) або `[0123456789]` (цей клас визначає одну цифру).

Оскільки для літер і цифр характерний певний порядок (абетка або порядок цифр), ви можете скорочувати запис за допомогою діапазонів: `[a-c]` рівнозначний до `[abc]`, а `[0-9]` — до `[0123456789]`. Можна також використовувати комбінації таких конструкцій, наприклад, `[a-fynot1-38]` (звичайно ж, остання комбінація відповідає одному з символів a,b,c,d, e,f,y,n,o,t, 1,2,3 або 8).

Оскільки великі літери відрізняються від своїх малих еквівалентів, для створення класу символів, у якому б регістр не мав значення, з літер a і b, вам слід записати `[aAbB]`.

Звичайно ж можна створити і негативний клас, до якого б входили всі літери окрім вказаних. Для запису такого класу вам слід скористатися символом каретки (`^`) на початку запису класу:

Набір `[^abc]` відповідає будь-якому символу, окрім a, b і c.

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

`\a`

- Цей рядок відповідає символу дзвінка у ASCII (BEL, 0x07).

`\f`

- Цей рядок відповідає символу зміни сторінки у ASCII (FF, 0x0C).

`\n`

- Цей рядок відповідає символу зміни рядка у ASCII (LF, 0x0A, символ нового рядка у Unix).

`\r`

- Цей рядок відповідає символу повернення каретки у ASCII (CR, 0x0D).

`\t`

- Цей рядок відповідає символу горизонтальної табуляції у ASCII (HT, 0x09).

`\v`

- Цей рядок відповідає символу вертикальної табуляції у ASCII (VT, 0x0B).

`\xhhhh`

- Такий рядок відповідає символу Unicode з шістнадцятковим номером hhhh (у межах від 0x0000 до 0xFFFF). \0ooo (тобто \нульo ooo) відповідає символу з ASCII/Latin-1 з вісімковим номером ooo (у межах від 0 до 0377).

`.` (крапка)

- Цей рядок відповідає будь-якому символу (зокрема символу нового рядка).

`\d`

- Цей рядок відповідає будь-якій цифрі. Він рівнозначний до `[0-9]`

`\D`

- Цей рядок відповідає будь-якому символу, окрім цифри. Він рівнозначний до `[^0-9]` або `[^\d]`

`\s`

- Цей рядок відповідає символу проміжку між символами. З практичної точки зору, він рівнозначний до `[ \t\n\r]`

`\S`

- Цей рядок відповідає символу, який не є пробілом. З практичної точки зору, цей рядок еквівалентний до `[^ \t\r\n]` або `[^\s]`

`\w`

- Цей рядок відповідає будь-якому символу слова — у нашому випадку, літері, цифрі або символу підкреслювання. Цей рядок еквівалентний до рядка `[a-zA-Z0-9_]`

`\W`

- Цей рядок відповідає будь-якому символу, який не є символом запису слів, тобто будь-якому символу, який не є літерою, цифрою або символом підкреслювання. Рядок еквівалентний до рядка `[^a-zA-Z0-9_]` або `[^\w]`

Передбачено також підтримку позначення класів POSIX — `[:<назва класу>:]`. Наприклад, `[:digit:]` є тим самим, що і `\d`, а `[:space:]` — тим самим, що і `\s`. Із повним списком класів символів POSIX можна ознайомитися тут.

Класи-скорочення можна використовувати в межах загальних класів, наприклад, щоб знайти літеру слова, символ пробілу або крапку, ви можете скористатися записом `[\w \.]`

#### Символи з особливим призначенням у класах символів

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

`]`

- Завершує визначення класу символів. Цей символ слід екранувати, якщо він не є найпершим символом у класі (можна використовувати одразу за неекранованим символом каретки).

`^` (знак вставки)

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

`-` (дефіс)

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

`\` (зворотна навскісна риска)

- Символ керівної послідовності. Цей символ завжди слід екранувати, якщо він потрібен вам як символ зворотної навскісної риски.

### Варіанти: пошук одного з

Якщо вам потрібно знайти один з декількох варіантів шаблонів, ви можете відокремити ці шаблони символом `|` (вертикальною рискою).

Наприклад, щоб знайти один з рядків, Іван або Юрій, вам слід скористатися виразом `Іван|Юрій`.

### Підшаблони

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

#### Визначення варіантів

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

Наприклад, для того, щоб знайти одне зі слів int, float або double, ви можете скористатися шаблоном `int|float|double`. Якщо вам потрібно знайти одне з цих слів, і вам відомо, що за цим словом має йти пробіл, а потім якісь літери, згрупуйте варіанти у підшаблон: `(int|float|double)\s+\w+`.

#### Збереження знайденого тексту (зворотні посилання)

Якщо ви хочете скористатися зворотним посиланням, скористайтеся рядком `(ШАБЛОН)` для запам'ятовування бажаної частини рядка. Щоб запобігти запам'ятовуванню шаблона, скористайтеся групуванням без захоплення вмісту `(?:ШАБЛОН)`.

Наприклад, якщо вам потрібно знайти два однакових слова, відокремлені комою і, можливо, пробільними символами, ви можете скористатися формальним виразом `(\w+),\s*\1`. За допомогою підшаблону `\w+` буде знайдено послідовність символів слів, збіг з формальним виразом буде зареєстровано, якщо за цією послідовністю буде кома, 0 або більше пробільних символів і така сама послідовність символів слів. (Рядок `\1` відповідає першому підшаблону, взятому у круглі дужки)

Щоб уникнути двозначностей, пов’язаних із використанням `\1` з якимись цифрами за ним (наприклад `\12` може означати 12 підшаблон або просто перший підшаблон із наступним `2`) ми використовуємо синтаксичну конструкцію `\{12}` для багатоцифрових номерів підшаблонів.

Приклади:

- `\{12}1` — це використати підшаблон 12

- `\123` — це використати підшаблон 1, потім 23 як звичайний текст

#### Умовні вирази з перевіркою

Умовний вираз з перевіркою — це підшаблон, що починається з символів `?=` або `?!`.

Наприклад, для того, щоб знайти рядок Білл, за яким не буде рядка Гейтс, можна скористатися таким виразом: `Bill(?! Gates)`. (Таким чином буде знайдено послідовності Білл Клінтон і Біллі Кід, але проігноровано всі інші варіанти.)

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

Див. також Умовні вирази.

#### Умовні вирази з перевіркою і пошуком назад

Умовний вираз з перевіркою і пошуком назад — це підшаблон, що починається з символів `?<=` або `?<!`.

Пошук назад працює так само, як і пошук вперед, але пошук виконується у протилежному напрямку. Наприклад, знайти рядок команда, але лише якщо перед ним немає рядка Зе, ви можете скористатися таким виразом: `(?<!Зе)команда`.

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

Див. також Умовні вирази

### Символи зі спеціальним значення у шаблонах

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

`\` (зворотна навскісна риска)

- Символ керівної послідовності.

`^` (знак вставки)

- Умовний початок рядка.

`$`

- Умовне завершення рядка.

`()` (ліва і права круглі дужки)

- Визначення підшаблону.

`{}` (ліва і права фігурні дужки)

- Визначення числових лічильників.

`[]` (ліва і права квадратні дужки)

- Визначення класів символів.

`|` (вертикальна риска)

- Логічне АБО. Відокремлює варіанти значення.

`+` (знак «плюс»)

- Лічильник, 1 або більше.

`*` (зірочка)

- Лічильник, 0 або більше.

`?` (знак питання)

- Необов’язковий символ. Може бути інтерпретовано яка лічильник, 0 або 1.

## Лічильники

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

Лічильники слід вказувати між фігурними дужками (`{` і `}`). У загальному випадку вони виглядають так: `{[мінімальна кількість][,[максимальна кількість]]}`

Використання найкраще пояснюється прикладом:

`{1}`

- Точно один збіг.

`{0,1}`

- Жодного або 1 збіг.

`{,1}`

- Те саме, але меншими зусиллями ;)

`{5,10}`

- Не менше 5, але не більше 10 збігів.

`{5,}`

- Не менше 5 збігів, без обмежень згори.

Крім того, існує декілька скорочень:

`*` (зірочка)

- те саме, що і `{0,}`, шукати без обмеження на кількість відповідників.

`+` (знак «плюс»)

- те саме, що і `{1,}`, принаймні один відповідник.

`?` (знак питання)

- те саме, що і `{0,1}`, жодного або один відповідник.

### Жадібність

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

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

### Приклади використання

Ось декілька прикладів використання лічильників.

`^\d{4,5}\s`

- Буде знайдено цифри у 1234 поїхали і 12345 давай, але не у 567 одинадцять і у 223459 десь.

`\s+`

- Буде знайдено один або декілька пробільних символів.

`(ля){1,}`

- Буде знайдено ляляля, а також ля у словах шабля або пляшка.

`/?>`

- Буде знайдено /> у <closeditem/>, а також > у <openitem>.

## Умовні вирази

За допомогою умовних виразів відповідники формального виразу буде знайдено лише за виконання певних керованих умов.

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

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

У формальних виразах, описаних у цій документації, підтримуються такі умовні вирази:

`^` (каретка: початок рядка)

- Пошук на початку рядка.

  За виразом `^Петре` буде знайдено рядок Петре у рядку Петре, агов!, але не у рядку Агов, Петре!

`$` (кінець рядка)

- Пошук наприкінці рядка.

  За виразом `ти\?$` буде знайдено останнє «ти» у рядку Це зробив не ти?, але нічого не буде знайдено у рядку Це ж не ти зробив?

`\b` (межа слова)

- Пошук рядка, з одного боку якого стоїть символ запису слів, а з іншого — символ, який не використовується для запису слів.

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

`\B` (відсутність межі слова)

- Пошук всіх відповідників, які не буде пропущено за використання умовного виразу \b.

  Такий вираз можна використовувати, наприклад, для пошуку внутрішніх частин слів: вираз `\Bу\B` буде знайдено у слові вухо, але не у слові увійшов або виразі Бачив у кіно.

`(?=ШАБЛОН)` (перевірка на збіг)

- За допомогою умовного виразу перевірки на збіг можна перевірити на відповідність частину рядка, яку розташовано безпосередньо за можливим відповідником. За використання перевірки на збіг частина рядка вважатиметься невідповідною, якщо за нею не йде вказаний ШАБЛОН умовного виразу, але текст цього шаблону не буде додано до результату пошуку.

  За виразом `зручно(?=\w)` буде знайдено рядок зручно у слові зручності, а у рядку Це дуже зручно! нічого не буде знайдено.

`(?!ШАБЛОН)` (перевірка на відсутність збігу)

- За перевірки на відсутність збігу знайдений рядок не буде вважатися потрібним, якщо за ним йде рядок вказаний як ШАБЛОН.

  Вираз `const \w+\b(?!\s*&)` відповідатиме частині const char у рядку const char* foo, але не частині const QString у рядку const QString& bar, оскільки символ & збігається з шаблоном перевірки на відсутність збігу.

`(?<=ШАБЛОН)` (перевірка на збіг із пошуком назад)

- Пошук назад працює так само, як і пошук вперед, але пошук виконується у протилежному напрямку. За допомогою умовного виразу перевірки на збіг із пошуком назад можна перевірити на відповідність частину рядка, яку розташовано безпосередньо перед можливим відповідником. Частина рядка вважатиметься відповідною, лише якщо перед нею не йде вказаний ШАБЛОН умовного виразу, але текст цього шаблону не буде додано до результату пошуку.

  За виразом `(?<=диво)сміх` буде знайдено сміх, якщо перед ним є рядок диво (тобто буде знайдено дивосміх, але не вогнесміх і не окреме слово сміх).

`(?<!ШАБЛОН)` (перевірка на відсутність збігу із пошуком назад)

- За перевірки на відсутність збігу із пошуком назад знайдений рядок не буде вважатися потрібним, якщо перед ним йде рядок вказаний як ШАБЛОН.

  За виразом `(?<![\w\.])[0-9]+` буде знайдено 123 у рядках =123 і -123, але не буде знайдено 123 у .123 і слово123.

`(ШАБЛОН)` (Захоплення групи)

- Засіб обробки захопить і запам'ятає шаблон у дужках так, щоб ним можна було скористатися для зворотного посилання. Наприклад, вираз `(&quot;+)[^&quot;]*\1` відповідає одразу рядку `""""текст""""` і рядку `"текст"`.

  Докладніший опис можна знайти у розділі Збереження знайденого тексту (зворотні посилання).

`(?:ШАБЛОН)` (Групування без захоплення)

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