Scroll to navigation

perlrequick(1) 2007-10-27-16:31 perlrequick(1)

Назва

perlrequick - ознайомлення з регулярними виразами Perl

Опис

Ця сторінка охоплює основи розуміння, створення і використання регулярних виразів ('рег. виразів') у Perl.

Простий збіг зі словами.

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


"Hello World" =~ /World/; # зійдеться
В цьому виразі, "World" являється рег. виразом, а "//", що оточують "/World/" вказують perl здійснити пошук по ланцюжку щодо співпадань. Оператор "=~" пов'язує ланцюжок із регулярним виразом і спричинить істинне значення, якщо рег. вираз зійшовся, або хибне - якщо ні. В нашому випадку, "World" збіглося з другим словом з "Hello World", тож вираз виявиться істинним. Ця ідея має декілька варіацій.

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


print "It matches\n" if "Hello World" =~ /World/;
Зміст порівняння можна обернути на протилежний за допомогою оператору "!~":


print "It doesn't match\n" if "Hello World" !~ /World/;
Літеральний ланцюжок регулярного виразу можна замінити на змінну:


$greeting = "World";
print "It matches\n" if "Hello World" =~ /$greeting/;
Якщо ви порівнюєте із $_, то "$_ =~" частину можна опустити:


$_ = "Hello World";
print "It matches\n" if /World/;
print "It matches\n" if /World/;
Нарешті, стандартні обмежувачі рег. виразу "//" можна поміняти на довільні, якщо додати 'm' (скорочення від 'match') напочатку:


"Hello World" =~ m!World!; # обмежувач '!'
"Hello World" =~ m{World}; # парні обмежувачі '{}'
"/usr/bin/perl" =~ m"/perl"; # лапки як обмежувач,
# '/' в ролі звичайного знаку
Регулярні вирази повинні *точно* співпасти з частиною ланцюжка, щоб вираз виявився істинним:


"Hello World" =~ /world/; # не сходиться за регістром
"Hello World" =~ /o W/; # зійдеться, ' ' є звичайним символом
"Hello World" =~ /World /; # не зійдеться, немає ' ' наприкінці
perl завжди спробує здійснити співпадання в найпершому можливому місці ланцюжка:


"Hello World" =~ /o/; # збіглося з 'o' в 'Hello'
"That hat is red" =~ /hat/; # зійшлося з 'hat' з 'That'
Не всі символи можна використати "такими, якими вони є" при порівнянні. Деякі знаки, названі *метазнаками*, резервовано для власних нотацій регулярних виразів. Метазнаками являються


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


"2+2=4" =~ /2+2/; # не збігається, + є метазнаком
"2+2=4" =~ /2\+2/; # зійдеться, \+ розглядається як
# звичний +
'C:\WIN32' =~ /C:\\WIN/; # зійдеться
"/usr/bin/perl" =~ /\/usr\/bin\/perl/; # зійдеться
В останньому виразі, правий слеш '/' так само екрановано, оскільки він використовується як обмежувач регулярного виразу.

Недруковні символи ASCII представлені керівними послідовностями. Спільними прикладами є "\t", що позначає табуляцію, "\n" - знак нового рядка, "\r" - повернення каретки тощо. Довільні байти можна представити вісімковими керівними послідовностями, наприклад "\033", або шістнадцятковими, як от "\x1B":


"1000\t2000" =~ m(0\t2) # зійдеться
"cat" =~ /\143\x61\x74/ # зійдеться, хоч це й дивний спосіб
# написання слова "cat" (кішка)
Регулярні вирази, в основному, розглядаються як привнесені в подвійні лапки ланцюжки, тож розкриття змінних працюватиме:


