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

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

Иногда нам требуется вывести не весь текст поста или комментария, а только его начало, и дать ссылку на продолжение «читать далее». Но стоит нам обрезать текст, как в конце его появляется (иногда) ромбик со знаком вопроса внутри (или рамка с мелкими циферками «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() я обычно прописываю в файле конфигурации проекта. Т.е. в том файле, который гарантированно будет подключен в первую очередь, до обработки кодов различных скриптов моего проекта.

Замечание

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

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

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

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

11 Responses

  1. 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 содержит определение функций, не требуется установки дополнительных расширений)

  2. Ден говорит:

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

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

    target = «_blank»

    • Павлуха говорит:

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

  3. Ден говорит:

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

    • Павлуха говорит:

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

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

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

  4. Владимир говорит:

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

    В 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

Добавить комментарий

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

Иногда ваш комментарий может не отобразиться сразу после публикации - будто пропал. Не волнуйтесь, он не пропадёт и появится потом, после моего одобрения.