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

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати

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

Для початку розберемося - що ж таке регулярний вираз. По-англійськи пишеться так - Regular Exdivssion (звідси часто зустрічається скорочення "regexp" і навіть по-російськи "регексп"). По-перше, не варто шукати сенс в самому терміні - це дослівний переклад з англійської мови, який видається занадто абстрактним. Але що б зрозуміти за яким принципом працюють регулярні вирази, нам і потрібно саме що абстрагуватися на рівень припущень. Приклад з пошуком входження підрядка повинен бути зрозумілий усім. Але, насправді, хоча за допомогою регулярних виразів можна легко знайти будь-яке входження, цей приклад не розкриває всієї краси регекспов. Краще згадайте як працює пошук файлів за шаблоном (або по масці). Алгоритм має на увазі використання певних символів (wildcards), які дозволяють як би закрити ту частину імені, яка для нас не має значення. Однак самі wildcards не використовуються в іменах файлів (що робить алгоритм менш гнучким). Так ось, пошук файлів за шаблоном дозволяє відібрати ті імена файлів, які задовольняють заданій умові. При цьому, можна вказати і точне ім'я, а можна в якомусь місці імені зробити припущення (за допомогою все тих же wildcards). Так от, регулярні вирази дозволяють виконувати аналогічний пошук в межах деякої послідовності байт. Додайте до цього можливість роботи з різними частинами освіченої маски як з окремими одиницями і ви зрозумієте принадність регекспов.

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

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

По частинах і все відразу

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

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

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

/^([^ S] *) s (.*)/

Пробіжимося за шаблоном зліва-направо. Слеш вказують межі регекспа, так що їх відразу можна викинути. Символ ^ відноситься до уявних символів. Він прив'язує шаблон на початок рядка. Що це означає? Це значить, що ми знайдемо шукане, тільки у разі якщо воно знаходиться на початку заданої стрічки. Елементарно, Ватсон. Дивимося найпростіший приклад

$ Source = 'Pupkin';

$ Source = ~ / ^ Pupkin /; # Оператор поверне істину, так

# Як в $ source Pupkin з самого початку

$ Source = 'Vasya Pupkin';

$ Source = ~ / ^ Pupkin /; # А тут вже буде брехня, оскільки перед

# Пупкіним стоїть його ім'я.

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

$ Source = 'Pupkin';

$ Source = ~ / Pupkin /; # Оператор поверне істину, так

# Як в $ source Pupkin з самого початку

$ Source = 'Vasya Pupkin';

$ Source = ~ / Pupkin /; # Тут те ж буде істина, так як

# Пупкін в рядку є, хоча і не з початку.

# Але ж і шаблон не вимагав Пупкіна на початку рядка

Тепер зрозуміло, що таке уявні символи? Просто додаткова умова, а не частина шуканого.

Отже, повернемося до наших баранів

([^ S] *) s (.*)

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

І що ми бачимо? У нас два контейнери, розділених s. s - це спеціальний символ, вказуючий на будь-який символ з підмножини пробільних (пробіл, табуляція, etc ...) Уточню. Те що у нас між контейнерами указує на одиничний пробільний символ. Ми підійшли до найважливішої основоположної - в регулярному виразі (просто шаблоні) будь-який символ відповідає самому собі, якщо тільки він не є метасимволом зі спеціальним значенням. Ось s якраз і відноситься до таких метасимволи. Зізнаюся, що наш приклад взагалі часто-густо складається з метасимволів. Так, так, у ньому немає жодного символу, відповідного самому собі.

Отже, що ж ми з'ясували? Ми з'ясували, що будемо шукати щось, що складається з двох контейнерів, які розділені між собою одиничним пробільних символів. Тепер пора розібратися з вмістом контейнерів. Почнемо з правого - він простіший. Крапка в регекспе визначає будь-який символ, крім символу нового рядка (є деякі моменти, коли абсолютно будь-який). Сподіваюся, що таке будь-який символ зрозуміло? Це може бути "1", "2", "8", "D", "a", "r" або "b" і так далі і тому подібне від кодів з нуля до самого 255.

