Правильная обрезка слов

StormMan

Новичок
Правильная обрезка слов

Всем привет!
Есть в smarty такой плагин, modifier.truncate.php. Изначально он вот такой:
PHP:
<?php
function smarty_modifier_truncate($string, $length = 80, $etc = '...',
                                  $break_words = false, $middle = false)
{
    if ($length == 0)
        return '';

    if (strlen($string) > $length) {
        $length -= strlen($etc);
        if (!$break_words && !$middle) {
            $string = preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, $length+1));
        }
        if(!$middle) {
            return substr($string, 0, $length).$etc;
        } else {
            return substr($string, 0, $length/2) . $etc . substr($string, -$length/2);
        }
    } else {
        return $string;
    }
}
?>
Проблема этого кода в том, что он неправильно работает с кодировкой utf-8, если символы отличаются от латиницы, и исходит эта проблема из-за того, что с многобайтными символами функция substr работать не умеет. Заменяю её на mb_substr:

PHP:
<?php
function smarty_modifier_truncate($string, $length = 80, $etc = '...',
                                  $break_words = false, $middle = false)
{
    if ($length == 0)
        return '';

    if (mb_strlen($string, utf8) > $length) {
    //if (strlen($string) > $length) {
        $length -= mb_strlen($etc, utf8);
        //$length -= strlen($etc);
        if (!$break_words && !$middle) {
            $string = mb_ereg_replace('/\s+?(\S+)?$/', '', mb_substr($string, 0, $length+1, utf8), utf8);
            //$string = preg_replace('/\s+?(\S+)?$/', '', substr($string, 0, $length+1));
        }
        if(!$middle) {
            return mb_substr($string, 0, $length, utf8).$etc;
            //return substr($string, 0, $length).$etc;
        } else {
            return mb_substr($string, 0, $length/2, utf8) . $etc . mb_substr($string, -$length/2, utf8);
            //return substr($string, 0, $length/2) . $etc . substr($string, -$length/2);
        }
    } else {
        return $string;
    }
}
?>
Остаётся одна проблема: слова обрезаются посередине, получается ерунда вроде "Слова обрезаются посе...". Подскажите, какие функции можно применить, чтобы текст обрезался правильно: "Слова обрезаются посередине...".

Буду очень благодарен за примеры!
 

varan

Б̈́̈̽ͮͣ̈Л̩̲̮̻̤̹͓ДͦЖ̯̙̭̥̑͆А͇̠̱͓͇̾ͨД͙͈̰̳͈͛ͅ
потому что preg_replace заменил на ereg_replace
 

StormMan

Новичок
Эх, если бы... Функции mb_preg_replace вообще нет, насколько я знаю.. Но чем чёрт не шутит - поменял я mb_ereg_replace на mb_preg_replace. Это ничего не дало, но и не отняло. Видимо, функция есть такая, но работает она аналогично. Слова также режутся посредине...
 

dimagolov

Новичок
StormMan, ты бы лучше почитал про [m]preg_replace[/m] и обработку UTF-8 в ней, чем шаманством заниматься
 

StormMan

Новичок
Таак, спасибо за наводку... Что-то начало проясняться)))

dimagolov, благодарю за ссылку, только в инглише я ещё не очень силён, нашёл русскоязычное описание. Понял, что с помощью регулярных выражений с этой функцией можно найти и заменить очень многие вещи... Но как это применить в моём конкретном случае... В моём случае функция удаляет совпадения $length+1 с шаблоном '/\s+?(\S+)?$/'. Что из этого можно сделать, пока придумать не получается.. Подскажи, пожалуйста.

гемоглобин, а можно хотя бы крошечный пример, в качестве наводки? про буковку u, что она даёт и куда её ставить, ничего найти не получилось... Заранее благодарю))
 

varan

