Интересный нюанс с пробелами при чистке текстов функцией preg_replace
При чистке текстов регулярными выражениями в PHP я столкнулся с интересной проблемой - некоторые пробелы игнорировались. Поиск и устранение проблемы отняло у меня не мало времени, был получен полезный опыт.
Задача состояла в том, чтобы очистить пустые абзацы в самом начале текста во множестве статей, поскольку эти абзацы там не нужны и портят общий вид статьи.
Содержание:
Проводим исследование
Для выполнения задачи по чистке кусков кода, представляющего пустые абзацы, я решил использовать регулярные выражения (RegExp) в PHP. Простой функцией замены подстроки здесь не обойтись, поскольку вариантов и комбинаций может быть много.
Вот некоторые варианты HTML кода, которые присутствовали в тексте и выводили пустые абзацы:
<p></p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
Как видим, между тегами может быть пробел, может быть его HTML-код, а также может и вовсе не быть никакого символа. Чистка таких строчек кода в текстах выполнялась регулярным выражением:
$txt=preg_replace( '#^<p>( |s| )+</p>#is' , '' , $original_text );
Статьи которые содержали ненужные пустые абзацы избавились от пустот, но в некоторых пустые абзацы вначале все же остались, посмотрев исходный код HTML одной из таких статей я увидел следующее:
<p> </p>
Странно... два тега и несколько пробелов, возможно что-то упущено. Запустил чистку приведенным выше регулярным выражением еще раз - ничего не изменилось.
Что же это за мистика? Скопировал этот кусок кода с пробелами в буфер обмена и забросил в конвертер Text->Hex (http://www.swingnote.com/tools/texttohex.php), получаю результат:
Hex Spaced:
3c 70 3e 20 20 20 20 20 3c 2f 70 3e
Не видно ничего необычного: "20" (x20) - это код обычного пробела, функция должна была очистить эту строчку.
Решил написать регулярное выражение конкретно для этого случая:
$txt=preg_replace( '#^<p>x20+</p>#is' , '' , $original_text );
Это регулярное выражение так и не очистило указанную строчку (не заменило на пустую) – пробелы окутанные двумя тегами остались на своем месте.
Возможно что при копировании текста из браузера в буфер обмена символы как-то конвертируются, нужно как-то это все проверить.
Поможет HEX-редактор
Для выяснения что же представляет из себя эта загадочная строчка кода с двумя тегами и пробелами, решил применить HEX-редактор.
Текст был сохранен в файл с помощью PHP-функции "file_put_contents". Для того, чтобы просмотреть текстовый файл в HEX-формате, пригодилась программа под названием "HxD".
HxD - это бесплатный HEX редактор (http://mh-nexus.de/en/hxd/), программа маленького размера, бинарный файл в несколько сотен килобайт.
Открыв в HxD ранее сохраненный в файл текст строки я увидел следующее:
<p> </p>
3C 70 3E A0 20 A0 A0 A0 3C 2F 70 3E
Оказалось что между тегами присутствуют символ с кодом A0 (xA0), а также один символ пробела с кодом 20 (x20).
Посмотрев в таблицу с кодами символов стало понятно что это за символ с кодом xA0. Это тот же самый пробел только НЕРАЗРЫВНЫЙ:
Dec | Hex | Symbol | HTML Number | HTML name | Description |
160 | A0 |   | | non-breaking space | |
32 | 20 |   | space |
Дополнив шаблон регулярного выражения кодом данного символа, функция выполнила чистку как положено:
$txt=preg_replace( '#^<p>[xA0x20]+</p>#is', '' , $original_text );
Заключение
Не спеша, я потратил несколько часов времени чтобы разобраться с данным нюансом. Был получен интересный опыт и проблема решена! Вот такая вот магия ...
P.S. Работы проводились на PHP 5.3;
Спасибо, друг! Помог! Помог сэкономить время!

Пожалуйста
. Рад что эта статья кому-то пригодилась!
Столкнулся с аналогичной ситуацией в БД. Нашел, что вместо пробела стоит символ А0. Полез искать что это за символ, и наткнулся на статью. Вот статья на Вики про неразрывный пробел: Неразрывный пробел