Поиск и замена текста между тегами на PHP
Рассмотрим примеры обработки и замены текста между HTML тегами, используя PHP функции для поиска и подмены по регулярным выражениям. Для примера возьмем абстрактный кусок кода, над которым будем проводить различные действия.
... <xx>наташа</xx> ... <xx>даша</xx> ... <xx>настя</xx> ...
На месте <xx> может быть любой тег, а троеточие обозначает любой произвольный текст.
- Поиск текста функцией «preg_match_all»
- Перебор найденных результатов в цикле «foreach»
- Отличие «preg_match» от «preg_match_all»
- Замена текста между тегами функцией «preg_replace»
- Замена тегов, оставляя всё, что внутри
- Обработка и замена при помощи «preg_replace_callback»
- Использование нумирации в заменах и другие продвинутые возможности
Поиск текста функцией «preg_match_all»
Для поиска текста внутри тегов воспользуемся функцией «preg_match_all». Зададим маску поиска и посмотрим, что она возвращает в качестве результата.
$sContent = "... <xx>наташа</xx> ... <xx>даша</xx> ... <xx>настя</xx> ..."; if (preg_match_all('|<xx>(.+)</xx>|isU', $sContent, $arr)) { echo $arr[0][0] . " " . $arr[0][1] . " " . $arr[0][2] . "<br />"; echo $arr[1][0] . " " . $arr[1][1] . " " . $arr[1][2]; } //на выходе получаем: //<xx>наташа</xx> <xx>даша</xx> <xx>настя</xx> //наташа даша настя
В нулевой разряд массива записались значения с тегами, а в первый — только текст между ними. Если требуется автоматизировать вывод всего найденного, то лучше использовать цикл foreach. Его рассмотрим ниже.
Функция preg_match_all возвращает «1» в случае нахождения в тексте соответствия с указанной маской или «0», если соответствий не найдено. В качестве параметров принимает маску, строку где ищем и переменную, в которую будут записаны найденные совпадения.
Маска поиска обрамляется символами «|». За ними идут директивы — «isU» обозначает регистронезависимый поиск в многострочном тексте с кодировкой «UTF-8»
|<xx>(.+)</xx>|isU
В самом правиле содержатся теги, между которыми требуется заменить текст — «(.+)». Точка символизирует любой символ, а плюс — что он может повторяться один или больше раз. Скобки говорят о том, что содержимое между ними нужно записать в переменную с результатом.
Перебор найденных результатов в цикле «foreach»
Для вывода результатов поиска можно воспользоваться циклом «foreach».
$sContent = "... <xx>Наташа</xx> ... <xx>Марина</xx> ... <xx>Настя</xx> ..."; if (preg_match_all('|<xx>(.+)</xx>|isU', $sContent, $arr)) { foreach ($arr[0] as $value) echo $value." "; echo "<br/>"; foreach ($arr[1] as $value) echo $value." "; }
На выходе получаем тоже самое что и в предыдущем примере, зато теперь мы автоматизировали перебор массива и сократили код.
Отличие «preg_match» от «preg_match_all»
Функция «preg_match» осуществляет поиск только до первого соответсвия с маской. Как только что-то найдено — поиск останавливается и возвращается одномерный массив.
if (preg_match('|<title>(.+)</title>|isU', $sContent, $arr)) return $arr[1]; else return false;
Здесь нулевой элемент массива «$arr» содержит найденное совпадение вместе с тегами «title», а первый элемент — «$arr[1]» только текст между этими тегами. Если в строке несколько тегов «title», это не значит что остальные значения будут записаны в «$arr[2]» и так далее. Элемент «$arr[2]» окажется не пуст если в маске указано несколько правил, но об этом в следующий раз.
Замена текста между тегами функцией «preg_replace»
Если требуется найти и произвести замену найденных элементов в строке, то на помощь приходит PHP функция «preg_replace».
Заменим в нашем примере все имена между тегами на какое-то конкретное, например — «Оля».
$sContent = "<xx>наташа</xx> ... <xx>даша</xx> ... <xx>настя</xx>"; $sContent = preg_replace('|(<xx>).+(</xx>)|isU', "$1"."Оля"."$2",$sContent);
Замена тегов, оставляя всё, что находится внутри
А теперь небольшой пример, показывающий как заменить определенные теги, сохранив содержимое между ними. Допустим, надо изменить в html коде все «strong» на CSS форматирование.
$sContent = "... <strong>Настя</strong> ..."; $sContent = preg_replace('|<strong(.*)strong>|isU', '<span style="font-weight: bold;" $1span>', $sContent); //на выходе получаем $sContent: //... <span style="font-weight: bold;" >Настя</span> ...
Обработка и замена при помощи «preg_replace_callback»
Переходим к самому интересному. Если нужно над найденным фрагметом произвести какие-то действия и только потом осуществить замену, то следует использовать «preg_replace_callback». Рассмотрим как с помощью этой функции в именах сделать первую букву заглавной.
<html> <head> <meta charset="UTF-8"> </head> <body> <?php $sContent = "<xx>наташа</xx> ... <xx>даша</xx> ... <xx>настя</xx>"; echo htmlspecialchars($sContent); echo "<br />"; $sContent = preg_replace_callback('|(<xx>)(.+)(</xx>)|iU', function($matches){ $matches[2] = mb_substr(mb_strtoupper($matches[2], 'UTF-8'),0,1,'UTF-8').substr($matches[2], 2); return $matches[1].$matches[2].$matches[3]; } ,$sContent); echo htmlspecialchars($sContent); ?> </body> </html>
В качестве параметров передаём маску поиска, функцию с кодом обработки и строковую переменную в которой осуществляем поиск. Дополнительно могут ещё быть заданы два необязательных параметра. О них в следующем разделе статьи.
Переменная «$matches» это массив, содержащий элементы регулярного выражения. В нулевом элементе будет содержаться вся исходная строка, а в остальных — содержимое скобок.
Код обработки не описываю, но отмечу что для замены первой буквы на заглавную я использую PHP функции для работы со строками в UTF-8 кодировке. Если у Вас кодировка cp1251, то нужно отбросить префикс «mb_» и удалить последний параметр у функций.
ВНИМАНИЕ! Код в примере будет работать только при использовании PHP версии 5.3 и выше. Для более поздних версий требуется доработка.
Использование нумирации в заменах и другие продвинутые возможности
Теперь немного о продвинутых возможностях функции «preg_replace_callback». Ранее я упоминал что у неё есть два необязательных параметра. Первый (по умолчанию равен «-1») содержит максимальное количество замен, которое должна произвести функция. Второй — переменная, в которую будет записано количество произведенных замен.
$sContent = preg_replace_callback('|(<xx>)(.+)(</xx>)|iU', function($matches){ //тут код } ,$sContent,2,$count);
Задав эти два параметра в предыдущем примере, замена главной буквы будет произведена только у первых двух имён. Соответственно, переменная «$count» будет содержать — 2. Если установить первый дополнительный параметр в «-1», то «$count» будет — 3.
И в конце о том, как узнать какая по счету замена происходит в данный момент. Это может потребоваться если появилась необходимость произвести замену между пятым и десятым найденным элементом строки или требуется для каких-то тегов прописать уникальные идентификаторы.
Для реализации может быть использована глобальная или статическая переменная. Использование глобальных переменных может быть отключено в PHP, поэтому рассмотрим пример со статической переменной. Присвоим всем тегам h2 уникальный идентификатор.
<?php $str = '<h2>Марина</h2> <b>Алёша</b> <h2>Наташа</h2> <h2>Катя</h2>'; $str = preg_replace_callback('|<h2>(.+)</h2>|iU', function($matches){ static $id = 0; $id++; return '<h2 id="uniq-'.$id.'">'.$matches[1].'</h2>'; }, $str,-1,$count); echo $str.' Количество замен: '.$count; ?>
Объявляя статическую переменную нужно помнить что она сохраняет своё значение между вызовами функции, поэтому идеально подходит для решения нашей задачи.
Есть функция:
заменяет внутри маркированного списка, надо переделать так, чтоб заменяла li внутри блока div class=«ingr», в который будет заключаться этот список
Хотя более точно можно сказать только увидев весь исходник
В последняя строке вместо $2 должна быть $3 :)
Если вместо ограничивающих символов || использовать //, то почему-то приходится экранировать слеш перед xx, т.е. строка будет такой:
Интересно, почему? А, понял — чтобы дать понять, где заканчивается само регулярное выражение :) Тогда вопрос: а какие ограничивающие символы вообще можно использовать? Видел — используют ##, ||, //. Где бы почитать про них?
Если в регулярном выражении присутствуют проценты и слеши, то обрамляем решеткой или прямым слешем. Если решетки внутри, то обрамляем любым вариантом, кроме решетки. Или экранировать, как в Вашем случае.
за UTF-8 отвечает маленькая u, а большая U — позволяет обрабатывать всю строку, а не только первое найденное выражение. Или по-научному — «Этот модификатор инвертирует жадность квантификаторов, таким образом они по умолчанию не жадные.»
Похоже, что нельзя, т.к. call_back-функции через массив передавать нельзя — http://us2.php.net/manual/ru/language.types.callable.php
Просто между title меняется так: Ну а любые символы можно сначала получить, используя preg_match
$html = preg_replace( "/". $rep[0], $rep[1], $html )
только в preg_replace могу вставить данные. А мне надо все что находится в теге тайл перенести в .Это возможно вообще?
Это если между h4 только текст, без доп. тегов. Если есть другие теги, то просто .+ поставь.