$foo = 'house';
'cathouse' =~ /cat$foo/; # зійдеться
'housecat' =~ /${foo}cat/; # зійдеться
З усіма наведеними вище виразами, якщо збіг відбувся десь посередині ланцюжка, це вважатиметься вдалим порівнянням. Для того, щоб точніше вказати де саме повинно відбутися співпадання, нам потрібно скористатися опорних (закріплювальних) метазнаків "^" із "$". Опора "^" означає збіг на початку рядка, тоді як "$" - на кінці, перед знаком нового рядка. Декілька прикладів:


"housekeeper" =~ /keeper/; # зійдеться
"housekeeper" =~ /^keeper/; # не зійдеться
"housekeeper" =~ /keeper$/; # сходиться
"housekeeper\n" =~ /keeper$/; # сходиться
"housekeeper" =~ /^housekeeper$/; # сходиться

Класи знаків

Клас знаків дозволяє вказати набір можливих знаків, а не тільки один. Класи вказуються за допомогою квадратних дужок "[...]" із набором імовірних знаків. Ось декілька прикладів:


/cat/; # зійдеться з 'cat'
/[bcr]at/; # зійдеться з 'bat', 'cat' або 'rat'
"abc" =~ /[cab]/; # зійдеться через 'a'
В останньому прикладі, навіть якщо 'c' стоїть першою в наборі класу, найпершим місцем, де регулярний вираз може зійтися з ланцюжком, є 'a'.


/[yY][eE][sS]/; # зіставити з 'yes' у регістро-незалежний спосіб
# 'yes', 'Yes', 'YES' тощо
/yes/i; # так само зіставити з 'yes', не залежачи від
# регістру
В останньому прикладі показано порівнювання із зразком за використанням модифікатору 'i' (insensitive), що скасовує чутливість до регістру.

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


/[\]c]def/; # збігається з ']def' or 'cdef'
$x = 'bcr';
/[$x]at/; # збігається з 'bat, 'cat' і 'rat'
/[\$x]at/; # зійдеться з '$at' і 'xat'
/[\\$x]at/; # зійдеться з '\at', 'bat, 'cat' і 'rat'
Спеціальний знак '-' діє як оператор діапазону всередині класу знаків, тож незграбне "[0123456789]" і "[abc...xyz]" можна перетворити на струнке "[0-9]" and "[a-z]":


/item[0-9]/; # збігається з 'item0' аж до 'item9'
/[0-9a-fA-F]/; # зійдеться з шістнадцятковим числом
Знак '-' як перший або останній символ класу розглядається як звичайний знак.

Спеціальний знак "^", якщо стоїть першим усередині класу, позначає заперечення цього класу знаків, що призведе до збігу з будь-яким знаком *за винятком* тих, що включено в квадратні дужки. Обидва, "[...]" і "[^...]", повинні зійтися з якимось символом, інакше порівняння зазнає невдачі. Таким чином,


/[^a]at/; # не зійдеться з 'aat' або 'at', зате
# із рештою, як от 'bat', 'cat, '0at', '%at' тощо.
/[^0-9]/; # зійдеться із нечисловим символом
/[a^]at/; # зійдеться з 'aat' або '^at'; тут '^' звичайний
Perl включає декілька скорочень для загальних класів знаків:


позначає цифру, тотожно [0-9]


позначає пробіл, тотожно [\ \t\r\n\f]


позначає символ, який може бути частиною слова (алфавітно-числовий або _), тотожно [0-9a-zA-Z_]


протилежний \d; будь-який символ окрім цифри, тотожно [^0-9]


протилежний \s; будь-який символ окрім пробілу, тотожно [^\s]


протилежний \w; будь-який символ окрім тих, що можуть складати слово, [^\w]

\&.

(крапка) збігається з будь-яким знаком окрім \n

Скорочення "\d\s\w\D\S\W" можна вживати всередині і поза межами класу знаків. Ось декілька прикладів використання:


