The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

НАИМЕНОВАНИЕ

perlrequick - быстрый старт использования регулярных выражений Perl

ОПИСАНИЕ

Эта страница описывает самые простые шаги для понимания, создания и использования регулярных выражений ('регексов' от regex, сокр. от regular expression) в Perl.

Руководство

Простой поиск соответствия слова

Простейший регекс это просто слово, или, более обще, строка символов. Регекс, состоящий из слова, соответствует любой строке, содержащей это слово:

    "Hello World" =~ /World/;  # совпадение

В этом выражении, World это регекс и слеши // вокруг /World/ говорят Perl искать строку для совпадения. Оператор =~ связывает строку с регексом и выдает значение true если регекс совпадает, или false если нет. В нашем случае, World совпадает со вторым словом в "Hello World", таким образом выражение истинно. У этой идеи есть несколько вариаций.

Такие выражения полезны в условиях:

    print "Найдено\n" if "Hello World" =~ /World/;

Смысл совпадения может быть полностью изменен при помощи оператора !~:

    print "Не совпало\n" if "Hello World" !~ /World/;

Последовательность строк в регексе может быть заменена переменной:

    $greeting = "World";
    print "Найдено\n" if "Hello World" =~ /$greeting/;

Если вы ищите в $_, то эту часть $_ =~ можно опустить:

    $_ = "Hello World";
    print "Найдено\n" if /World/;

Наконец, разделители по умолчанию // для поиска могут быть изменены на произвольные разделители, но тогда в начале должны быть буква 'm':

    "Hello World" =~ m!World!;   # найдено, разделители '!'
    "Hello World" =~ m{World};   # найдено, отметим совпадение '{}'
    "/usr/bin/perl" =~ m"/perl"; # найдено после '/usr/bin',
                                 # '/' стал произвольным символом

Регексы должны точно совпадать с частью строки чтобы выражение было true:

    "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/ # найдено в кодах ASCII, хотя и странный способ искать 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', or 'rat'
    "abc" =~ /[cab]/; # найдет 'a'

В последнем выражении, даже несмотря на то, что 'c' является первым символом в классе, самой ранней точкой совпадения регекса будет 'a'.

    /[yY][eE][sS]/; # найдет 'yes' в регистронезависимом поиске
                    # 'yes', 'Yes', 'YES', etc.
    /yes/i;         # также найдет 'yes' без учета регистра

Последний пример показывает поиск с модификатором 'i' , который делает поиск независимым от регистра (ignore registr).

Классы символов также имеют обычные и специальные символы, но наборы обычных и специальных символов внутри класса символов отличаются от тех, которые есть за пределами класса символов. Спец символами для классов символов являются -]\^$ и для их поиска используется экранирование символом escape:

   /[\]c]def/; # найдет ']def' или 'cdef'
   $x = 'bcr';
   /[$x]at/;   # найдет 'bat, 'cat', или 'rat'
   /[\$x]at/;  # найдет '$at' или 'xat'
   /[\\$x]at/; # найдет '\at', 'bat, 'cat', или 'rat'