Б̈́̈̽ͮͣ̈Л̩̲̮̻̤̹͓ДͦЖ̯̙̭̥̑͆А͇̠̱͓͇̾ͨД͙͈̰̳͈͛ͅ
Автор оригинала: StormMan
функция удаляет совпадения $length+1 с шаблоном
может я к вечеру туплю от усталости, но мой моск не сумел распарсить эту фразу :)

-~{}~ 10.06.10 22:30:

про букву u
http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php
 

StormMan

Новичок
varan, спасибо за ссылку)) Правда, я из неё не очень понял, как это дело в моём случае использовать можно... То есть я понял, что он строку обрабатывает в UTF8, но вот куда его втыкнуть..

а насчёт фразы... :) в функции preg_replace происходит сравнение третьего аргумента (например, строки) с первым (рег.выражение в качестве шаблона), и совпавшая часть заменяется на второй аргумент. Я так принцип работы функции понял.
 

dimagolov

Новичок
StormMan, ты или читай и разбирайся во всем сам, или просто переделай исходный "однобайтный" код в "многобайтный" добавляя mb_ к одним ф-ям и модификатор u в preg_replace.
отлаживать за тебя код мало кому интерсно.
 

StormMan

Новичок
Я же не прошу ничего отлаживать. Если бы у меня получилось во всём самому разобраться, я бы ничего и не спрашивал...
Поделитесь, пожалуйста, примером, как u использовать!

Я поставил preg_replace вместо mb_ereg_replace таким образом:
PHP:
$string = preg_replace('/\s+?(\S+)?$/', '', mb_substr($string, 0, $length+1, utf8));
Стало обрезать немного корректнее, то есть теперь в редких случаях оставляет слово целиком, в зависимости от того, какая часть слова входит в число символов, подлежащих обрезке... Наверное, проблема в utf8, то есть в этом самом модификаторе u. Куда его поставить?
 

StormMan

Новичок
fixxxer, спасибо!!
Испробовал, лучше стало)) Пока тему можно считать закрытой))) Всем большое спасибо за помощь!

Вот окончательный вариант строки:

PHP:
$string = preg_replace('/\s+?(\S+)?$/u', '', mb_substr($string, 0, $length+1, utf8));
 

StormMan

Новичок
AmdY, гран мерси! :) Google рулит)) я всё по привычке яшу юзаю...

-~{}~ 11.06.10 01:59:

Вот это интересная вещь:
PHP:
<?php
function smarty_modifier_mb_truncate($string, $length = 80, $etc = '...', $charset='UTF-8',
                                  $break_words = false, $middle = false)
{
    if ($length == 0)
        return '';
 
    if (strlen($string) > $length) {
        $length -= min($length, strlen($etc));
        if (!$break_words && !$middle) {
            $string = preg_replace('/\s+?(\S+)?$/', '', mb_substr($string, 0, $length+1, $charset));
        }
        if(!$middle) {
            return mb_substr($string, 0, $length, $charset) . $etc;
        } else {
            return mb_substr($string, 0, $length/2, $charset) . $etc . mb_substr($string, -$length/2, $charset);
        }
    } else {
        return $string;
    }
}
 
/* vim: set expandtab: */
?>
-~{}~ 11.06.10 02:03:

В результате вот что у меня получилось:
PHP:
<?php
function smarty_modifier_mb_truncate($string, $length = 80, $etc = '...', $charset='UTF-8',
                                  $break_words = false, $middle = false)
{
    if ($length == 0)
        return '';
 
    if (strlen($string) > $length) {
        $length -= min($length, strlen($etc));
        if (!$break_words && !$middle) {
            $string = preg_replace('/\s+?(\S+)?$/u', '', mb_substr($string, 0, $length+1, $charset));
        }
        if(!$middle) {
            return mb_substr($string, 0, $length, $charset) . $etc;
        } else {
            return mb_substr($string, 0, $length/2, $charset) . $etc . mb_substr($string, -$length/2, $charset);
        }
    } else {
        return $string;
    }
}
 
/* vim: set expandtab: */
?>
 
Сверху