В одной из прошлых публикаций мы начали разговор о регулярных выражениях языка Perl, а точнее, о том, как с помощью этих выражений составляются шаблоны для поиска различных данных. Однако зачастую одним только поиском задача не исчерпывается. Как правило, возникает необходимость совершить некое действие над найденным фрагментом данных. В этой статье мы рассмотрим варианты решения нескольких задач такого типа. Предполагается, что читатель внимательно ознакомился с предыдущей публикацией, что он знаком с азами записи и исполнения Perl-скриптов, протестирует приведенные здесь примеры решения задач, поэкспериментирует с заменой метасимволов и так далее.
Проблема: в тексте встречаются даты, записанные в формате
Метасимвол \d соответствует цифрам, метасимвол-повторитель {n} обозначает количество повторений предыдущего символаСинтаксис операций замены по шаблону или, иными словами, операций подстановки, в языке Perl выглядит следующим образом:
Обратите внимание на круглые скобки. Они не только разбивают регулярное выражение на некие фрагменты, но и заставляют Perl присваивать значения этих фрагментов переменным вида $n, где n — положительное число. В нашем случае переменной $1 будет присвоено значение \d{4}, то есть, год, переменной $2 — \d{2} (месяц), переменной $3 — \d{2} (день). Зная об этом, мы сможем записать вторую часть подстановки, то есть на что меняем.
Обратный слеш \ заставляет воспринимать следующую за ним точку именно как точку, а не как метасимвол подстановки любого символаВыглядеть эта часть будет так:
#!/usr/bin/perl
print "Content-Type: text/html\n\n"; # 1-я строка
print "<html>"; # 2
$old_date = "2004-09-30"; # 3
$text_in = "Допустим, в некотором тексте встречается дата <b>$old_date</b>."; # 4
print "$text_in<br>"; # 5
($text_out = $text_in) =~ s/(\d{4})-(\d{2})-(\d{2})/$3\.$2\.$1/; # 6
$text_out =~ s/\D+/В результате применения поиска и замены по шаблону
формат даты изменился: <b>/; # 7
print "$text_out</b><br>"; # 8
print "<hr>Обратите внимание, что первоначальный текст не изменился:<br>
<i>$text_in</i><br>
Это и понятно, ведь в шестой строке скрипта мы обрабатывали
не саму переменную <b>\$text_in</b>, а только ее копию."; # 9
Операнд связывания =~ применяется при поиске и замене по шаблонуЧто происходит при исполнении этого скрипта? В четвертой строке создается определенный текст (переменная $text_in), содержащий дату в «неправильном» формате. В шестой строке создается переменная $text_out, в которую копируется значение переменной $text_in, а затем применяется составленное нами выражение подстановки. Таким образом, в новую переменную записывается тот же текст, но дата в нем меняет свой формат. В седьмой строке мы слегка изменяем текст «на выходе». Для этого опять-таки применяется операция подстановки: с помощью метасимвола \D+ мы находим все нечисловые символы в тексте, от его начала до первой встречающейся цифры, и заменяем их на другой текст. Кстати, попробуйте удалить метасимвол-повторитель + и взгляните, как изменится результат работы скрипта.
Проблема: известен полный путь к файлу, например,
Эта задача довольно проста. В общем случае путь к файлу выглядит следующим образом:
- латинская буква, соответствующая имени диска, и двоеточие после нее,
- имена каталогов, в которых расположен файл, ограниченные символом слеша / с обеих сторон,
- имя файла,
- расширение файла.
Запишем эти составные части в синтаксисе регулярных выражений:
- [a-z]: — обозначает одну любую букву латинского алфавита и двоеточие после нее,
- .*\/ — обозначает любое количество символов, включая последний слеш в пути к файлу,
- .* — обозначает любое количество символов,
- \..* — обозначает любое количество символов от последней точки в пути к файлу.
Операция поиска по шаблону в общем случае записывается очень просто: =~ /шаблон/Теперь нам достаточно проверить имеющееся значение переменной на соответствие данному шаблону. Для этого необязательно использовать подстановку, ограничимся простой операцией поиска по шаблону. Причем, как мы помним, значения всех фрагментов регулярного выражения, помещенных в круглые скобки, Perl будет автоматически присваивать переменным $1, $2 и так далее. Отсюда выводим простенькую функцию:
$file_path_and_name = "c:/temp/somefile.txt";
# Проверяем соответствие значения переменной
# составленному нами шаблону.
# в случае успеха переменным $n присваиваются некоторые значения:
if ($file_path_and_name =~ /([a-z]:)(.*\/)(.*)(\..*)/ ) {
# Если условие выполнено, выводим на экран необходимые значения:
print "Файл расположен на диске <b>$1</b><br>
Файл расположен в каталоге <b>$1$2</b><br>
Имя файла: <b>$3</b><br>
Расширение файла: <b>$4</b><br>";
# Делаем подстановку для изменения имени диска:
($new_disc = $file_path_and_name) =~ s/[a-z]/j/;
print "<hrgt;В результате подстановки файл \"лег\" на другой диск: <b>$new_disc</b>";
}
# Если значение переменной не соответствует шаблону,
будет выведено предупреждение:
else {
print "Внимание! Значение переменной не соответствует шаблону пути к файлу!";
}
Обратите внимание на сделанную нами подстановку. В результате этой подстановки первая латинская буква, встреченная в пути файла, будет заменена на j. Здесь важно понимать: для того чтобы скрипт отработал нормально, данная подстановка должна быть включена в тело оператора if. Зачем? Для дополнительной подстраховки. Вдруг в переменной по каким-то причинам первой стоит не латинская буква, а знак препинания или буква из другого алфавита? Например:
Проблема: некоторые браузеры и почтовые клиенты неверно отображают отдельные спецсимволы типа короткого и длинного тире, типографских кавычек «елочкой», буквы ё и т.д. Необходимо заменить все подобные символы, встречающиеся в тексте, на ближайшие аналоги (простые кавычки, дефис, букву е и т.д.).
Для подобной операции можно применить так называемую транслитерацию. Синтаксис таков:
- В обоих списках транслитерации можно использовать одиночные символы или диапазоны (перечни) вроде [a-z], [а-я], [0-9] и т.д. Например,
tr/[A-Z]/[a-z]/ обозначает замену букв верхнего регистра на соответствующие буквы нижнего. - Порядок следования символов и диапазонов в списках замены должен совпадать. Скажем, если длинное тире в первом списке стоит на пятой позиции, то во втором списке дефис так же должен стоять на пятой позиции.
- В отдельных случаях можно использовать в списках замены непосредственно одиночные символы, например, тот же дефис. Но для пущей корректности правильно использовать коды, например, метасимволы вида \xDD, где DD — шестнадцатеричный код символа (см. предыдущую публикацию на тему регулярных выражений).
Для демонстрации одного из решений описанной выше задачи предлагаем воспользоваться такой функцией:
$old_text = "«Привет!» — сказала Матрёна.";
print "До транслитерации: <b>$old_text</b><br>";
($new_text = $old_text) =~ tr/\xAB\xBB\x97\xB8/\x22\x22\x2D\xE5/;
print "После транслитерации: <b>$new_text</b>";
Здесь в списки транслитерации подставлены следующие шестнадцатеричные коды:
- xAB — открывающие типографские кавычки,
- xBB — закрывающие типографские кавычки,
- x97 — длинное тире,
- xB8 — буква ё,
- x22 — обычные кавычки,
- x2D — дефис,
- xE5 — кириллическая буква е,
Таков список задач на сегодня. Еще раз напомню, что ни сами задачи, ни их решения никоим образом не претендуют на высокое звание образцов для подражания. Они лишь призваны наглядно продемонстрировать некоторые основные принципы использования регулярных выражений в программировании на языке Perl. Ждем ваших вопросов и собственных вариантов решений в комментариях к этой статье.
Ссылки по теме
- Спецификация регулярных выражений Perl
- Perl: бороться и искать
Статья получена: hostinfo.ru