Опубликован: 01.03.2005 | Доступ: свободный | Студентов: 33896 / 4520 | Оценка: 4.31 / 4.06 | Длительность: 16:41:00
ISBN: 978-5-9556-0026-0
Лекция 13:

Регулярные выражения

< Лекция 12 || Лекция 13: 12345 || Лекция 14 >

Подвыражения (подшаблоны)

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

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

  1. Локализует множество альтернатив.

    Например, шаблон

    жар(кое|птица)
    совпадает с одним из слов " жаркое ", " жарптица " и " жар ". Тогда как без скобок это было бы " жаркое ", " птица " и пустая строка.
  2. Устанавливает подшаблон как "захватывающий" подшаблон. Это значит, что, когда какая-то подстрока в тексте совпала с шаблоном, все подстроки, которые совпали с подшаблонами этого РВ, тоже возвращаются в качестве результата. Скобки, обозначающие начало подшаблона, пересчитываются слева направо (начиная с 1) для того, чтобы узнать, сколько подшаблонов нужно захватить.

    Например, имеется такой шаблон:

    победитель получит 
           ((золотую|позолоченный)
                      (медаль|кубок))
    и строка, в которой ищутся совпадения с этим шаблоном: " победитель получит золотую медаль ". Тогда кроме этой фразы будут еще захвачены и выданы как результаты поиска следующие совпадения в подвыражениях: " золотую медаль ", " золотую ", " медаль ", пронумерованные 1, 2, 3 соответственно.

Однако это не всегда удобно. Для того чтобы избавиться от "захватывающего" эффекта подвыражения, после открывающей скобки пишут " ?:". Тогда это подвыражение в результат поиска не включается и при нумерации остальных подшаблонов с "захватывающим" эффектом не учитывается.

победитель получит 
      ((?:золотую|позолоченный)
                  (медаль|кубок))
Пример 13.1. Применение ?:

Тогда в условиях предыдущего примера получим искомую строку " победитель получит золотую медаль " и строки " золотую медаль ", " медаль ", пронумерованные 1 и 2 соответственно.

Если в html-файле название находится после <body> и отделено от него только пробелами или переводами строк, заключено в тег <h1> и после него тоже может идти сколько-то пробелов и переводов строк, то его можно найти с помощью следующего скрипта:

<?
//считываем файл в строку
$str = file_get_contents('1.htm');
$pattern = "/<body.*?>[\n\s]*<h1>".
              "(.*?)<\/h1>[\n\s]*/m";
// осуществляем поиск
$n = preg_match_all ($pattern, $str, $res);
echo $res[1][0]; // выводим заголовок
?>
Пример 13.2. Поиск названия в html-файле

Заметим, что здесь выводится первое захваченное подвыражение, поскольку нам интересно только само название, а не все РВ. Так как в этом РВ есть только одно подвыражение, то его значение содержится в нулевом элементе первого массива результатов.

Повторения (квантификаторы)

В предыдущих примерах мы часто писали комбинации типа \d\d. Это значит, что цифра должна повторяться два раза. А что же делать, если повторений очень много или мы не знаем, сколько именно? Оказывается, нужно использовать специальные метасимволы.

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

Квантификаторы могут следовать за любым из перечисленных элементов:

  • одиночный символ (возможно, в комбинации с обратным слэшем );
  • метасимвол "точка";
  • символьный класс;
  • обратная ссылка (о них расскажем позднее);
  • подшаблон.

Общие квантификаторы задают минимальное и максимальное число дозволенных повторений элемента; эти два числа, разделенные запятой, заключаются в фигурные скобки. Числа не должны превышать 65 536 и первое число должно быть меньше или равно второму. Например,

x{1,3}

говорит о том, что символ "x" должен повторяться минимум один, а максимум три раза. Соответственно этому шаблону удовлетворяют строки: x, xx, xxx.

Если второй параметр отсутствует, но запятая есть, то повторений может быть сколько угодно. Таким образом,

[aeuoi]{2,}

значит, что любой из символов " a ", " e ", " u ", " o ", " i " в строке может повторяться два и более раз, а регулярное выражение

\d{3}

задает ровно три цифры.

Сокращенные квантификаторы задают наиболее используемые количественные отношения (повторения). Они придуманы для удобства, чтобы не перегружать и без того сложные выражения лишним синтаксисом.

Исходя из исторических традиций три наиболее часто встречающихся квантификатора имеют следующие обозначения:

* эквивалентно {0,} – то есть это ноль и более повторений;

+ эквивалентно {1,} – то есть это одно и более повторений;

? эквивалентно {0,1} – то есть это ноль или одно повторение.

