ВЛАДИСЛАВ ГОРОШКО
Регулярные выражения и поиск текста в Perl
В данной статье я попытаюсь как можно лучше объяснить, что такое регулярные выражения в Perl. Изюминкой Perl является работа с текстом – для этого он и был создан. Регулярные выражения очень часто используются в Unix-системах, например, для поиска файлов по шаблону, также в sh, примеров много и все не перечислить, Perl не исключение.
В некоторых случаях синтаксис регулярных выражений может быть немного другой, но поняв синтаксис регулярных выражений Perl, с другими проблем не будет.
Я читал много статей по этому поводу и видел много книг, но действительно хорошо описывающего материала не нашел, потому что в одних книгах слишком сильно растягивают материал и теряют основную нить рассказа, в других же наоборот, очень мало... Поэтому я решил написать эту статью, в которой я постараюсь оптимально и ясно рассказать о регулярных выражениях и поиске текста. Причем не просто на словах, а на большом количестве примеров.
Начнем с того, что в Perl имеются три основных оператора для работы с текстом:
- m/.../ – проверка совпадений (matching)
- s/.../.../ – подстановка текста (substitution)
- tr/.../.../ – трансляция текста (translation)
также парочка полезных функций:
- substr(EXPR,OFFSET,LEN,REPLACEMENT)
- split(/PATTERN/,EXPR,LIMIT)
Оператор m/.../ пытается сопоставить шаблон, указанный в качестве аргумента, с текстом, с которым идет сравнение. Например:
$string = "Tasty Berry";
if ($string =~ m/berry/i){
print "This is berry string!";
}
В данном случае будет выведена строка «This is berry string!». Для сравнения скаляра с шаблоном нужно использовать сравнение такого вида «=~».
Здесь операратор m/.../ вернет истину или ложь (т.к. данное регулярное выражение используется в скалярном контексте, а не в списковом – о нем ниже), если его опустить, то оператор m/.../ будет использовать специальную переменную $_.
Буква i после второй косой черты означает игнорирование регистра, как вы видите, в шаблоне написано «berry» а в $string – «Berry», т.е. если бы не было буквы i, то наша программа не вывела бы строку «This is berry string!». Шаблон m/berry/i совпадет со строками «BERRY», «berry», «BeRrY» и т. д.
$lines="";
open(FILE,"file.txt") or die $!;
while(){
if(/exit/i){
last;
}else{
$lines.=$_;
}
}
close(FILE);
Данная программа открывает файл и идет по строкам (по строкам, т.к. по умолчанию переменная $/ является переносом строки, переопределяя ее, вы меняете терминатор строки), сохраняя каждую строчку в переменную $lines, а если в строчке встречается слово «exit», то выходит из цикла.
Как вы уже успели заметить, я пропустил букву m в начале оператора: этот оператор используется очень часто и можно использовать его сокращенную форму – без первой буквы m.
Также поменять смысл на противоположный можно просто сменив оператор «=~» на «!~», например:
$string = "Tasty Berry";
if($string !~ /berry/i){
print "This is NOT berry string!";
}else{
print "This is berry string!";
}
В данном случае будет выведена строка «This is berry string!». Но если нам немного поменять строку таким образом:
$string = "Tasty Strawberry";
Логически мысля, человек ищет строчку berry (с англ. ягода), но у нас в строчке strawberry (с англ. клубника), т.е. это нам не подходит, хотя наш шаблон все равно сработает и выведет «This is berry string!», хотя должен вывести «This is NOT berry string!». Это получается, потому что, когда оператор получает шаблон «berry» и другую строку, в которой он ищет символ «b», за которым следует «e», затем «r», «r» и «y», а все, что находится до этой последовательности или после нее, не имеет значения. Поэтому придется улучшить шаблон, подставив мнимый символ (об этих символах ниже), вот так:
$string = "Tasty Berry";
if($string !~ /berry/i){
print "This is NOT berry string!";
}else{
print "This is berry string!";
}
А вот сейчас все правильно – теперь наша программа выведет «This is NOT berry string!».
«» в начале шаблона – это мнимый символ, соответствующий границе слова. Как вы видите, некоторые символы имеют определенный смысл для регулярных выражений. Метасимволы могут создавать альтернативные значения, организовывать повторы, группировать (что позволяет запоминать часть найденной строки), создавать классы символов и т. д. Все метасимволы начинаются с обратной косой черты (). Если шаблон содержит символы косой черты (например, анализ каталогов, или HTML-тег), то лучше использовать другие ограничители, т.к. перед каждой чертой придется ставить обратную косую черту ().
Вот пример:
$string = "/usr/bin/perl";
# например, вы хотите заменить каталог /bin – на /local/bin
$string =~ s//bin//local/bin/ig;
Как видите, перед каждой косой чертой пришлось ставить обратную косую черту – некрасиво и очень громоздко, поэтому заменим разделитель (символы ? и " лучше не использовать в качестве разделителей, т.к. шаблоны, ограниченные этими символами, обрабатываются иначе):
$string = "/usr/bin/perl";
$string =~ s%/bin%/local/bin%ig;
Или можно вот так:
$string =~ s|/bin|/local/bin|ig;
Или:
$string =~ s(/bin)(/local/bin)ig;
Или так:
$string =~ s{/bin}{/local/bin}ig;
Точно также можно использовать квадратные скобки. Эти же правила можно применять и для других операторов этого класса. Если вы успели заметить, я использовал еще один модификатор регулярных выражений, а именно g – он означает глобальную поиск и/или замену, например:
$string = "Some path /usr/bin/perl ...and another path usr/bin/perl";
$string =~ s%/bin%/local/bin%i;
print $string;
Вывод будет следующим:
Some path /usr/local/bin/perl
...and another path /usr/bin/per
|
Как вы видите, путь изменился только в первом совпадении с шаблоном, после этого наш оператор заканчивает работу, а вот если добавить модификатор g, тогда регулярное выражение будет сопоставляться до конца текста, т.е. глобально. После добавления модификатора g результат будет таков:
Some path /usr/local/bin/perl
...and another path /usr/local/bin/per
|
Теперь рассмотрим все по порядку, начнем с модификаторов операторов s/.../.../ и m/.../:
- i – игнорирует различие между заглавными и строчными буквами;
- s – метасимволу точка (.) разрешено соответствовать ;
- m – разрешает метасимволам ^ и $ привязываться к промежуточным символам , имеющимся в тексте;
- x – разрешает использовать пробелы и комментарии в регулярных выражениях;
- g – глобальный поиск и/или замена (т.е. по всему тексту);
- с – работает только для m/.../, не позволяет сбрасывать текущую позицию поиска;
- o – однократная компиляция шаблонов;
- e – говорит о том, что правый аргумент команды s/.../.../ – это исполняемый код, в качестве подстановки будет использовано возвращаемое значение;
- ee – говорит о том, что правый аргумент команды s/.../.../ – это строка, которую нужно выполнить как фрагмент кода, с помощью функции eval(), потом значение интерполируется.
Перед тем как рассмотреть метасимволы, я хочу сказать пару слов об обычных символах. В шаблоне любой символ соответствует самому себе, если не является метасимволом или мнимым символом, или этим символом:
"", "|", "(", "{", "[", "*", "+", "$", "?", "."
А теперь давайте посмотрим на все метасимволы:
- \077 – восьмеричный символ;
- \xFF – шестнадцатиричный символ;
- \a – символ звонка (alarm);
- \c[ – управляющие символы, т.е. CTRL + <символ>, в данном случае это ESC;
- \f – символ «прогона» страницы;
- \d – соответствует цифре;
- \D – соответствует любому символу, кроме цифр;
- \e – символ ESC;
- \l – следующая литера становится строчной;
- \L – все последующие литеры становятся строчными (вплоть до E);
- \u – следующая литера становится заглавной;
- \U – все последующие литеры становятся заглавными (вплоть до E);
- \r – возврат каретки (CR);
- \n – символ новой строки (LF);
- \t – символ горизонтальной табуляции;
- \v – символ вертикальной табуляции;
- \Q – все последующие метасимволы становятся обычными (вплоть до E);
- \E – конец действия комманд L, U и Q;
- \s – соответствует любому пробельному символу (т.е. пробел, символ вертикальной/горизонтальной табуляции, символу новой строки, и т. д.);
- \S – любой символ, кроме пробельного;
- \w – алфавитно-цифровой символ (любая цифра, буква или символ подчеркивания);
- \W – любой символ, кроме буквы, цифры или символа подчеркивания.
Также в Perl есть определенные символы, которые соответствуют не какой-нибудь литере, а означают выполнение какого-нибудь условия, они называются мнимыми символами. Вот они:
- | – альтернатива;
- ! – символ логического NOT;
- . – любой символ, кроме переноса строки;
- ^ – начало строки текста;
- $ – конец строки текста;
- \b – граница слова;
- \B – отсутствие границы слова;
- \A – «истинное» начало строки;
- \Z – «истинный» конец строки или позиция перед символом начала новой строки, расположенным в «истинном» конце строки;
- \z – «истинный» конец строки;
- \G – граница, на которой остановился предыдущий глобальный поиск;
- (?=шаблон) – после этой точки есть фрагмент текста, который соответствует указанному регулярному выражению;
- (?!шаблон) – после этой точки нет текста, который бы соответствовал указанному регулярному выражению;
- (?<=шаблон) – перед этой точкой есть фрагмент текста, соответствующий указанному регулярному выражению;
- (?<!шаблон) – перед этой точкой нет фрагмента текста, соответствующего указанному регулярному выражению;
- (?модификаторы) – задает модификаторы, которые локальным образом меняют работу процедуры поиска. В отличие от глобальных модификаторов имеют силу только для текущего блока, т.е. для ближайшей группы круглых скобок, охватывающих конструкцию, например, шаблон ((?i)text) соответcтвует слову «text» без учета регистра;
- (?:шаблон) или (?модификаторы:шаблон) – группирует элементы шаблона. В отличие от обычных круглых скобок не создает нумерованной переменной. Например, модификатор i не будет делать различия между строчными и заглавными буквами, однако область действия этого модификатора будет ограничена только указанным шаблоном;
- (?#текст) – комментарий, текст комментария игнорируется;
- (?{код}) – выполнение кода.
Теперь рассмотрим квантификаторы:
- + – одно или сколько угодно совпадений;
- * – ноль или сколько угодно совпадений;
- ? – ноль или одно совпадение;
- {n} – ровно n совпадений;
- {n,} – как минимум n совпадений;
- {n,m} – как минимум n, как максимум m совпадений.
Создание групп и классов символов:
- [] – класс символов;
- () – группа символов.
Теперь несколько слов о встроенных переменных:
- $" – подстрока, следующая за совпадением;
- $& – совпадение с шаблоном поиска (при последней операции поиска или замены);
- $` – подстрока, расположенная перед совпадением;
- $^R – результат последнего вычисления утверждения в теле шаблона;
- $n – n-ный фрагмент совпадения;
- \n – n-ный фрагмент совпадения, вызываемый в самом операторе (например, в операторе s/.../.../);
- $+ – последняя группа;
- $* – разрешает выполнять поиск в многострочных файлах (булевая переменная);
- @- – спецмассив, который содержит начальную позицию найденного совпадения;
- @+ – массив, содержащий позицию последнего найденного совпадения.
Вышеперечисленные метасимволы, квантификаторы, мнимые символы, специальные переменные – это та часть статьи, к которой вы будете обращаться чаще всего – поэтому я собрал все вместе.
А далее будем рассматривать более сложные решения задач, с пояснениями.
Начнем с создания регулярных выражений. А более точно со специальных переменных:
$string = "one two three four five";
$string =~ m/three/;
print $`; # результат: "one two "
print $"; # результат: " four five"
print $&; # результат: "three"
# теперь насчет массивов @+ и @-
print $-[0]; # результат: 8
print $+[0]; # результат: 13
# переменные $#-, $#+ указывают на длину @-, @+ соответственно
# с помощью функции substr() можно получить переменные $`, $",
# $&, например
$before_pat = substr($string,0,$-[0]); # аналог $`
$after_pat = substr($string,$+[0]); # аналог $"
$pattern = substr($string,$-[0],$+[0]-$-[0]); # аналог $&
Переменная $^R, пример:
$string = "some text";
$qwer =~ /(?{$var1=2.3;$var2=3.2})/;
print $^R
Результат: 3.2
Переменная $+, пример:
$string = "some text";
$string =~ m/(w+)s+(w+)/;
print $+;
Результат: text
Переменная $*, пример:
$string = "couple of lines goes bellow...";
print $string =~ m/^lines/; # в этом случае пустая строка ""
$*=1; # после присвоения переменной $* истины, результат будет
print $string =~ m/^lines/; # – истина
Используем модификатор e для оператора s/.../.../:
$string = "words don"t come easy";
$string =~ s/(w+)/uc($1)/eg;
print $string;
Данный фрагмент кода «поднимает» регистр букв у всех слов из строки $string (глобально), вот что получается: WORDS DON"T COME EASY
Что такое альтернатива? Это вот что:
Данный фрагмент:
while(<>){
if(/^exit$/){last}
if(/^quit$/){last}
if(/^stop$/){last}
}
Можно заменить этим, с использованием альтернативы:
while(<>){
# это перебор со сравнением с текстом, т.е. если хотя бы
# один фрагмент из альтернативы совпадет с текстом,
# то возвращаемое значение примет истину, т.е. в данном
# случае, если пользователь введет в STDIN, exit или quit или
# же stop, то мы завершим цикл
if(/^(quit|exit|stop)$/){
last;
}
}
Нужно проверить, есть ли в строке слово ALPHA и слово BETA:
$string = "BETALPHA";
if(($string =~ /ALPHA/) and ($string =~ /BETA/)){
print "OK";
}
Или так:
if($string =~ /(?=.*ALPHA)(?=.*BETA)/){
# так не делать – пример для перекрывающихся совпадений
print "OK";
}
$string =~ /^(?:(?!PAT).)*$/
Выражение истинно, если шаблон /PAT/ не совпадает (аналогично $string !~ /PAT/). Далее насчет конструкций (?=шаблон) и т. д.
В следующем примере ищется слово, за которым следуют три восклицательных знака, но сам пробел не включается в результат поиска:
$string = "One! Two!! Three!!! And some words next...";
$string =~ m/(w+(?=!!!))/;
print $1;
Ответ: Three
Вот неплохой пример, взятый из книги Perl Cookbook.
$string = "1234567890";
@nonlap = $string =~ /(ddd)/g;
@yeslap = $string =~ /(?=(ddd))/g;
print "Non-overlapping: @nonlap ";
print "Overlapping: @yeslap ";
Вот что получается:
Non-overlapping: 123 456 789
Overlapping: 123 234 345 456 567 678 789 890
|
Идем дальше: совпадение с любым символом, т.е. точка (.), например:
$string = "secret password";
$string =~ s/./*/g;
print $string;
Хоть * – это квантификатор, но в данном случае предшествующего элемента нет, поэтому используется лексическое значение и результат такой:
Но мнимый символ точка не совпадает с символом переноса строки, вот пример:
$string = "some text new string";
$string =~ s/./*/g;
print $string;
Вот вывод программы:
Как видите, перенос строки остался, чтобы разрешить точке совпадать с переносом строки, нужно добавить модификатор s и все:
$string = "some text new string";
$string =~ s/./*/gs;
print $string;
Результат:
То, что нужно...
Проверяем, в строке $string больше 80 символов или нет:
$string = "simple text";
$_ = $string;
if(/.{80,}/){
print "Length of text is OK";
}else{
print "Length of text isn"t OK";
}
Теперь поговорим о квантификаторах.
$string = "It is some text!!!!!!!!!!!!!!!!!";
$string =~ s/!+/!/;
print $string;
В этом случае вывод будет таков: It is some text!
Квантификатор + означает одно или более совпадений, но т.к. квантификаторы количества изначально являются «жадными» (о жадности квантификаторов ниже), то в данном случае квантификатор + заменит самую длинную последовательность восклицательных знаков, а вот если поставить этот квантификатор – *, то в первом случае он отработает также, но если строку заменить на эту:
$string = "It is some text";
Тогда результат будет следующим: !It is some text
Удивлены? Да, именно такой результат и будет, (не забыли – квантификатор * означает ноль или более совпадений), т.к. в данном случае нет восклицательных знаков, то он удовлетворяется нулем.
Теперь посмотрим, если нужно определенное количество символов, например:
$string = "It is some text!!!!!!!!?!!!!!!!!";
$string =~ s/!{8}//;
print $string;
Здесь мы удаляем ровно 8 восклицательных знаков, поэтому результат будет такой: It is some text?!!!!!!!!
Если мы добавим модификатор g, то и последние 8 восклицательных знаков тоже исчезнут, а вот если сделать 9 восклицательных знаков, тогда ничего не будет заменено...
Теперь о «жадности» квантификаторов: квантификатор заменит самую длинную серию – это и есть жадность.
Разберем такой пример, вы хотите заменить «That is» на «That"s»:
# вероятно, вы сделате так:
$string = "That is reality, isn"t it?";
$string =~ s/.*is/That"s/;
print $string;
Результат будет такой: That"sn"t it?
Так как «жадность» квантификаторов проявляется слева направо, т.е. будет выбрана максимальная серия слева направо. Это исправить легко – просто добавляем знак вопроса «?»:
$string =~ s/.*?is/That"s/;
И теперь результат будет тот, что нужно: That"s reality, isn"t it? Вопросительный знак обозначает ноль или одно совпадение, поэтому как только найдено первое совпадение оно сразу заменяется, и все.
Вот список минимальных квантификаторов: +?, *?, ??, {}?.
Дальше несколько примеров с квантификаторами.
Удаляем начальные пропуски:
$string = " Some text";
$string =~ s/^s+//;
Удаляем конечные пропуски:
$string = "Some text ";
$string =~ s/s+$//;
А теперь классы и группы.
Класс символов – это символ, или список символов, заключенные в квадратные скобки. Любой символ из квадратных скобок сопоставляется со строкой для сравнения, например:
$string = "Berry";
if ($string =~ /[br]/){
print "B and R";
}
В данном случае оператор m/.../ используется в скалярном контексте, поэтому в случае совпадения шаблона возвращает истину, иначе пустую строчку. Знак дефис (-) имеет особый смысл для класса символов – диапазон символов (в начале указывается начальный символ, потом конечный), например:
$string = "some Text";
if($string =~ /[A-Z]/){
print "Uppercase here...";
}
Или вот другой пример:
# если есть буквы, то возвращает истину
$string = "some text";
if($string =~ /[A-Za-z]/){
print "Letters here...";
}
Здесь диапазон от A до Z заглавных букв, т.е. если в строчке есть хотя бы одна заглавная буква то оператор m/.../ возвращает истину. А если перед открывающей квадратной скобкой, поставить символ ^, тогда смысл меняется на противоположный, т.е. символ сравнивается, с любым не входящим в этот класс.
Вот пример:
$string = "abcdef";
if($string =~ /[^abcde]+/){
print "In class";
}
В данном случае результат будет такой: In class
А если немного поменять $string:
$string = "abcde";
Тогда ничего не будет выведено, т.к. это и есть те символы, которых не должно быть...
$string = "some_text 12345";
$string =~ /([a-z_]+)s+(d+)/g;
print "Text: $1 Numbers: $2";
Вот вывод программы:
Text: some_text
Numbers: 12345
|
Что-то заключенное в скобки – это группа, с которой ассоциируется специальная переменная вида $1 или ссылка на группу вида 1.
В первой группе диапазон строчных символов от a до z и символ подчеркивания (если вы хотите использовать дефис как символ в группе, а не как диапазон, то поставьте перед ним обратную косую черту). Со вложенными скобками также – на каждую открывающую скобку формируется специальная переменная (например, $1). Меняем местами слова при помощи оператора s/.../.../:
$string = "she loves me not";
# как видите, здесь четыре группы
$string =~ s/(w+)s+(w+)s+(w+)s+(w+)/$4 $3 $2 $1/g;
print $string;
Вот вывод нашей программы: not me loves she
Есть и такой вариант, без регулярных выражений:
$string = "she loves me not";
$string = join(" ",reverse split(" ",$string));
Результат такой же, как и в первый раз.
Также с помощью функции reverse можно проверить, является ли слово палиндромом:
$word = "reviver";
print $is_palindrome = ($word eq reverse($word));
Очень удобно работать с обработкой информации, например, от данных выхода какой-нибудь программы...
Рассмотрим более сложный пример:
# вот какие-нибудь входные данные
$string = " x = -15.32";
$string =~ ([a-z]+)s*=s*([+-]?d+.?d*)
/x;
В переменной $1 находится «x», а в переменной $2 – «-15.32». Теперь давайте разберемся, что к чему. В первой группе класс символов, т.е. диапазон символов от a до z строчных букв (начиная от одной буквы и более, т.к. используется квантификатор «+», квантификаторы действуют только на предыдущий символ, или класс символов). Далее, s* значит сколько угодно пробельных символов, или ни одного, затем символ «=», и опять же s*. После этого идет группа, в которой есть класс символов из «+» и «-», т.е. это значит, что символ «+» или «-» может присутствовать или не присутствовать (напомню: квантификатор «?» – ноль или одно совпадение).
Далее, d+ одно или сколько угодно чисел, затем точка или ее нет, а затем ноль или сколько угодно чисел.
А теперь чуть-чуть усложним нашу задачу, пусть данные будут такие (например, их вводил человек и допустил пару неточностей):
x = 15.1 y = 3. z = +12.22
x=3 y= 5 z=3.4
x=1 y=2 z=3
|
То есть в переменной это будет выглядеть так:
$string = " x = 15.1 y = 3. z = +12.22 x=3 y= 5 z=3.4 ї
nx=1 y=2 z=3";
$c=0; # счетчик итераций
while($string =~ m/
([a-z]+)s*=s*([+-]?d+.?d*)s+
([a-z]+)s*=s*([+-]?d+.?d*)s+
([a-z]+)s*=s*([+-]?d+.?d*)/xg){
# используем расширенный вид – для более легкого чтения
$x = $2; # а вот и данные, т.е. x, y, z
$y = $4;
$z = $6;
# далее что угодно делаем с данными,
# а в переменных $1, $3, $5 находятся имена переменных
# "вытянутых" из данных на всякий случай
$c++;
print "x = $x ";
print "y = $y ";
print "z = $z ";
print "Iteration number: $c ";
}
Вывод программы будет таков:
x = 15.1
y = 3.
z = +12.22
Iteration number: 1
x = 3
y = 5
z = 3.4
Iteration number: 2
x = 1
y = 2
z = 3
Iteration number: 3
|
А теперь списковый контекст оператора m/.../:
$string = "words 999 text";
# извлекает в массив @arr все "слова",
# а точнее, диапазон строчных или заглавных букв от a до z
@arr = ($string =~ m/([A-Za-z]+)/g);
print join " ",@arr;
Вот что получится: words text
А теперь насчет использования внутренних ссылок.
$string = "go to main page";
$string =~ m%<(a|body)s+(.*)>(.*)%;
$link_name = $3;
print $link_name;
Вывод такой: go to main page
Здесь 1 – это внутреняя ссылка на первую группу, т.е., например, в первой группе шаблон совпал с тегом A, тогда 1 тоже будет равно тому же. Точно также, как и с другими ссылками 1,2,3...
С помощью функции split() и шаблонов текст можно разбивать на куски, например, терминатор строки (или разделитель записей) должен быть фиксированной величиной, поэтому чтобы прочитать файл по шаблону, нужно сделать следующее:
open(FH,"file.txt") or die $!;
undef $/;
@chunks = split(/ | /,);
Данный кусок заносит в массив @chunks строки файла независимо от того, создан он в Unix-системе или в Windows.
Также с помощью split() можно разбить строку на символы, т.е. каждый элемент массива будет равен одному символу, это делается так:
$string = "some letters";
@l = split(//,$string);
Выше я в основном рассматривал работу с операторами m/.../ и s/.../.../, а теперь посмотрим на оператор трансляциии текста, т.е. tr/.../.../. Начнем с его модификаторов:
- d – удаляет непарные символы;
- с – первый аргумент это полный список из 256 символов, кроме тех, которые указаны в аргументе;
- s – удаляет повторяющиеся символы, образованные при замене.
Давайте посмотрим, как используется модификатор c:
$string = "This is some text";
$string =~ tr[A-Za-z][*]c;
print $string;
Вот что получится: This*is*some*text
В этом случае заменяются все символы, кроме тех, которые указаны в первом аргументе (т.е. всех латинских), в данном случае – это пробел.
Удаляем удвоенные, утроенные и т. д. символы:
$string = "Thiiiisss is sooooome teeeeeeext";
$string =~ tr[A-Za-z][]s;
# если второй аргумент пуст, то в него подставляется
# первый аргумент
print $string;
Результат: This is some text
Пример: я скачал книгу, но в ней был такой недостаток: заглавные буквы были инвертированы строчными и наоборот, вот как я справился с этим:
$string = "tHIS iS bUGGY tEXT !!!";
$string =~ tr{A-Za-z}{a-zA-z};
print $string;
Получилось, что надо: This Is Buggy Text !!!
Пересчитываем количество букв в строке:
$string = "Some text";
$count = ($string =~ tr{A-Za-z}{});
print $count;
Результат: 8
Немного изменив выражение, можно подсчитать количество небуквенных символов:
$string = "Some _unique_text_ !!!";
$count = ($string =~ tr{A-Za-z}{}c);
print $count;
Ответ – 8, или вместо диапазона A-Za-z можно подставить 0-9 и тогда, в $count будет количество цифр в строке.
Оператор tr/.../.../ возвращает количество успешных замен, если не было сделано никаких замен – возвращает ноль. Таким образом можно подсчитать количество определенного символа в строке, например, подсчитываем количество буквы i в строке.
$string = "This is some text !";
$count = ($string =~ tr/i/i/);
print $count;
Результат: 2
Если у оператора tr/.../.../ нет модификаторов, тогда ее аргументы должны быть одинаковой длины, а если второй аргумент длиннее первого – он усекается до длины первого аргумента, например:
$string = "Some numbers: 7321";
$string =~ tr/7321/0-9/;
print $string;
Не зная того, что я сказал выше, вы, наверное, хотели заменить цифры 7321 на 0123456789, но результат вот какой: Some numbers: 0123
Т.е. команда tr/7321/0-9/ равна следующей: tr/7321/0123/.
Даже если цифры будут не по порядку, т.е.:
$string = "Some numbers: 7 then 3 then 2 and then 1";
$string =~ tr/7321/0-9/;
Результат будет таким: Some numbers: 0 then 1 then 2 and then 3
Или вот другой пример:
$string = "some text is here";
$string =~ tr/oet/ina/;
print $string;
Вот что получилось: simn anxa is hnrn
То есть к каждому символу в первом аргументе сопоставляется какой-то символ из второго аргумента.
А вот если первый аргумент длиннее второго, тогда последний символ второго аргумента повторяется до тех пор, пока не будет по длине как первый, например, tr/0-9/abc/ это равно tr/0123456789/abcccccccc/, вот так.
Здесь мы рассмотрим множество решений, собранных мной:
Поверяем на правильность адрес электронной почты:
$string = "some@mail.ru";
if($string =~ /([^<>(),;s]+@[^<>(),;s]+)/){
print "Valid mail";
}else{
print "Invalid mail";
}
Это примитивная проверка, и рассчитана она только на то, чтобы пользователь не ошибся при вводе. Лучше всего проверить, правилен ли почтовый адрес – это послать письмо с подтверждением.
Выделяем имя и расширение программы:
$progname = "/usr/bin/perl.txt";
$progname =~ m%(^.*/)(.*).(.*)%;
# $1 – путь до программы: /usr/bin/
# $2 – программа: perl.txt
# $3 – программа без расширения: perl
# $4 – расширение программы: txt
В основном это нужно для консольных приложений, вместо $progname, подставьте специальную переменную $0, хранящую имя данного исполняемого файла.
Поиск n-го совпадения, пример: в каждом четном русскоязычном слове у каждой нечетной буквы поднять регистр. Решение:
use locale;
# для русских букв (если настроен локальный контекст), т.е. утверждение w+ используется для русских слов
$string = "здесь какие-нибудь слова и что-нибудь еще...";
$wc=0;
$string =~ s/(w+)/
if(++$wc % 2 == 0){
chars($1)
}else{
$1
}
/igex;
sub chars{
my($text)=@_;
my($res,$chr,$i);
for($i=0;$i<=length($text);$i++){
$chr = substr($text,$i,1);
if($i % 2 == 0){
$res.=uc($chr);
}else{
$res.=$chr;
}
}
return $res;
}
print $string;
Вот какой результат будет: здесь КаКиЕ-нибудь СлОвА и ЧтО-нибудь ЕщЕ...
Программа Urlify из книги Perl Cookbook:
$urls = "(http|telnet|gopher|file|wais|ftp)";
$ltrs = "w";
$gunk = "/#~:.?+=&%@!-";
$punc = ".:?-";
$any = "${$ltrs}${$gunk}${$punc}";
while(<>){
s{($urls:[$any]+?)(?=[$punc]*[^$any]|$)} {$1}igox;
print;
}
Определение своих тегов:
$string = "some text, some uppercase text, than normal text and uppercase text again";
$string =~ s#((?:(?!).)*)#uc($1)#igmse;
print $string;
Результат такой: some text, SOME UPPERCASE TEXT, than normal text AND UPPERCASE TEXT AGAIN
Вложенные теги не поддерживаются.
Хочу сказать пару слов о «доставании» атрибутов из HTML-тегов, если вы не уверены в структуре, тогда это надо делать через модули типа HTML::Parser и подобные ему. А если вам нужна скорость и может быть потеря нескольких ссылок, тогда воспользуйтесь моим решением:
ї
title =" cmon "> test
new link
~;
sub cut_tag{
my($tag,$param,$text)=@_;
my(@mas,$param_line);
if($tag and $param and $text){
while($text =~ m#<($tag)(.+?)>(.*?)#igso){
$param_line = $2;
if($param_line =~ /$param/i){
$param_line =~ m/$params*=s*([""]?) (.*?)1/ogis;
push(@mas,$2);
}
}
return @mas;
}else{
return 0;
}
}
# первый аргумент – HTML-тег, из которого нужно доставать
# атрибут, второй аргумент – это и есть атрибут, который
# будет вынут, а третий аргумент – это текст, из котрого все
# это будет доставаться (все извлекается в массив)
@m = cut_tag("a","href",$href);
print join " ",@m;
Вот результат:
new.htm
Далее несколько решений, взятых из книги Perl Cookbook.
Удаление комментариев C (не идеальное):
$string =~ s{
/*
.*?
*/
}[]gsx;
Поиск всех слов, записанных символами верхнего регистра:
@capwords = ($string =~ m/([^Wa-z0-9_]+)/g);
Поиск всех слов, записанных символами нижнего регистра:
@capwords = ($string =~ m/([^WA-Z0-9_]+}/g);
Поиск всех слов, начинающихся с буквы верхнего регистра:
@icwords = ($string =~ m/([^Wa-z0-9_][^WA-Z0-9_]*)/g);
Вроде бы разобрались с операторами поиска/замены текста, на самом деле это очень маленькая доля того, что можно рассказать о регулярных выражениях, но как я говорил в самом начале статьи, что попытаюсь как можно лаконичнее объяснить материал, надеюсь, у меня получилось.
Удачи!