Нина Савельева - Курс "Язык программирования PHP"
/* .*? */
успешно выделяет Си-комментарии.
В PHP существует опция PCRE_UNGREEDY, которая делает все квантификаторы «не жадными» по умолчанию и «жадными», если после них идет знак вопроса.
<?
//Рассмотрим html-файл, где имеется
//следующая строка:
$str = "<div id=1>Привет</div> ".
"
Текст, не заключенный в тег ".
"div
<div id=2>Пока</div>";// Если мы хотим найти текст,
// содержащийся между тегами div,
// естественно написать такой шаблон:
$pattern = "!<div id=1>.*</div>!si";
// Но этот шаблон слишком "жадный"
// и захватит также и текст,
// заключенный в нашем примере между
// тегами
. Чтобы этого избежать,
// нужно написать следующий шаблон,
// отличающийся только наличием знака
// вопроса, который запрещает
// квантификатору быть "жадным".
$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]);
?>
Результаты работы скрипта:
«Жадный» шаблон:<div id=1>Привет</div>
Текст,не заключенный в тег div
<div id=2>Пока</div>
«Нежадный» шаблон:<div id=1>Привет</div>
Теперь мы в принципе можем решить задачу выделения содержания из html-файла, если оно заключено в теге <div id=content>. Предлагаем читателям проделать это самостоятельно.
Модификаторы PCRE
Еще один немаловажный элемент регулярного выражения – это список применяемых к нему модификаторов. Модификаторы – это выдаваемая интерпретатору регулярных выражений инструкция по обработке данного выражения. Например, считать, что все символы регулярного выражения соответствуют как большим, так и маленьким буквам в строке, где производится поиск. Примеры модификаторов приведены в таблице 13.3.
i (PCRE_CASELESS)
Если указан этот модификатор, то буквы в шаблоне совпадают с буквами и верхнего, и нижнего регистра в строке
m (PCRE_MULTILINE)
По умолчанию строка, подающаяся на вход интерпретатору РВ, рассматривается как состоящая из одной линии. Этот модификатор включает поддержку многострокового режима
s (PCRE_DOTALL)
Если установлен этот модификатор, то метасимвол точка «.» совпадает с любым символом, ВКЛЮЧАЯ символ перевода строки
x (PCRE_EXTENDED)
Заставляет интерпретатор игнорировать пробелы между символами в шаблоне, за исключением пробелов, экранированных обратным слэшем или находящихся внутри символьного класса, а также между неэкранированным символом # вне символьного класса и символом новой строки
U (PCRE_UNGREEDY)
Этот модификатор инвертирует «жадность» квантификаторов, т.е. они становятся «нежадными» по умолчанию и «жадными» если предшествуют символу «?»
Регулярные выражения для «продвинутых»
В последующих разделах обсуждаются более сложные конструкции работы с регулярными выражениями, без которых в принципе можно обойтись. Поэтому мы не будем в них особо углубляться, а приведем лишь общие сведения.
Обратная ссылка
Вне определения символьного класса (это тот, что задается квадратными скобками) комбинация обратный слэш и цифра больше нуля (например, 1) называется обратной ссылкой и представляет собой ссылку на захваченное ранее регулярное подвыражение. Этих подвыражений ровно столько, сколько открывающихся круглых скобок (перед которыми нет знака вопроса) стоит левее данного элемента.
Обратная ссылка совпадает с конкретным выбранным значением подвыражения, на которое она ссылается, а не с любым возможным значением этого подвыражения. Таким образом, шаблон
(ответствен|надеж)ный проявляет 1ность
совпадет со строками «ответственный проявляет ответственность», «надежный проявляет надежность» и не совпадет со строкой «ответственный проявляет надежность».
Обратные ссылки могут использоваться внутри подвыражений. При первом использовании подвыражения ссылка внутри него не срабатывает, но при последующих повторениях подшаблона она работает, как описано выше.
Утверждения
Утверждение – это проверка символов, следующих до или после текущего символа. Простейшие утверждения закодированы последовательностями A, Z, ^, $ и т.д. Более сложные утверждения кодируются с помощью подшаблонов. Постараемся вкратце описать, как это делается.
Существует два типа утверждений: те, что смотрят за текущую позицию в исходной строке («смотрящие вперед»), и те, что смотрят на символы перед текущей позицией («смотрящие назад»).
Утверждения, закодированные подшаблонами, сравниваются как обычные подшаблоны, за исключением того, что при их обработке не происходит изменения текущей позиции.
«Смотрящие вперед» утверждения ищут совпадения в строке за текущей позицией поиска и начинаются с (?= для позитивных утверждений и с (?! для негативных. Например,
w+(?=;)
совпадает со словом, заканчивающимся точкой с запятой (не включая точку с запятой в результат поиска), и
foo(?!bar)
совпадает с любым появлением foo, после которого нет bar. Как все происходит? Берем строку и ищем в ней foo. Как только нашли, заглядываем вперед (текущая позиция при этом не меняется) и смотрим, идет ли далее bar. Если нет, то совпадение с шаблоном найдено, иначе продолжаем поиск.
Регулярное выражение
(?!foo)bar
не найдет все вхождения bar, перед которыми нет foo, потому что оно «смотрит вперед», а перед ним никаких символов нет. Поэтому в данном шаблоне ?!foo всегда верно.
«Смотрящие назад» утверждения ищут совпадения перед текущей позицией. Позитивные утверждения этого типа начинаются с (?<= , негативные – с (?<! . Смотрящим назад утверждениям позволено искать только строки фиксированной длины, т.е. в них нельзя использовать квантификаторы. Например,
(?<!foo)bar
находит все появления bar, перед которыми нет foo.
В начале лекции мы хотели научиться находить в html-файле упоминание об авторе. Это можно сделать с помощью «смотрящих назад» утверждений в РВ (хотя можно и проще).
<?
//считываем файл в строку
$str = file_get_contents('1.htm');
$pattern = "/(?<=Автор:)s[А-Я]".
"[а-я]*s([А-Я].s*){1,2}/m";
// осуществляем поиск
$n = preg_match_all ($pattern, $str, $res);
// выводим результаты
for ($i=0;$i<$n;$i++)
echo htmlspecialchars($res[0][$i]).
"<br>";
?>
Часть РВ после утверждения определяет, что мы ищем строку (ФИО), которая начинается с пробела, большой буквы, затем идут маленькие буквы в произвольном количестве, пробел и инициалы через точку. Утверждение задает то, что перед данной строкой должно стоять «Автор:».
Дату можно вычислить похожим образом. Оставляем это в качестве упражнения.
Условные подвыражения
Как в любом языке программирования, в РВ существуют условные конструкции. Применяются они к подвыражениям. То есть можно заставить процессор РВ выбирать подшаблон в зависимости от условия или выбирать между двумя альтернативными шаблонами в зависимости от результата утверждения или от того, совпал ли предыдущий захваченный подшаблон. Существуют две формы условных подвыражений:
(?(условие)шаблон_выполняемый_если_
условие_верно)
(?(условие)шаблон_если_условие_верно
|шаблон_если_условие_неверно)
Существует два типа условий. Если текст между круглыми скобками состоит из последовательности цифр, то условие удовлетворяется, если захваченное подвыражение с этим номером ранее совпало.
( ( )? [^()]+ (?(1) ) )
Первая часть этого РВ опционально совпадает с открывающейся круглой скобкой, и если этот символ присутствует, то устанавливает его как первое захваченное подвыражение.
Вторая часть совпадает с одним или более символами, не заключенными в круглые скобки.
Третья часть РВ – это условное подвыражение, которое проверяет, совпало ли первое множество скобок или нет (попалась ли нам в строке открывающая круглая скобка). Если попалась, то есть объект (строка) начинается с символа «(», то условие верно и вычисляется условный шаблон, а именно требуется наличие закрывающей круглой скобки. В противном случае подшаблон ни с чем не совпадает.