Корректное урезание строк по словам в PHP

Рубрика: MaxSite CMS -> PHP
Четверг, 13 декабря 2007 г.
Просмотров: 4693
Подписаться на комментарии по RSS
]]>
]]>

Все гениальное не просто, а очень просто! Константин придумал способ как корректно обрезать текст по словам причем без использвания строковых функций.

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

До недавнего времени я вынужден был вообще пойти на крайнее извращение и перед использованием str_функций преобразовывал текст в windows-1251, выполнял нужные операции, а потом кодировал обратно в utf-8.

Константин сделал совсем просто: строка разбивается в массив (которые уже корректно работают с utf-8), массив обрезается, а потом объединяется в строку. Все элементарно. smile

Вот немного модифицированная функция для таких случаев:

  1.  function maxsite_str_word($text, $counttext = 10, $sep = ' ') {
  2.   $words = split($sep, $text);
  3.   if ( count($words) > $counttext )
  4.   $text = join($sep, array_slice($words, 0, $counttext));
  5.   return $text;
  6.  }

То есть в ней можно указать сам текст, количество слов и разделитель (это для универсальности).

]]>twitter.com Google Buzz google.com bobrdobr.ru del.icio.us technorati.com linkstore.ru news2.ru rumarkz.ru memori.ru moemesto.ru]]>

Комментариев: 18