/\d\d:\d\d:\d\d/; # збігається з форматом часу гг:хх:сс
/[\d\s]/; # збігається з будь-якою цифрою або пробілом
/\w\W\w/; # збігається зі знаком, що складатиме слово
# із наступним знаком, що не є частиною слова із
# наступним знаком слова
/..rt/; # збігається з будь-якими двома знаками і 'rt'
/end\./; # збігається з 'end.'
/end[.]/; # те саме, збігається з 'end.'
Опорний знак слова "\b" збігається з межею між знаком, що складає слово і таким, що не може бути частиною слова, тобто "\w\W" або "\W\w":


$x = "Housecat catenates house and cat";
$x =~ /\bcat/; # збігається з cat у 'catenates'
$x =~ /cat\b/; # збігається з cat у 'housecat'
$x =~ /\bcat\b/; # збігається з 'cat' наприкінці ланцюжка
В останньому прикладі, кінець ланцюжка розглядається як обмежувач слова.

Порівнювання з тим або іншим

Ми можемо порівняти з різноманітними символьними ланцюжками за допомогою метасимволу альтернативи '|'. Щоб збіглося із "dog" або "cat", ми створимо рег. вираз "dog|cat". Як і раніше, Perl намагатиметься знайти збіг у найпершому можливому місці ланцюжка. В кожній позиції perl спочатку пробує зіставити з першою альтернативою "dog". Якщо "dog" не співпав, perl спробує тоді другу альтернативу "cat". Якщо "cat" також не збігається, тоді перевірка зазнає невдачі і perl переходить до наступної позиції в ланцюжкові. Декілька прикладів:


"cats and dogs" =~ /cat|dog|bird/; # збігається з "cat"
"cats and dogs" =~ /dog|cat|bird/; # збігається з "cat"
Хоч "dog" і являється першою альтернативою в останньому регулярному виразі, "cat" вдається співпасти раніше в ланцюжку.


"cats" =~ /c|ca|cat|cats/; # збігається з "c"
"cats" =~ /cats|cat|ca|c/; # збігається з "cats"
У поточній позиції знаку, перша ж альтернатива, яка призводить до збігу з регулярним виразом, буде тією, що співпала. В вищенаведеному прикладі, всі альтернативи підходять у першій позиції ланцюжка, але лише перша з альтернатив співпадає насправді.

Групування речей і ієрархічне порівнювання

Метасимвол групування "()" дозволяє розглянути частину регулярного виразу як єдине ціле. Окремі частини рег. виразу згруповано шляхом включення їх у круглі дужки. Регулярний вираз "house(cat|keeper)" означає збіг із "house" із наступним або "cat", або "keeper". Ще декілька прикладів:


/(a|b)b/; # збігається з 'ab' або 'bb'
/(^a|b)c/; # збігається 'ac' на початку ланцюжка і 'bc' деінде
/house(cat|)/; # збігається з або 'housecat', або 'house'
/house(cat(s|)|)/; # збігається з або 'housecats', або 'housecat',
# або 'house'. Зверніть увагу, що групи можуть
# гніздитися
"20" =~ /(19|20|)\d\d/; # збігається з нульовою альтернативою
# '()\d\d', оскільки '20\d\d' не співпадає

Звертання до збігів

Метасимвол групування "()" також дозволяє здобуття частин ланцюжка, що зійшлися. Для кожної групи, частину, що збіглася, буде збережено в спеціальні змінні $1, $2 і.т.д. Їх можна застосовувати як звичайні змінні:


# добути години, хвилини, секунди
$time =~ /(\d\d):(\d\d):(\d\d)/; # збігається з форматом гг:хх:сс
$hours = $1;
$minutes = $2;
$seconds = $3
У першому контексті, збіг з "/рег. виразом/" з групуванням поверне список значень, що зійшлися "($1, $2,...)". Ми можемо змінити це на


($hours, $minutes, $second) = ($time =~ /(\d\d):(\d\d):(\d\d)/);
Якщо групування в регулярному виразі гніздовано, $1 отримає групу з крайньою лівою відкриваючою дужкою, $2 з наступною відкриваючою дужкою тощо. Ось, наприклад, складний регулярний вираз і відповідні змінні, вказані нижче:


/(ab(cd|ef)((gi)|j))/;
1 2 34
Зі змінними збігів $1, $2, ... пов'язані зворотні посилання (backrerefence) "\1", "\2", ... Зворотні посилання - це змінні співпадань, які можна використати всередині регулярних виразів:


/(\w\w\w)\s\1/; # знайти послідовності на кшталт 'the the'
# в ланцюжкові
$1, $2, ... повинні використовуватись тільки поза межами регулярного виразу, тоді як "\1", "\2", ... - тільки всередині.

Повторення збігів

Метасимволи-квантори (кількісні метасимволи) "?", "*", "+" і "{}" дозволяють нам визначити число повторень частини регулярного виразу, який ми вважаємо, що збігся. Квантори додаються одразу після знаку, класу знаків або групи, які ми вказали. Вони означають наступне:


збігтися з 'a' 1 або 0 разів


збігтися з 'а' 0 або більше разів, тобто будь-яку кількість разів


збігтися з 'a' 1 або більше разів, тобто щонайменше один раз


збігтися щонайменше "n" раз, але не більше ніж "m" раз


збігтися щонайменше "n" або більше разів


збігтися точно "n" раз

З наступними прикладами:


/[a-z]+\s+\d*/; # зійдеться зі словом у нижньому регістрі,
# принаймні одним пробілом, і будь-якою кількістю
# цифр
/(\w+)\s+\1/; # зійдеться з повтореним словом довільної довжини
$year =~ /\d{2,4}/; # впевнитись, що $year складається з
# щонайменше 2 цифр, але не більше за 4
$year =~ /\d{4}|\d{2}/; # навіть краще; відкидає дати з 3-ох цифр
Ці квантори спробують збігтися з найбільшою частиною ланцюжка, однак дозволяючи співпадання решти регулярного виразу. Таким чином,


$x = 'the cat in the hat';
$x =~ /^(.*)(at)(.*)$/; # зійдуться
# $1 = 'the cat in the h'
# $2 = 'at'
# $3 = '' (0 співпадань)
Перший квантор ".*" захватить якнайбільше ланцюжка, все ж дозволяючи збіг з регулярним виразом. Для другого квантору ".*" не залишилось ланцюжка, тож він не зійдеться.

Інші порівняння

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

Маючи код


$pattern = 'Seuss';
while (<>) {
print if /$pattern/;
}
perl повинен оцінити $pattern (зразок) щоразу при проходженні через цикл. Якщо $pattern не міняється, використайте модифікатор "//o" для одноразового розкриття змінної. Якщо ви не бажаєте жодного розкриття змінних, скористайтеся спеціального роздільника "m''":


@pattern = ('Seuss');
m/@pattern/; # збігається з 'Seuss'
m'@pattern'; # збігається з буквальним ланцюжком '@pattern'
Глобальний модифікатор "//g" дозволяє регулярному виразу зійтися всередині ланцюжка стільки разів скільки можливо. В скалярному контексті, послідовні співпадання всередині ланцюжка спричинять перехід "//g" від одного збігу до іншого, запам'ятовуючи при цьому позицію в ланцюжкові. Ви можете отримати або встановити позицію за допомогою функції pos(). Наприклад,


$x = "cat dog house"; # 3 words
while ($x =~ /(\w+)/g) {
print "Word is $1, ends at position ", pos $x, "\n";
}
виводить


Word is cat, ends at position 3
Word is dog, ends at position 7
Word is house, ends at position 13
Порівнювання, що зазнало невдачі, або зміна ланцюжка поверне позицію до початкового значення. Якщо ви не хочете перезавантаження позиції після невдалого співпадання, скористуйтеся модифікатором "//c", як от "/рег. вираз/gc".

