Ромбик с вопросительным знаком в конце текста

ромбик с вопросиком

Иногда нам требуется вывести не весь текст поста или комментария, а только его начало, и дать ссылку на продолжение «читать далее». Но стоит нам обрезать текст, как в конце его появляется (иногда) ромбик со знаком вопроса внутри (или рамка с мелкими циферками «1001» — в зависимости от браузера). Предлагаю разобраться, почему такое происходит, и устранить проблему.

Очень популярной кодировкой текста в современном Вебе является «UTF-8». Например, именно её использует WordPress. Кодировка эта мультибайтная, благодаря чему в ней закодированы сотни алфавитов и разнообразные графические символы.

Суть вопроса

Но многие функции PHP писались изначально под однобайтные кодировки. Если при использовании таких функций (например, substr()) указать, что вам нужно взять первые 100 символов строки, то функция возьмёт первые 100 байт. Однако, 1 байт в кодировке «utf-8» занимают далеко не все символы. К примеру, английские буквы и знаки препинания занимают 1 байт, а вот русские буквы занимают 2 байта.

Не все функции учитывают такую особенность кодировки «utf-8», потому случается, что при обрезке строки последний символ «режется пополам». В итоге в конце строки мы имеем символ с номером, отображение которого браузером не предусмотрено — он и будет показан как вопросик в ромбе или какая-то другая непонятная неожиданная символика.

substr работает некорректно

Бывают и другие странности. Например, функцией strlen() вы захотите определить длину строки. Если строка, к примеру, «PHP!» — проблем никаких, результат верный: 4. Но если строка: «Ура!» — результат будет: 7. Если разобраться, в этом нет ничего странного, поскольку кириллические символы занимают по 2 байта, а восклицательный знак — 1 байт. И, если мы захотим усечь строку «Ура!» до 3-х символов функцией substr(), то вместо ожидаемого «Ура» получим «У» и половинку буквы «р» в виде вопросика в ромбе.

Решение

Для устаревших функций работы со строками существуют мультибайтные аналоги. Например, функции strlen() соответствует функция mb_strlen(), для функции substr() есть мультибайтный аналог mb_substr(). Синтаксис почти идентичен, за тем лишь исключением, что вы можете (не обязательно) одним из аргументов указывать кодировку, с которой работаете. Пример:

mb_strlen( 'Ура!', 'utf-8' )

Вернёт корректный результат: 4. Внимание! Если явно не указать кодировку, будет использована серверная кодировка «по умолчанию». А она может отличаться от кодировки, в которой вы работаете над сайтом.

Указывать кодировку явно не всегда удобно. Например, когда требуется взять часть строки с 5 символа до конца, удобней воспользоваться такой конструкцией:

mb_substr( 'разрезаемая строка', 5 )

Здесь я не указал, сколько символов мне нужно, потому функция вернёт весь остаток строки. Но если нужно указать кодировку, код становится сложней:

mb_substr( 'разрезаемая строка'.'1', 5, -1, 'utf-8' )

Тут уже надо явно задавать, сколько нужно символов в результате, чтобы следующим аргументом указать кодировку (нельзя один аргумент пропустить, а следующий указать). Чтобы избежать ещё большего усложнения кода (использования функции mb_strlen() для определения длины строки) я прибегнул к хитрости — добавил к строке произвольный символ '1' и аргументом «-1» указал, что мне нужна вся строка с 5-го до последнего символа (1 с конца).

Более простой и понятный вариант — указать используемую кодировку до того, как пользоваться функциями обработки многобайтных строк:

mb_internal_encoding( 'utf-8' )

Один раз указали «UTF-8» — и в дальнейшем в своих функциях кодировку можно не указывать, если в них вы планируете работать со строками, кодированными в «UTF-8». Функцию mb_internal_encoding() я обычно прописываю в файле конфигурации проекта. Т.е. в том файле, который гарантированно будет подключен в первую очередь, до обработки кодов различных скриптов моего проекта.

Замечание

