Флаги регулярных выражений: g, i, m

В предыдущих уроках мы видели, что операторы m// и s/// по умолчанию ведут себя довольно скромно: находят только первое совпадение и различают регистр букв. Флаги — это буквы, которые ставятся сразу после закрывающего слэша шаблона и меняют это поведение. Разберём три самых важных: g, i и m.

Флаг g: глобальный поиск

Буква g означает global. Без этого флага замена s/// останавливается после первого найденного совпадения. С флагом g она находит и заменяет вообще все вхождения в строке.

my $text = "кот сидит, кот спит, кот играет";

my $count = ($text =~ s/кот/пёс/g);

print "$text\n";
print "Заменено раз: $count\n";

Вывод:

пёс сидит, пёс спит, пёс играет
Заменено раз: 3

Обратите внимание на вторую строку: оператор s/// с флагом g возвращает не просто «истина/ложь», а количество сделанных замен. Это удобно, когда нужно не только поменять текст, но и узнать, сколько раз замена сработала — например, для подсчёта статистики или проверки, что заменилось хоть что-то.

Флаг g работает и с оператором поиска m//, но там его поведение чуть более тонкое: в списковом контексте он возвращает сразу все найденные совпадения массивом.

my $text = "Заказ 102, заказ 58, заказ 700";

my @numbers = $text =~ /(\d+)/g;

print "Найдено номеров: @numbers\n";

Вывод:

Найдено номеров: 102 58 700

Здесь (\d+)/g означает «найди все последовательности цифр по всей строке и верни их списком». Без флага g мы получили бы только «102» — первое совпадение — а флаг заставляет регулярку продолжать поиск до самого конца строки.

Флаг i: не важен регистр

Буква i означает insensitive, «нечувствительный» — а именно нечувствительный к регистру букв. Без этого флага заглавные и строчные буквы считаются разными символами.

my @words = ("Perl", "PERL", "perl", "PeRl");

foreach my $word (@words) {
    if ($word =~ /^perl$/i) {
        print "$word — совпадает\n";
    }
}

Вывод:

Perl — совпадает
PERL — совпадает
perl — совпадает
PeRl — совпадает

Без флага i шаблон /^perl$/ совпал бы только со строкой «perl» в нижнем регистре — а «Perl» или «PERL» бы уже не подошли. Флаг i убирает эту чувствительность полностью, и любая комбинация заглавных и строчных букв засчитывается как совпадение.

Кстати, в этом примере появились ещё два новых символа: ^ означает «начало строки», а $ — «конец строки». Вместе ^perl$ означает «вся строка целиком состоит ровно из слова perl», а не просто «где-то внутри встречается perl».

Флаг m: построчный режим

Буква m означает multiline, «многострочный». Он меняет, как символы ^ и $ ведут себя, если строка на самом деле содержит несколько строк текста, разделённых переносами \n.

Без флага m символы ^ и $ относятся только к самому началу и самому концу всей строки целиком. С флагом m они начинают работать после каждого переноса строки — то есть означают «начало любой строки» и «конец любой строки» внутри текста.

my $log = "OK: старт сервера\nОШИБКА: нет подключения\nOK: сервер работает";

my @errors = $log =~ /^ОШИБКА:.*$/mg;

print "Найдено строк с ошибкой: " . scalar(@errors) . "\n";
print "$errors[0]\n";

Вывод:

Найдено строк с ошибкой: 1
ОШИБКА: нет подключения

Здесь переменная $log — это на самом деле три строки текста, склеенные через \n. Флаг m позволяет шаблону ^ОШИБКА:.*$ сработать не только в самом начале всего текста, а на начале каждой отдельной строки внутри него. Флаг g здесь тоже нужен — без него мы бы нашли только первую подходящую строку и остановились, даже если ошибок было несколько.

Комбинирование флагов

Флаги можно (и часто нужно) комбинировать — они просто пишутся подряд одной группой букв после шаблона, в любом порядке:

my $text = "perl отлично\nPERL — язык текста\nPyThOn — не он";

my @lines = $text =~ /^perl.*$/mig;

foreach my $line (@lines) {
    print "$line\n";
}

Вывод:

perl отлично
PERL — язык текста

Здесь сразу три флага: m позволяет искать по каждой строке отдельно, i убирает разницу между заглавными и строчными буквами, а g собирает все подходящие строки, а не только первую. Строка про «PyThOn» не попала в результат, потому что она не начинается со слова perl ни в каком регистре.

Частые ошибки

Самая частая путаница — забыть разницу между m и g, когда речь о многострочном тексте. Флаг m меняет смысл ^ и $, а флаг g заставляет искать все совпадения, а не только первое — это два независимых поведения, и для поиска «во всех строках» обычно нужны оба сразу.

Вторая ошибка — использовать точку . в многострочном тексте и удивляться, что она не захватывает перенос строки. По умолчанию точка означает «любой символ, кроме переноса строки» — это отдельное поведение, которое флаги g, i, m не меняют (для этого есть отдельный флаг s, который выходит за рамки этого раздела).

Третья ошибка — забывать флаг i при работе с текстом, который вводят реальные люди. Пользователи печатают как попало: «Да», «ДА», «да» — если сравнивать без i, часть ответов будет незаслуженно отбракована.

Итоги

Флаги — это короткие модификаторы поведения регулярного выражения, которые пишутся сразу после закрывающего слэша шаблона. Флаг g ищет или заменяет все совпадения вместо одного, флаг i убирает чувствительность к регистру букв, а флаг m заставляет ^ и $ работать на границах каждой строки внутри многострочного текста. Комбинируя их, можно очень точно настроить поведение поиска под конкретную задачу — и в этом, пожалуй, вся суть регулярных выражений в Perl: гибкость в паре символов.

Проверьте себя
1. Что означает флаг g в регулярном выражении Perl?
AРегистронезависимый поиск
BПоиск/замена всех совпадений, а не только первого
CПострочный режим для многострочного текста
DЖадный (greedy) режим квантификаторов
2. Какой флаг делает символы ^ и $ границами каждой строки внутри многострочного текста, а не только всего текста целиком?
Ag
Bi
Cm
Dx