Последнее в категории Регулярные выражения

^\w+.(\w+)$

| Комментариев: 12

Еще одно интересный вопрос для собеседования.

Дано регулярное выражение, с которым сопоставляется строка:

$str =~ /^\w+.(\w+)$/

Что окажется в переменной $1 при таких значениях $str?

  • ab.cd
  • abcd

Про /m

| 1 комментарий

Бывает, что какой-то возможностью либо настолько редко пользуешься, либо она настолько неудобна, что про нее забываешь напрочь.

Сегодня меня заново познакомили (hsw++) с модификатором регулярных выражений /m.

В дизайне регулярных выражений Perl 5 есть аж пять метасимволов, совпадающие с границами строк (физических или логических):

^ $ \A \Z \z

И на все это наслаивается модификатор /m, изменяющий действие первых двух из списка. И, до кучи, пара символов для переноса строк (\n и \R). Полное безобразие. Иными словами, отказ от /m в Perl 6 — очень правильное решение.

Я погрепал свои старые файлы, и обнаружил, что этот модификатор, даже когда и используется, ставился или по инерции, или, в совсем старом коде, из-за трепетного отношения к магическим в то время регулярным выражениям.

Perl 5.12 и \w

| Комментариев: 2

Некоторое время назад в файле perl5110delta появился странный раздел о том, что метасимволы \w и \d в регулярных выражениях должны совпадать только с ASCII-символами:

digit   \d  [0-9]
word    \w  [0-9A-Z_a-z]

К счастью, это пока не перекочевало в Perl 5.12, причем независимо от того, определен ли при компиляции символ PERL_LEGACY_UNICODE_CHARCLASS_MAPPINGS или нет.

Программа, от которой ожидается совпадение \w с русскими буквами, по-прежнему работает:

use v5.10;
use utf8;
use open qw(:utf8 :std);

"123abcабв" ~~ /(\w+)/;
say $1;

На печать выводится 123abcабв.

Когда именованные сохраняющие скобки встречаются в регулярных выражениях вместе с обычными, может возникнуть вопрос о том, как перл будет эти скобки нумеровать.

use v5.10;
use strict;

my $string = "Fri Mar 19 13:59:06 MSK 2010";
$string =~ /(?<tz>([A-Z]{3}))/;
say $+{tz};
say $1;
say $2;

Программа трижды напечатает MSK.

Итого, ответ на вопроса простой: учитываются все скобки.

Trim

| Комментариев: 3

Себастьян Виллинг затронул в своем блоге вечную тему о том, как отрезать от строки пробелы с обеих сторон, сделав это в одно действие.

Вместо двух замен

$str =~ s/^\s+//;
$str =~ s/\s+$//;

Себастьян указывает на такой вариант:

$str =~ s/^\s*(.*?)\s*$/$1/;

Интересно почитать и комментарии, где предлагают несколько похожих вариантов с альтернативами в регулярном выражении:

$str =~ s/(^\s+|\s+$)//g;

$str =~ s/(?:^\s+|\s+$)//g;

$str =~ s/^\s*|\s*$//g;

$str =~ s/^\s+|\s+$//g;

P. S. В комментариях, кстати, видно, как почти одно и то же выражение разные люди записывают по-разному.

В магазинах появился русский перевод книги O’Reilly «Регулярные выражения. Сборник рецептов» (Regular Expressions Cookbook), написанная Яном Гойвертсом и Стивеном Левитаном.

Регулярные выражения. Сборник рецептов / Regular Expressions Cookbook

(На оригинальной книге, которую я получил от O’Reilly, тоже есть черная полоса с надписью Includes a Regular Expressions Tutorial.)

Содержание

  1. Введение в регулярные выражения
  2. Основные навыки владения регулярными выражениями
  3. Программирование с применением регулярных выражений
  4. Проверка и форматирование
  5. Слова, строки и специальные символы
  6. Числа
  7. URL, пути и адреса в Интернете
  8. Разметка и обмен данными

Все примеры в рецептах даны на семи диалектах регулярных выражений: .NET, Java, JavaScript, PCRE, Perl, Python и Ruby.

Цена на books.ru вдвое меньше (650 руб.), чем в «Московском доме книги» (1135 руб.).

Еще раз про \K

| Нет комментариев

Метасимвол \K в регулярных выражениях Perl 5.10 не только полезен, но и, как выясняется на реальном проекте, меняет мысли разработчика :-)

Время от времени возникает задача выделить из текста одно или несколько предложений, чтобы сформировать сниппет записи. Неправильный подход — обрезать строку на заданном числе символов и поставить многоточие (Фейсбук в транслящиях вообще обрезает строку по байтам, и от русских букв иногда отрезается половинка юникодной последовательности). Чуть более приятный результат (но подход настолько же неправилен) дает отсечение по границе слова (как делает Яндекс в поисках по блогам).

Обрезать же текст на границе предложения (ну или по крайней мере, после точки) одним регулярным выражением без \K не очень удобно (приходится задумываться о том, чтобы угодить коротким предложениям).

С метасимволом \K это проще:

$snippet =~ s{^.{150}[^\.]+\.\K.*$}{};

\K

| Комментариев: 3

В регулярных выражениях, доступных в Perl 5.10, появился метасимвол \K, который устанавливает точку «невозврата», до которой строка не должна измениться даже после замен.

Мой коллега порадовал еще раз, применив эту фичу в работе.

my $uri = $ENV{REQUEST_URI};
$uri =~ s{^.+?\K\?.*$}{};

Здесь в переменной $uri оказывается адрес без строки запроса, если она была.

Без использования метасимвола \K подобные замены приходилось делать явно, применяя захват и соответствующие переменные для подстановки:

$uri =~ s{^(.+?)\?.*$}{$1};

В документации замечено, что использование \K намного эффективнее (much more efficient), чем явная замена.

До Perl 5.10 аналогичная функциональность была возможна при подключенном модуле Regexp::Keep.

%+ и %-

| Нет комментариев

В Perl 5.10 появилась возможность давать имена сохраняемым совпадениям в регулярных выражениях. Чтобы эффективно пользоваться этим нововведением, необходимо разобраться в том, как работают хеши %+ и %-, куда попадают совпавшие фрагменты.

Именованные скобки в регулярных выражениях — это конструкция вида (?<name>...). Если сопоставление оказалось успешным, то к искомому фрагменту возможно обратиться через элементы хешей %+ и %-. Например, в этом примере совпадение будет доступно в переменной %+{name}.

Сложности начинаются, если регулярное выражение составлено таким образом, что в нем оказывается несколько скобок с одинаковым именем. Такое (если это сделано намерено) может произойти в том случае, когда автор выражения, например, предполагает, что на реальных данных должен совпадать только один именованный фрагмент. В другом случае регулярное выражение может быть составлено из отдельных подвыражений: здесь возможны как многократные вхождения, так и многократное сопоставление в цикле.

Вот пример, где из строки 120 EUR in USD, please извлекают названия валют.

use v5.10;
use strict;
use Data::Dumper;

my $string = '120 EUR in USD, please';
my @currencies = $string =~ /(?<currencyName>[A-Z]{3})/g;
say Dumper(\@currencies);
say Dumper(\%+);
say Dumper(\%-);

Именованное выражение ?<currencyName> встречается один раз, но обрабатывается в цикле. В массиве @currencies окажется два элемента:

$VAR1 = [
          'EUR',
          'USD'
        ];

А в хешах %+ и %- будет лишь по одному:

$VAR1 = {
          'currencyName' => 'USD'
        };

$VAR1 = {
          'currencyName' => [
                              'USD'
                            ]
        };

Важно обратить внимание на то, что в эти хеши попала последняя совпавшая подстрока.

Теперь вынесем часть регулярного выражения в отдельную переменную и дважды применим ее в сохраняющих скобках с разными именами:

my $re = '[A-Z]{3}';
$string =~ /(?<from>$re).*(?<to>$re)/;
say Dumper(\%+);
say Dumper(\%-);

Поскольку сейчас имена не пересекаются, все совпадения окажутся доступными:

$VAR1 = {
          'to' => 'USD',
          'from' => 'EUR'
        };

$VAR1 = {
          'to' => [
                    'USD'
                  ],
          'from' => [
                      'EUR'
                    ]
        };

Обратить внимание, кстати, стоит на то, что в хеше %- поименованные элементы всегда содержат списки, даже если они состоят из одного элемента.

Наличие списков, однако, помогает, когда одноименных элементов становится больше:

$string =~ /(?<currencyName>$re).*(?<currencyName>$re)/;
say Dumper(\%+);
say Dumper(\%-);

В этом примере хеш %+ вновь содержит один элемент — последнее совпадение, но в ключе $-{currencyName} теперь список элементов из двух строк:

$VAR1 = {
          'currencyName' => 'EUR'
        };

$VAR1 = {
          'currencyName' => [
                              'EUR',
                              'USD'
                            ]
        };

Ничто не мешает вынести и само имя фрагмента в отдельную переменную:

my $named = '?<currencyName>[A-Z]{3}';
$string =~ /($named).*($named)/;
say Dumper(\%+);
say Dumper(\%-);

Результат будет таким же, как и в предыдущем случае:

$VAR1 = {
          'currencyName' => 'EUR'
        };

$VAR1 = {
          'currencyName' => [
                              'EUR',
                              'USD'
                            ]
        };
Важно помнить, что если именованное выражение было необязательным и не совпало, его следы все равно будут присутствовать в хеше %-:

$string = "120 EUR, please";
$string =~ /($named).*($named)?/;
say Dumper(\%-);

Этот пример вернет хеш со списком из строки и значения undef:

$VAR1 = {
          'currencyName' => [
                              'EUR',
                              undef
                            ]
        };

Порядок элементов списка совпадает с порядком именованных скобок в регулярном выражении.

Страницы

  • img

Об архиве

Эта страница содержит последние записи категории Регулярные выражения.

Предыдущая категория — Разработка.

Следующая категория — Сообщество.

Смотрите новые записи на главной странице или загляните в архив, где есть ссылки на все сообщения.