В официальных источника сказано, что расширение iconv по умолчанию включено. При написании скриптов для себя, вам достаточно просто проверить, включено ли на вашем хостинге mbstring (и можно даже включить, если выключено). Но, при написании скриптов в продакшн, один из ста клиентов создаст вам проблемы. Чтобы этого избежать, пользуйтесь аналогичными функциями расширения iconv:

  • iconv_strlen — Возвращает количество символов в строке
  • iconv_strpos — Возвращает позицию первого вхождения подстроки
  • iconv_strrpos — Возвращает позицию последнего вхождения подстроки
  • iconv_substr — Получение части строки
  • iconv — Преобразование строки в требуемую кодировку
  • и т.д.

За подсказку выражаю благодарность x64 (aka andi).

Запись опубликована в рубрике Web-мастеринг с метками . Короткая ссылка для добавления в закладки: Ромбик с вопросительным знаком в конце текста.

12 ответов к “Ромбик с вопросительным знаком в конце текста”

x64:

Более надёжный вариант — использовать функцию из набора iconv*

$str = iconv_substr ($str, 0, 10, 'UTF-8');

Начиная с PHP 5.0 входит в список расширений, устанавливаемых по умолчанию.

Ну и, конечно, можно указать дефолтную кодировку (чтоб не пихать при каждом использовании функций библиотеки):

ini_set ('iconv.internal_encoding', 'UTF-8');

Павлуха:

Спасибо за вариант. Можете объяснить, чем он более надёжный?

x64:

Чисто с эстетической точки зрения.

Хостингов без iconv не попадалось. А вот без mb вполне попадаются.

А в версии под win похоже, что iconv уже «вшит», т. е. является частью ядра (библиотека php5ts.dll содержит определение функций, не требуется установки дополнительных расширений)

Хорошо. Звучит разумно. Надо включить в статью

Павлуха:

Дополнил статью, сослался на твой блог в качестве благодарности. Спасибо за инфу!

Ден:

Почему при наведении на ссылку, появляются ромбики с вопросиками

как здесь www.seowebstroy.ru ,наведите на сайте на ссылку СТАТЬИ и они появляются именно когда в ссылке добавляю

target = «_blank»

Павлуха:

Ден, эти ромбики — фраза «Откроется в новом окне», которую подставляет плагин tooltip к ссылкам с атрибутом target = "_blank". А проблема в том, что ваш плагин tooltip работает в кодировке Win-1251, тогда как ваш сайт в кодировке utf-8

Ден:

Привет Павлуха. Не понял твоего ответа на мой вопрос. Так как мне исправить ошибку?

Павлуха:

Самый простой способ, учитывая твои познания, Ден — убрать target = "_blank" у ссылки «Статьи».

Ещё лучше способ — отключить плагин tooltip, ибо красоты у тебя на сайте и без него хватает.

Кстати, у тебя сейчас скрипт menu_files/script.js вываливает ошибку в 8 строке, потому что на странице нет элементов с id="sidebarmenu1". Из-за этой ошибки весь остальной JS на странице не работает. И плагин tooltip тоже. Так что, проблема решена. ;)

Владимир:

Товарищи, помогите уничтожить ромб, уже сил нет.

В php не понимаю, но насколько понял, в этом месте нужно что-то сделать

function myarcade_title ($chars = 0, $echo = true) { $title = strip_tags( the_title('', '', FALSE) ); if ( $chars > 0 ) { if ( (strlen($title) > $chars) ) { $title = mb_substr($title, 0, $chars, 'utf-8'); $title = mb_substr($title, 0, -strlen(strrchr($title, ' ')), 'utf-8'); // Wordwrap if ( strlen($title) < 4 ) { $title = mb_substr( the_title('', '',FALSE), 0, $chars , 'utf-8'); } $title .= ' ..'; } } if ($echo == true) { echo $title; } else { return $title; } } }

В конце строк я добавил 'utf-8', но эффекта нет. Ромбы на месте =(((

Павлуха:

Владимир, нужно вот что сделать: выдернуть руки автору функции myarcade_title, выпрямить их и вставить в правильное место))

Ну как можно одновременно работать с мультибайтными функциями (mb_substr) и однобайтными (strlen, strrchr)? Как минимум используйте mb_strlen и mb_strpos

Алексей:

Спасибо дружище!

Шикарно «разжевал» проблему...

Добавить комментарий для Павлуха Отменить ответ

Ваш адрес email не будет опубликован. Обязательные поля помечены *