Особый символ '-' действует как оператор диапазона, когда он стоит между символами, так что громоздкая запись [0123456789] и [abc...xyz] становится стройной [0-9] и [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 имеет несколько сокращений для общих классов символов. (Это те определения , которые Perl использует в ASCII-безопасном режиме с модификатором /a. В противном случае они могут соответствовать многим не-ASCII Юникодным символам. Смотри "Последовательности с обратной косой чертой" in perlrecharclass для деталей.)

  • \d это цифра и содержит

        [0-9]
  • \s это пробельный символ и содержит

        [\ \t\r\n\f]
  • \w это символ слова (буквоцифры или _) и содержит

        [0-9a-zA-Z_]
  • \D это отрицание \d; и содержит любой символ кроме цифры

        [^0-9]
  • \S это отрицание \s; он содержит любой непробельный символ

        [^\s]
  • \W это отрицание \w; и содержи любой несловесный символ

        [^\w]
  • Точка '.' найдет любой символ, кроме "\n"

Сокращения \d\s\w\D\S\W можно использовать как внутри, так и вне классов символов. Вот некоторые приемы использования:

    /\d\d:\d\d:\d\d/; # найдет время в формате hh:mm:ss
    /[\d\s]/;         # найдет любой цифровой или пробельный символ
    /\w\W\w/;         # найдет символ слова, следующий несловесный символ
                      # за которым символ слова
    /..rt/;           # найдет 2 символа, за которыми следует 'rt'
    /end\./;          # найдет 'end.'
    /end[.]/;         # то же самое, найдет 'end.'

якорь слова \b находит границу между сиволом слова и не словесным символом \w\W или \W\w:

    $x = "Housecat catenates house and cat";
    $x =~ /\bcat/;  # найдет cat в 'catenates'
    $x =~ /cat\b/;  # найдет cat d '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)/;  # найдет hh:mm:ss формат
    $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, ... являются обратными ссылками \g1, \g2, ... Обратные ссылки на найденные переменные могут использоваться внутри регекса:

    /(\w\w\w)\s\g1/; # найдет последовательность такую как 'the the' в строке

$1, $2, ... должны использоваться за пределами регекса, а \g1, \g2, ... только внутри регекса.

Поиск повторений

Метасимволы повторения ?, *, +, и {} позволяют нам определить число повторений порции регекса, которые нам нужно найти. Повторители ставятся сразу после символа, символьного класса, или группировки того, что мы хотим указать. Они, имеют следующие значения:

  • a? = найдет 'a' 1 или 0 раз

  • a* = найдет 'a' 0 или больше раз, т.е. любое число раз

  • a+ = найдет 'a' 1 или больше или много раз, но как минимум один

  • a{n,m} = найдет минимум n раз, но не более чем m раз.

  • a{n,} = найдет минимум n или больше раз

  • a{n} = найдет точно n раз

Вот несколько примеров:

    /[a-z]+\s+\d*/;  # найдет слов в нижнем регистре, как минимум один пробельный символ, и
                     # любое число символов
    /(\w+)\s+\g1/;    # найдет удвоенное слово произвольной длины
    $year =~ /^\d{2,4}$/;  # гарантирует в году 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 matches)

Первый повторитель .* захватывает как можно больше от строки поиска пока регекс все еще будет соответствовать шаблону поиска. Второму повторителю .* совсем не осталось строки, таким образом он найдется 0 раз.

Находим больше

Есть еще несколько вещей, которые вы, возможно, захотите узнать, речь идет об операторах поиска. Глобальный модификатор //g позволяет оператору поиска найти строку столько раз сколько это возможно. В скалярном контексте, удачный поиск в строке, имеющей //g прыгает с найденной точки на найденную точку, отслеживая позицию в строке, как она идет вдоль. Вы можете получить или установить позицию поиска с помощью функции pos(). Например,

    $x = "cat dog house"; # 3 слова
    while ($x =~ /(\w+)/g) {
        print "Слово $1, заканчивается в позиции ", pos $x, "\n";
    }

напечатает

    Слово cat, заканчивается в позиции 3
    Слово dog, заканчивается в позиции 7
    Слово house, заканчивается в позиции 13

Неудавшийся поиск или изменения в целевой строки сбрасывает позицию. Если Вы не хотите, чтобы позиция сбрасывалась после неудачного поиска добавте //c, как в /регекс/gc.

В списочном контексте, //g вернет список найденных групп или, если групп нет, то список поисков целого регекса. Так

    @words = ($x =~ /(\w+)/g);  # найдет,
                                # $word[0] = 'cat'
                                # $word[1] = 'dog'
                                # $word[2] = 'house'

Поиск и замена

Поиск и замена осуществляется с помощью s/регекс/замена/модификаторы. Замена является Perl строкой в двойных кавычках, которая заменяет в строке то, что нашел регекс. Оператор =~ также используется здесь, чтобы связать строку с 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///, найденные переменные C <$ 1>, C <$ 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///r делает так, что результаты подстановки должны быть возвращены, переменная $_ не изменяется (или любая другая переменная с которой заменитель связан с помощью =~):

    $x = "I like dogs.";
    $y = $x =~ s/dogs/cats/r;
    print "$x $y\n"; # напечатает "I like dogs. I like cats."

    $x = "Cats are great.";
    print $x =~ s/Cats/Dogs/r =~ s/Dogs/Frogs/r =~ s/Frogs/Hedgehogs/r, "\n";
    # напечатает "Hedgehogs are great."

    @foo = map { s/[a-z]/X/r } qw(a b c 1 2 3);
    # @foo сейчас qw(X X X 1 2 3)

Модификатор выполнения 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 /регекс/, строка расделяет строку в список подстрок и возвращает этот список. Регекс определяет последовательность символов , которые разделяют строку. Например, чтобы разделить строку на слова, нужно

    $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 ,а для справочной страницы см perlre.

АВТОР И АВТОРСКОЕ ПРАВО

Copyright (c) 2000 Mark Kvale Все права защищены.

Этот документ может распространяться на тех же условиях, что и Perl.

Благодарности

Автор хотел бы поблагодарить Марка-Джейсона Доминуса, Том Кристиансена, Илью Захаревича, Брэда Хьюза, и Майка Жиру за все их полезные комментарии.

ПЕРЕВОДЧИКИ

  • Николай Мишин <mishin@cpan.org>