Ну а тепер, дозвольте представити вам ... Символ * перетворює попередню частину шаблону в маленьке ненажерливе істота - квантіфікатор. Цей самий квантіфікатор зжере всі символи (оскільки у нас перед цим була вказівка ​​крапка - будь-який символ) до самого кінця стрічки. Безкоштовний сир тільки в мишоловці, але квантіфікатор цього не знає. Ми не дарма помістили його в контейнер. Після того, як обробка регулярного виразу буде завершена у нас буде контейнер, в якому збережеться все те, що зжер квантіфікатор. Так як у нас всього два контейнери, то це контейнер буде у нас під номером два. Надалі ми так і скажемо perl - а ну, віддай нам вміст другого контейнера. Ось так то.

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

Пора приступати до вмісту лівого контейнера. Нагадаю як він виглядає

[^ S] *

Квадратні дужки визначають клас символів. Що таке клас символів? Припустимо, що шукане не може бути представлено послідовністю символів, тобто підрядком. Інакше кажучи, в прикладі з Пупкіним ми не можемо явно вказати

/ Pupkin /

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

[A-zA-Z]

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

[0-9]

Хоча для цифрових символів є ефективніше рішення - метасимвол d. Отже, у нас в лівій частині визначений клас символів. Але якийсь цікавий клас виходить - ніби прив'язаний на початок рядка. Ні, метасимвол ^ всередині класу вказує на заперечення символів класу. Це означає, що на місці цієї частини шаблону повинен знаходитися будь-який символ, що не входить до складу класу. Тобто, для прикладу

[^ 0-9]

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

#! C: / per / bin / perl-w

use strict;

reg ("Vasya Pupkin");

reg ("Vasya Pupkin");

reg ("Vasyattpupkin");

sub reg {

print "$ 1 = $ 1n $ 2 = $ 2nn" if $ _ [0] = ~ / ([^ s] *) s (.*)/;

}

У результаті вийде

$ 1 = Vasya

$ 2 = Pupkin

$ 1 =

$ 2 = Vasya Pupkin

$ 1 = Vasya

$ 2 = pupkin

Тепер давайте розберемося чому і як. Перший тест однозначно потрапляє під шаблон: Vasya не складається з пробільних символів, далі слідує один пробільний символ (натурально пропуск), а Pupkin становить частину, що залишилася рядка. Результат другого тесту у нас якийсь дивний. Перший контейнер у нас опинився порожній, а другий чому те містить весь рядок без провідного пробіл. З чим це пов'язано? Та з тим, що квантіфікатор * означає нуль або більше символів. Так як першим в рядку у нас пробільний символ, в правий контейнер, згідно з умовою, потрапляє нуль непробельний символів. Далі, пропуск то не входить до складу контейнерів. Ну а другий контейнер жере весь рядок до кінця. Третій варіант, я думаю, зрозумілий. Я вже говорив, що кожен символ регулярного виразу відповідає одиничному. І тільки квантифікатори дозволяють їсти декілька символів одного класу. У шаблоні контейнери розділені одиночним пробільних символів. У лівий контейнер потрапляє Vasya. Самим законним чином перший пробільний символ (табуляція в прикладі) пропускається, а правий контейнер їсть все що залишилося - у тому числі і другий табулятор. Таким чином, отримуємо Пупкіна з ведучою табуляцією.

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

/ ([^ S] *) s *(.*)/

Тепер наше регулярний вираз буде пропускати між ім'ям і прізвищем всі пробільні символи. Результат повинен бути таким.

$ 1 = Vasya

$ 2 = Pupkin

$ 1 =

$ 2 = Vasya Pupkin

$ 1 = Vasya

$ 2 = pupkin

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

/ S * ([^ s] *) s *(.*)/

Вітаю! Ви пройшли вступний курс з регекспам;)

Про ненажеру і інші тонкощі

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

/ .* Pupkin /

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

Vasya pupkin pupkin

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

/ .*? Pupkin /

для коректного поїдання "Vasya" з рядка "Vasya pupkin pupkin". Далі, конструкції з фігурними дужками дозволяють визначати межі апетиту квантіфікатор. Усередині фігурних дужок (природно після самого квантіфікатор) може бути вказано одне або два значення, перерахованих через кому, які відповідно визначають межі жадності. Пригадати про специфікатор *. Аналогічний йому + перетворює шаблон в ненажеру, якого не задовольняє менше одного збігу. Тобто при використанні + умова відбору є істинним тільки коли є 1 і більше збігів. Зауважте, що верхня межа у нас невизначений і може бути опущений усередині конструкції з фігурними дужками. Якщо всередині фігурних дужок вказати всього одне значення без ком, то квантіфікатор зжере тільки такий рядок, в якій збігів з шаблоном буде саме вказана кількість.

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