Есть еще один важный момент, на который стоит обратить внимание при изучении квантификаторов. По умолчанию все квантификаторы "жадные", они стараются захватить как можно больше повторений элемента. То есть если указать, что символ должен повторяться один и более раз (например, с помощью * ), совпадение произойдет со строкой, содержащей наибольшее число повторений указанного символа. Это может создать проблемы, например, при попытке выделить комментарии в программе на языке Cи или PHP. Комментарии в Cи и PHP записываются между символами /* и */, внутри которых тоже могут встречаться символы * и /. И попытка выявить Си-комментарии с помощью шаблона

/\* .* \*/

в строке

/* первый комментарий */ 
     не комментарий 
/* второй комментарий */

не увенчается успехом из-за "жадности" элемента " .* " (будет найдена также строка "не комментарий").

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

/\* .*? \*/

успешно выделяет Си-комментарии.

В PHP существует опция PCRE_UNGREEDY, которая делает все квантификаторы "не жадными" по умолчанию и "жадными", если после них идет знак вопроса.

<?
//Рассмотрим html-файл, где имеется 
//следующая строка:
$str = "<div id=1>Привет</div> ".
   "<p>Текст, не заключенный в тег ".
        "div</p><div id=2>Пока</div>";
// Если мы хотим найти текст,  
// содержащийся между тегами div, 
// естественно написать такой шаблон:
$pattern = "!<div id=1>.*</div>!si";
// Но этот шаблон слишком "жадный"  
// и захватит также и текст, 
// заключенный в нашем примере между 
// тегами <p>. Чтобы этого избежать, 
// нужно написать следующий шаблон, 
// отличающийся только наличием знака 
// вопроса, который запрещает 
// квантификатору быть "жадным".
$pattern1 = "!<div id=1>.*?</div>!si";
// Запускаем поиск в строке $str
// совпадений с шаблонами 
// $pattern и $pattern1
$s = preg_match_all ($pattern, $str,
                                $res);
$js = preg_match_all ($pattern1,
                         $str, $res1);
//выводим результаты поиска
// функция htmlspecialchars позволяет 
// выводить html без 
// его обработки браузером
echo "Жадный шаблон:". 
       htmlspecialchars($res[0][0]).
                               "<br>";
echo "Нежадный шаблон:".
        htmlspecialchars($res1[0][0]);
?>
Пример 13.3. Использование "жадных" квантификаторов

Результаты работы скрипта:

"Жадный" шаблон:<div id=1>Привет</div> 
<p>Текст,не заключенный в тег div</p>
<div id=2>Пока</div> 
"Нежадный" шаблон:<div id=1>Привет</div>

Теперь мы в принципе можем решить задачу выделения содержания из html-файла, если оно заключено в теге <div id=content>. Предлагаем читателям проделать это самостоятельно.

Модификаторы PCRE

Еще один немаловажный элемент регулярного выражения – это список применяемых к нему модификаторов. Модификаторы – это выдаваемая интерпретатору регулярных выражений инструкция по обработке данного выражения. Например, считать, что все символы регулярного выражения соответствуют как большим, так и маленьким буквам в строке, где производится поиск. Примеры модификаторов приведены в таблице 13.3.

Таблица 13.3. Наиболее часто используемые модификаторы
Обозначение Описание
i ( PCRE_CASELESS ) Если указан этот модификатор, то буквы в шаблоне совпадают с буквами и верхнего, и нижнего регистра в строке
m ( PCRE_MULTILINE ) По умолчанию строка, подающаяся на вход интерпретатору РВ, рассматривается как состоящая из одной линии. Этот модификатор включает поддержку многострокового режима
s ( PCRE_DOTALL ) Если установлен этот модификатор, то метасимвол точка " ." совпадает с любым символом, ВКЛЮЧАЯ символ перевода строки
x ( PCRE_EXTENDED ) Заставляет интерпретатор игнорировать пробелы между символами в шаблоне, за исключением пробелов, экранированных обратным слэшем или находящихся внутри символьного класса, а также между неэкранированным символом # вне символьного класса и символом новой строки
U ( PCRE_UNGREEDY ) Этот модификатор инвертирует "жадность" квантификаторов, т.е. они становятся "нежадными" по умолчанию и "жадными" если предшествуют символу " ?"
< Лекция 12 || Лекция 13: 12345 || Лекция 14 >
Дарья Федотова
Дарья Федотова
Сергей Березовский
Сергей Березовский

В рамках проф. переподготовки по программе "Программирование"

Есть курсы, которые я уже прошел. Но войдя в курс я вижу, что они не зачтены (Язык Ассемблера и архитектура ЭВМ, Программирование на С++ для профессионалов). Это как?