В контексті списку, "//g" повертає список груп, що співпали, або якщо групування відсутнє - список збігів із цілим регулярним виразом. Тож


@words = ($x =~ /(\w+)/g); # збігається,
# $word[0] = 'cat'
# $word[1] = 'dog'
# $word[2] = 'house'

Пошук і заміна

Пошук із заміною відбувається за схемою "s/рег. вираз/текст заміни/модифікатори". Текст заміни змістить те, що співпало з рег. виразом в ланцюжку. Оператор "=~" також застосовується тут для того, щоб пов'язати ланцюжок із дією "s///". Якщо порівнюється супроти $_, то вираз "$_ =~" можна опустити. При вдалім порівнянні, "s///" повертає число здійснених замін, у протилежному випадку - хибно. Ось деякі приклади:


$x = "Time to feed the cat!";
$x =~ s/cat/hacker/; # $x міститиме "Time to feed the hacker!"
$y = "'quoted words'";
$y =~ s/^'(.*)'$/$1/; # видалити одинарні лапки,
# $y міститиме "quoted words"
Із оператором "s///", змінні збігу $1, $2 і.т.д. одразу доступні для вжитку у виразі заміни. Додавши глобальний оператор, "s///g" шукатиме і замінить всі збіги з регулярним виразом в ланцюжку:


$x = "I batted 4 for 4";
$x =~ s/4/four/; # $x міститиме "I batted four for 4"
$x = "I batted 4 for 4";
$x =~ s/4/four/g; # $x міститиме "I batted four for four"
Модифікатор обчислення "s///e" огортає ланцюжок заміни функцією "eval{...}" і обчислення візьме місце частини ланцюжка, що співпала з рег. виразом. Декілька прикладів:


# обернути всі слова в ланцюжку
$x = "the cat in the hat";
$x =~ s/(\w+)/reverse $1/ge; # $x міститиме "eht tac ni eht tah"

# перетворити відсотки на десяткові числа
$x = "A 39% hit rate";
$x =~ s!(\d+)%!$1/100!e; # $x міститиме "A 0.39 hit rate"
Останній приклад демонструє, що "s///" може використати інші обмежувачі, такі як "s!!!" і "s{}{}", або навіть "s{}//". Якщо застосувати одинарні лапки "s'''", то рег. вираз і заміна вважатимуться ланцюжками, включеними в одинарні лапки.

Оператор split

"split /рег. вираз/, ланцюжок" розбиває ланцюжок на список з його частин і повертає цей список. Регулярний вираз визначає послідовність знаків, згідно з яким відбувається розбиття. Так, скажімо, щоб розбити ланцюжок на слова використайте


$x = "Calvin and Hobbes";
@word = split /\s+/, $x; # $word[0] = 'Calvin'
# $word[1] = 'and'
# $word[2] = 'Hobbes'
А щоб добути розділений комою список чисел:


$x = "1.618,2.718, 3.142";
@const = split /,\s*/, $x; # $const[0] = '1.618'
# $const[1] = '2.718'
# $const[2] = '3.142'
Якщо використати порожній регулярний вираз "//", ланцюжок буде розщеплено на окремі символи. Якщо ж регулярний вираз включає групування, тоді отриманий список міститиме також підланцюжки, що співпали:


$x = "/usr/bin";
@parts = split m!(/)!, $x; # $parts[0] = ''
# $parts[1] = '/'
# $parts[2] = 'usr'
# $parts[3] = '/'
# $parts[4] = 'bin'
Позаяк перший знак $x збігся з регулярним виразом, split додала порожній перший елемент до списку.

Вади

Жодних.

Дивіться також

Ця сторінка являється лише швидким введенням для початківців. Для глибшого ознайомлення з регулярними виразами зверніться до perlretut(1) і довідника perlre(1).

Переклав українською Віталій Цибуляк.

2007-10-27-16:31 © 2005-2007 DLOU, GNU FDL