/ (. {2,10}) /

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

$ 1 = Vasya Pupkin

$ 2 = in

$ 1 = Vasya Pupkin

$ 2 = kin

$ 1 = Vasya pupkin

$ 2 = kin

Загалом з квантифікатори можна ще багато балуватися. Всього розповісти все одно не вдасться. Тут тільки один засіб - практикуватися.

Далі на порядку денному таке поняття як альтернативні шаблони. Це елементи регулярного виразу, які дозволяють визначати декілька варіантів шаблону. Найбільш наочний приклад це визначення протоколу в рядку URL

/ ^ (Http | ftp) /

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

Є ще одна фіча, яка може вам стати в нагоді. Це, так звані, додаткові конструкції. Вони дозволяють виконувати перевірку до або після поточного місця в шаблоні, але при цьому в сам шаблон не входять. Їх описувати я не буду, так як це звичайна довідкова інформація, яка є в будь-якій книзі з perl. Просто - що б ви знали.

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

одиночні символи (characters) - він і є одиночний, чого його коментувати;)

класи символів (character classes) - [], [^]

альтернативні шаблони (alternative match patterns) - (X | X | X)

квантифікатори (quantifiers) - {},?, +, *

уявні символи (assertions) - s, ^, $, etc ...

контейнери (backreferences) - $ 1, $ 2, $ x

додаткові конструкції

Від теорії до практики

У perl є три основних оператора які працюють з рядками. Це

m / / - перевірка збігів (або пошук)

s / / / - підстановка

tr / / / - заміна

Кожен оператор має свої свої модифікатори. Для початку розглянемо для чого потрібні всі три оператори.

Перший - m / / (або просто / /) використовується для пошуку збігів з вказаним шаблоном. Це якраз те, на чому ми тренувалися вище. Там же і приклад, як можна його використовувати. Другий оператор s / / / дозволяє не тільки знаходити певні ділянки, співпадаючі із заданим шаблоном, але й виконувати нерівнозначні підстановку. Фактично, s / / / це те саме що і m / / (навіть модифікатори співпадають), але з можливістю довільної підстановки. Сенс нерівнозначної підстановки відкривається коли ми звертаємося до третього оператора tr / / /. Оператор заміни може замінювати ділянки тільки на рівнозначні по довжині. Як наслідок - він працює швидше s / / /. З усіх операторів s / / / найгнучкіший - він дозволяє виконувати все те, що можуть m / / і tr / / /. З його допомогою можна скрутити гори. Але, за все доводиться платити і тут ми розплачуємося швидкістю. tr / / / можна взагалі не розглядати (якщо кінцевий ви не фанат швидкості). А ось на s / / / хочеться зупинитися детальніше.

Перш за все хочу попередити - не намагайтеся запхати в праву частину оператора s / / / (тобто в ту, яка визначає що будемо підставляти замість знайденого шаблону) квантифікатори, уявні символи і взагалі всякі інші невизначеності. Все повинно бути чітко і однозначно. Робота оператора s / / / (в іншому як і m / / /) має на увазі компіляцію на кожному етапі обігу до регулярного виразу. Якщо ви не лінувалися (та й так він часто зустрічається) то вже знаєте про модифікатор глобального пошуку g, який змушує працювати регексп протягом залишку від попереднього результату і так до кінця рядка. Так от, якщо в правій частині розмістити ім'я змінної-контейнера і заюзать регексп з модифікаторами o і g, то напевно вийде бардак, так як o забороняє повторну компіляцію шаблону. Загалом тут потрібно бути гранично уважним. Ще хочу звернути вашу увагу на модифікатори e і ee. Вони дозволяють виконувати код безпосередньо в процесі роботи регулярного виразу. Якщо у вас дуже складне завдання і його дуже важко реалізувати в одному регулярному виразі, розбийте їх на складові в правій частині - і працювати буде швидше і налагоджувати простіше.

Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Реферат
35кб. | скачати


Схожі роботи:
Крилаті вирази
Використання мови Perl
Паскаль лексика вирази семантика
книга Основи Perl для Біоінформатика
Захищаємо Perl шунт в мозок або звіряча нейрохірургія
Вирази і умовний оператор IF Оператори циклів Масиви та підпрограми
© Усі права захищені
написати до нас