Вы можете получать новые комментарии к этой записи по RSS или оформить подписку на все комментарии сайта. Или даже на все новые записи сайта. Не знаете, как это сделать?
  1. 2007-12-13 в 17:58:11 | Бресь Сергей

    http://php.net/mb_substr

    http://php.net/manual/en/ref.mbstring.php

    ?

  2. 2007-12-13 в 17:58:41 | Dimox

    Жаль, что я в этом нифига не понимаю :mrgreen:

  3. 2007-12-13 в 18:07:04 | Бомж без колес

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

  4. 2007-12-13 в 19:27:55 | Максим

    Тут главное не в том, чтобы разбить по пробелу, а в том, что стандартные str_ некорректно работают с юникодовскими строками. Поэтому нужно либо локаль подключать, либо mbstrings пользоваться. Но не всегда mb включены на серверах, поэтому нужно всегда проверять наличие этих функций перед использованием. При использовании же массива все очень просто, быстро и без проблем с совместимостью.

  5. 2007-12-13 в 23:51:47 | CTapbIu

    как вариант:

    if (preg_match('/([^ \n\r]+[ \n\r]+){1,10}/s', $text, $match))
    $substr = $match[0];
  6. 2007-12-14 в 02:07:32 | generator2003
    Проблема собственно в том, что юникодовский текст занимает больше байт, чем количество символов. Поэтому обычный substr и т.п. могут обрезать текст по служебному байту, что приводит к отображению браузером знака вопроса в конце текста. Все это не очень красиво.

    А можно пример в коде ???

    а то на словах не совсем понятно

  7. 2007-12-14 в 02:32:27 | Maksus

    Dimox, чем-то мы с тобой похожи :???:

  8. 2007-12-14 в 02:33:44 | Максим

    Вот такой код может неверно обрезать текст:

    $TEXT = substr($TEXT, 0, $counttext );
  9. 2007-12-14 в 17:52:53 | Охотник на зелёных

    Уже давным давно, когда я только переделывал виджеты, я переделал виджет и "последние комментарии". Мне ещё тогда в дефолтном бесило, что делиться всё по символам, а не по словам. И я переделал виджет. Но у меня почему то и со split-ом была проблема, добавлялось в конце всегда ? (символ пробела видимо не в той кодировке). Вот выдержки из обновлённого виджета latest comments который работает по такому же принципу, что и описывается:

    $arr = explode(" ", $comment_content);

    $count_arr = count($arr);

    $comment_content ="";

    if ($max_chars > $count_arr) $max_chars2 = $count_arr;

    else $max_chars2 = $max_chars;

    for ($i = 0; $i

  10. 2007-12-14 в 17:54:57 | Охотник на зелёных

    забыл про эти символы grin

    $arr = explode(" ", $comment_content);

    $count_arr = count($arr);

    $comment_content ="";

    if ($max_chars > $count_arr) $max_chars2 = $count_arr;

    else $max_chars2 = $max_chars;

    for ($i = 0; $i<$max_chars2; $i++) {

    $comment_content .= $arr[$i]." ";

    }

    if ($max_chars2 != $count_arr) $comment_content .= "...";

  11. 2007-12-14 в 18:19:24 | Yantar

    то есть, explode вместо split тоже может сработать некорректно?

  12. 2007-12-14 в 21:09:33 | Max

    А почему бы не взять готовую функцию из Drupal? там вообще много всего хорошего уже сделано :Р

    function truncate_utf8($string, $len, $wordsafe = FALSE, $dots = FALSE) {
    $slen = strlen($string);
    if ($slen <= $len) {
    return $string;
    }
    if ($wordsafe) {
    $end = $len;
    while (($string[--$len] != ' ') && ($len > 0)) {};
    if ($len == 0) {
    $len = $end;
    }
    }
    if ((ord($string[$len]) < 0x80) || (ord($string[$len]) >= 0xC0)) {
    return substr($string, 0, $len) . ($dots ? ' ...' : '');
    }
    while (--$len >= 0 && ord($string[$len]) >= 0x80 && ord($string[$len]) < 0xC0) {};
    return substr($string, 0, $len) . ($dots ? ' ...' : '');
    }
  13. 2007-12-15 в 00:24:35 | cross

    Идея со сплитом не очень быстродейственная. А ведь есть еще и разные разделительные знаки... smile

  14. 2007-12-15 в 12:05:52 | CTapbIu

    а чем же вам не нравятся регэкспы?

  15. 2007-12-15 в 14:43:04 | Максим

    2CTapbIu: Вариант с preg_match, тоже хорош.

  16. 2007-12-17 в 16:11:04 | Алексей

    Действительно, а чем не устраивают мультибайтовые строковые функции? (как сказано в первом коменте)

    http://php.net/manual/en/ref.mbstring.php

    Урезание по словам - это не то, что требуется в большинсте случаев, так как фраза из N слов может быть очень разной по длине.

    Но если стоит задача именно разбиения по словам, то я бы использовал explode, implode.

  17. 2007-12-17 в 22:00:07 | murich

    я бы чесно говоря если надо было бы сразу через массив делал

  18. 2008-01-10 в 14:10:04 | Zmei

    Спасибо за статью.

    Предлогаю модификацию функции где текст урезается не только по словам , но и по символам в случае если текст длиннннннее заданного количества $maxchar .

    Может быть кому нибудь пригодится mad

    function getPrewText( $text, $maxwords = 30, $maxchar = 300 )
    {
    $sep = ' '
    $words = split( $sep, $text );
    $char = iconv_strlen( $text, 'utf-8' );
    if ( count( $words ) > $maxwords )
    {
    $text = join($sep, array_slice($words, 0, $maxwords));
    }
    if ( $char > $maxchar )
    {
    $text = iconv_substr( $text, 0, $maxchar, 'utf-8' );
    }
    return $text;
    }

Оставьте комментарий!

Не регистрировать/аноним

Используйте нормальные имена. Ваш комментарий будет опубликован после проверки.

Если вы уже зарегистрированы как комментатор или хотите зарегистрироваться, укажите пароль и свой действующий email.
(При регистрации на указанный адрес придет письмо с кодом активации и ссылкой на ваш персональный аккаунт, где вы сможете изменить свои данные, включая адрес сайта, ник, описание, контакты и т.д.)



grin LOL cheese smile wink smirk rolleyes confused surprised big surprise tongue laugh tongue rolleye tongue wink raspberry blank stare long face ohh grrr gulp oh oh downer red face sick shut eye hmmm mad angry zipper kiss shock cool smile cool smirk cool grin cool hmm cool mad cool cheese vampire snake excaim question

(обязательно)