PHP 7: простая замена с помощью регулярного выражения

В интернете много курсов по регулярным выражениям, но все они написаны слишком сложно даже для человека, имеющего большой опыт в разработке сайтов ) Попробую исправить эту ситуацию, возьму живой пример из своей работы и напишу просто — о сложном.

Самый частый случай использования регулярок в моих проектах — какое-то преобразование контента на сервере перед передачей клиенту. Например, есть картинка с подписью alt:

<img src="file.jpg" alt="подпись">

Нужно вытащить подпись из тега alt, поставить её под картинкой, и обернуть всё это дело в div.
Вот, что должно получиться на выходе:

<div><img alt="подпись" src="file.jpg"><span>подпись</span></div>

Цветом помечены те кусочки, которые мы взяли из первой фразы в неизменном виде. Как видим, в выходной конструкции «подпись» использована два раза, а свойства поменяны местами — это значит, что обычной заменой str_replace данная задача никак не решается )

Если вам сразу нужна готовая функция из моего блога, которая это делает, то вот она. А далее разберём, как она работает.

function regrepl_img($in) {
$out=preg_replace_callback(
"/(<image src=")(.*?)(" alt=")(.*?)(">)/",
function($m) {
return '<div><img alt="'.$m[4].'" src="'.$m[2].'"><span>'.$m[4].'</span></div>';
},$in);
return $out;
}

Функция preg_replace_callback — стандартная функция языка PHP для работы с регулярными выражениями. Она работает аналогично str_replace, но имеет более широкие возможности. Однако суть у неё та же: на входе — то, что мы ищем, а на выходе — то, на что нужно заменить. Самый сложный этап — составление шаблона для поиска, его мы разберём максимально подробно. А вот замена — это просто, и о ней будет только пара слов в конце статьи.

Как составить шаблон поиска?

Представим, что мы сами решаем данную задачу, и опишем наши действия простыми словами. Но сначала ещё раз повторим исходный текст:

<img src="file.jpg" alt="подпись">

Итак, нам нужно:
1) найти в тексте все подобные конструкции
2) вытащить имя файла
3) вытащить подпись из тега alt

Имя файла находится между двумя кусками текста:

текст слеваимя файлатекст справа
<img src="file.jpg" alt="

Чтобы найти имя файла, нужно найти один из этих кусков текста, например, левый:

<img src="

На языке регулярных выражений это выглядит как тот же самый текст, заключённый в скобки:

(<image src=")

Затем найти правый кусок:

" alt="

Или на языке регулярных выражений:

(" alt=")

А то, что окажется между этими кусками — и будет искомым именем файла. Мы не знаем, какая длина имени файла, и какие могут быть символы в имени файла. На языке регулярных выражений это записывается вот так:

(.*?)

— это означает «любое количество любых символов».

Вот такой шаблон пока что получается:

(<image src=")(.*?)(" alt=")

Буквально это означает «найти в тексте левый кусок, затем двигаться вправо, пропуская любое количество любых символов, пока мы не встретим правый кусок, и в итоге запомнить оба этих куска и то, что находится между ними». Нам доподлинно неизвестно, как именно машина будет искать текст — слева направо или справа налево, или прыгать из одного места в другое. Но при составлении и проверке условий проще всего считать, что поиск идёт слева направо. На самом деле есть и прямые указания, в каком направлении искать, строго или нестрого, но это выходит за рамки данной статьи.

Текст из тега alt ищется аналогично — по двум соседним известным кускам текста. В итоге получается вот такой шаблон поиска, который я разбил на 5 частей для удобства понимания:

(<image src=")(.*?)(" alt=")(.*?)(">)
А ниже — то, что находит функция по данным условиям:
<img src="file.jpg" alt="подпись">

Пять конструкций в круглых скобках — это ключевые фразы, которые машина пытается найти в исходном тексте. У каждой фразы есть начало и конец, или какие-то иные признаки, которые мы задаём в условиях. Если мы не зададим каких-то конкретных условий, то поиск работать не будет.

Грубо говоря, нам нужно разрезать текст на пять частей.
Где резать? — Определяют условия поиска, заданные в шаблоне.

Ключевых фраз может быть сколько угодно, но важно уметь их правильно составить, чтобы однозначно указать машине на нужные нам фрагменты текста, и при этом чтобы условий не получилось слишком много. Работа с регулярными выражениями — не самый экономичный процесс, и это может ухудшить производительность системы.

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

Разъясняющая табличка

Условие(<img src=")(.*?)(" alt=")(.*?)(">)
ОбъяснениеИщем ключевую фразу <img src="Если нашли, далее движемся вправо по тексту, и пропускаем любое количество любых символов, пока не встретим следующую ключевую фразуВот эту: " alt="Далее опять пропускаем любое кол-во символов до следующей ключевой фразыВот этой: ">
Результат<img src="file.jpg" alt="подпись">

Итак, верхняя строка таблицы — это условие поиска (<image src=")(.*?)(" alt=")(.*?)(">), разбитое на пять столбиков.

А нижняя строка — исходный текст, разбитый на пять частей, в соответствии с условиями, которые заданы в первой строке. Это промежуточный этап работы функции preg_replace_callback. Эти пять значений записаны в массив, и можно использовать их на своё усмотрение.

Массив внутри функции preg_replace_callback можно обозначить как угодно. Пусть будет $m. Вот, что в него попало (та самая нижняя строчка таблицы):

$m[1] = <img src=";
$m[2] = file.jpg;
$m[3] = " alt=";
$m[4] = подпись;
$m[5] = ">;

Больше всего нас интересуют элементы $m[2] и $m[4], в которых хранится имя файла и подпись картинки.

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

Замена

Далее нужно составить конструкцию, которую будет возвращать функция замены.

function($m) {
return '<div><img alt="'.$m[4].'" src="'.$m[2].'"><span>'.$m[4].'</span></div>';
}

И мы получаем то, что нужно:

<div><img alt="подпись" src="file.jpg"><span>подпись</span></div>

При составлении подобных замен важно учитывать, что поиск может зациклиться. Чтобы надёжно избежать зацикливания, я всегда делаю так, чтобы повторная замена точно не сработала. Например, можно изменить порядок свойств — было АБВ, стало АВБ, — и тогда строгое соответствие при потенциально повторяющемся поиске уже не сработает.

Спасибо за внимание )

P.S. Прошу прощения у true программистов, которым данная статья показалась нестрогой или неправильной. Написана она, в первую очередь, для новичков и для таких же лентяев, как я )

2 мартаPHPурок

Благодарность автору сайта

Если Вы хотите поддержать проект, воспользуйтесь платформой Яндекс.Деньги:

Смотрите также

Спонсоры поста

Комментарии

Дальнейшие комментарии закрыты. По всем вопросам обращайтесь ко мне по почте, через Вотсап или Вконтакте, см. раздел Контакты

Следующая заметка

© 2019