Форум dkLab и Denwer
Здесь общаются Web-разработчики.
Генеральный спонсор:
Хостинг «Джино»

Обрезка текста с тегами (SETKINS)
Author Message
SETKINS
Заглянувший



Joined: 29 Nov 2007
Posts: 14
Карма: 0
   поощрить/наказать


PostPosted: Thu Jan 10, 2008 3:44 am (написано за 2 минуты 10 секунд)
   Post subject: Обрезка текста с тегами
Reply with quote

есть какой-то текст с тегами html

<img>
<a href >
<object >


может кто сталкивался с его обрезкой до определенной длины.
т.е. вообще это новости. Если они очень большие, то их надо резать допустим выводить первые 250 символов

как сделать так чтобы выдался текст, может чуть больше, но если на 245 месте начался тег, чтобы его не раздробило,
 а вывело целиком

спасибо!!!
Back to top
View user's profile Send private message
Юрий Насретдинов
Модератор



Joined: 13 Mar 2003
Posts: 8642
Карма: 198
   поощрить/наказать

Location: 007 495

PostPosted: Thu Jan 10, 2008 3:59 am (спустя 14 минут; написано за 4 минуты 47 секунд)
   Post subject:
Reply with quote

SETKINS
Задачка довольно сложная. Я думаю, что самое лучшее решение -- это сначала удалять все теги из текста новости, а потом её обрезать (причём, ИМХО, лучше не оставлять не то, чтобы кусков тегов, но и даже кусков предложений). При необходимости, можно попробовать с помощью «нечёткой логики :)» возвращать теги в те предложения, которые остались целы после обрезки текста.
Back to top
View user's profile Send private message Send e-mail
kernel32
Участник форума



Joined: 18 Mar 2006
Posts: 256
Карма: 24
   поощрить/наказать

Location: Москва

PostPosted: Thu Jan 10, 2008 7:42 am (спустя 3 часа 43 минуты; написано за 2 минуты 38 секунд)
   Post subject:
Reply with quote

Думаю, тут надо strip_tags_smart(). А если хочется, чтоб в возвращаемом превью была и картинка, то можно вставлять туда первую картинку из новости ( <img...> ), вытаскиваю её через preg_match()
У меня где-то была похожая функция, которая возвращала превью новости, но там bb-коды... Написал сам, но глючноватая она. На досуге, может, допишу.
Back to top
View user's profile Send private message
SETKINS
Заглянувший



Joined: 29 Nov 2007
Posts: 14
Карма: 0
   поощрить/наказать


PostPosted: Thu Jan 10, 2008 10:03 pm (спустя 14 часов 20 минут; написано за 37 секунд)
   Post subject:
Reply with quote

у меня html коды получаются после парсинга bbcode

так если бы поделился примером функции было бы круто
Back to top
View user's profile Send private message
kernel32
Участник форума



Joined: 18 Mar 2006
Posts: 256
Карма: 24
   поощрить/наказать

Location: Москва

PostPosted: Thu Jan 10, 2008 10:50 pm (спустя 47 минут; написано за 47 секунд)
   Post subject:
Reply with quote

SETKINS, примером функции делиться не буду, т.к. она потенциально недописана.
Как допишу - с удовольствием поделюсь. :) Время только бы найти.
Back to top
View user's profile Send private message
SETKINS
Заглянувший



Joined: 29 Nov 2007
Posts: 14
Карма: 0
   поощрить/наказать


PostPosted: Fri Jan 11, 2008 12:37 am (спустя 1 час 47 минут; написано за 2 минуты 10 секунд)
   Post subject:
Reply with quote

function breakLongWords($str, $maxLength, $char){
    $wordEndChars = array(" ", "\n", "\r", "\f", "\v", "\0");
    $count = 0;
    $newStr = "";
    $openTag = false;


 

    for($i=0; $i<strlen($str); $i++){
        $newStr .= $str{$i};
       
        if($str{$i} == "<"){
            $openTag = true;
            continue;
        }
        if(($openTag) && ($str{$i} == ">")){
            $openTag = false;
            continue;
        }
       
        if(!$openTag){
            if(in_array($str{$i}, $wordEndChars)){//If not word ending char
            
                if($i>$maxLength){//if current word max length is reached
                    return $newStr;
                 
                }
            }else{//Else char is word ending, reset word char count
                    
            }
        }
       
    }//End for
    return $newStr;
}




вот эта штука в принципе режет текст, правда у нее проверка отстойная на открыт тег или нет
подходит допустим для тегов [b] [i] [img]

т.е. тег [link] site.dom [/link] она спокойно порежет, без пробелов правда не порежет.
Back to top
View user's profile Send private message
Валенок
Участник форума



Joined: 06 Apr 2006
Posts: 520
Карма: -3
   поощрить/наказать


PostPosted: Fri Jan 11, 2008 8:55 am (спустя 8 часов 18 минут; написано за 4 минуты 12 секунд)
   Post subject:
Reply with quote

SETKINS перед этим (и после этого, если нет защиты от отрезания конечных тегов) я бы посоветовал провести HTML через Tidy, да и вообще лучше не по байту добавлять к строке, а вести переменную и ее инкрементировать, а потом строку обрезать... и обрезать лучше перед тегом, т.е. сделать так:
Code (any language): скопировать код в буфер обмена
Back to top
View user's profile Send private message
kernel32
Участник форума



Joined: 18 Mar 2006
Posts: 256
Карма: 24
   поощрить/наказать

Location: Москва

PostPosted: Fri Jan 11, 2008 1:24 pm (спустя 4 часа 28 минут; написано за 1 минуту 3 секунды)
   Post subject:
Reply with quote

Вот. Написал с нуля.
Только прошу: больно не бейте, сделал на скорую руку и особо не тестировал... :)
Code (php): скопировать код в буфер обмена
function breakLongWords($str, $maxLength, $endChar){
        // Если длина строки меньше $maxLength
        if (strlen (www.php.net/strlen)($str) <= $maxLength) return $str;
        // Массив информации о тэгах.
        // Для каждого элемента:
        //        array(имя_тэга, шаблон_начала, закрывающий_тэг);
        $tags = array (www.php.net/array)(
                array (www.php.net/array)('b', '\[b\]', '[/b]'),
                array (www.php.net/array)('i', '\[i\]', '[/i]'),
                array (www.php.net/array)('color', '\[color=.*?\]', '[/color]'),
        );
        // Отрезаем строку по вхождению $endChar...
        $str = substr (www.php.net/substr)($str, 0, strpos (www.php.net/strpos)($str, $endChar, $maxLength));
        // Активируем тэги, чтобы потом узнать, какие остались незакрыты
        $newStr = activateBbTags($str);
        // Генерируем паттерн. Получается что-то вроде {\[b\]|\[i\]|\[color=.*?\]}
        $pattern = '';
        foreach ($tags as $c=>$tInfo) {
                $pattern = $pattern.$tInfo[1];
                if ($c<count($tags)-1) $pattern = $pattern . "|";
        }
        $pattern = "{".$pattern."}";
        // Теперь ищем открытые тэги, которые надо закрыть.
        preg_match_all (www.php.net/preg_match_all)($pattern, $newStr, $openedTags, PREG_PATTERN_ORDER);
        // Ставим элементы массива совпадений в обратном порядке,
        // чтобы в правильном порядке закрывать открытые тэги
        $openedTags = array_reverse (www.php.net/array_reverse)($openedTags[0]);
        // теперь $newStr - отрезанный текст без активированных тэгов.
        $newStr = $str;
        // Ищем, какой тэг открыт, и в конец строки добавляем его закрывающий тэг
        foreach ($openedTags as $oTag) {
                // Ищем в каждом элементе массива $tags
                foreach ($tags as $tInfo) {
                        // Если нашли, что надо, закрываем тэг и выходим из этого (внутреннего) цикла.
                        // Внешний цикл продолжается...
                        if (preg_match (www.php.net/preg_match)('{^'.$tInfo[1].'$}', $oTag)) {
                                $newStr = $newStr.$tInfo[2];
                                break;
                        }
                }
        }
        // Теперь возвращаем результат :) ^_^
        return $newStr;
}

function activateBbTags($str) {
        $str = preg_replace (www.php.net/preg_replace)('{\[b\](.+?)\[/b\]}', '<b>$1</b>', $str);
        $str = preg_replace (www.php.net/preg_replace)('{\[i\](.+?)\[/i\]}', '<i>$1</i>', $str);
        $str = preg_replace (www.php.net/preg_replace)('{\[color=(.*?)\](.+?)\[/color\]}', '<font color="$1">$2</font>', $str);
        return $str;
}

$text2= 'asdf; [i]lkhj [color=red]skl adfj[/color] sdlfk sdafasd [i] sdfasdf asdklhfjkashd';
echo (www.php.net/echo) activateBbTags(breakLongWords($text2, 20, " "));
правда, если вызвать мою функцию так:
Code (php): скопировать код в буфер обмена
echo (www.php.net/echo) activateBbTags(breakLongWords($text2, 50, " "));
то в результате получим
Code (any language): скопировать код в буфер обмена
asdf; <i>lkhj <font color="red">skl adfj sdlfk sdafasd [i]</i></font>[/i]
Но тут уже дело в "жадности" и "ленивости" квантификаторов...
Надо бы с этим разобраться.
Back to top
View user's profile Send private message
kernel32
Участник форума



Joined: 18 Mar 2006
Posts: 256
Карма: 24
   поощрить/наказать

Location: Москва

PostPosted: Fri Jan 11, 2008 1:37 pm (спустя 13 минут; написано за 17 секунд)
   Post subject:
Reply with quote

Нашёл еще кое-что: ru.php.net/preg_match_all
неизвестный аффтар написал функцию, которая закрывает все html-тэги. Не знаю, на сколько она адекватная.
Можно попробовать использовать её.
Code (php): скопировать код в буфер обмена
/**
 * close all open xhtml tags at the end of the string
 *
 * @author Milian Wolff <[url]http://milianw.de[/url]>
 * @param string $html
 * @return string
 */

function closetags($html){
  #put all opened tags into an array
  preg_match_all (www.php.net/preg_match_all)("#<([a-z]+)( .*)?(?!/)>#iU",$html,$result);
  $openedtags=$result[1];

  #put all closed tags into an array
  preg_match_all (www.php.net/preg_match_all)("#</([a-z]+)>#iU",$html,$result);
  $closedtags=$result[1];
  $len_opened = count (www.php.net/count)($openedtags);
  # all tags are closed
  if(count (www.php.net/count)($closedtags) == $len_opened){
    return $html;
  }
  $openedtags = array_reverse (www.php.net/array_reverse)($openedtags);
  # close tags
  for($i=0;$i<$len_opened;$i++) {
    if (!in_array (www.php.net/in_array)($openedtags[$i],$closedtags)){
      $html .= '</'.$openedtags[$i].'>';
    } else {
      unset (www.php.net/unset)($closedtags[array_search (www.php.net/array_search)($openedtags[$i],$closedtags)]);
    }
  }
  return $html;
}
Back to top
View user's profile Send private message
kernel32
Участник форума



Joined: 18 Mar 2006
Posts: 256
Карма: 24
   поощрить/наказать

Location: Москва

PostPosted: Fri Jan 11, 2008 2:19 pm (спустя 42 минуты; написано за 41 секунду)
   Post subject:
Reply with quote

Посмотрел я, подумал, и понял, что лучше использовать следующий вариант:
Code (php): скопировать код в буфер обмена
function conciseStr($str, $maxLength, $endChar){
        if (strlen (www.php.net/strlen)($str) <= $maxLength) return closeTags(activateBbTags($str));
        $str = activateBbTags($str); // Ваша функция, которая активирует нужные бб-тэги
        $str = substr (www.php.net/substr)($str, 0, strpos (www.php.net/strpos)($str, $endChar, $maxLength));
        $str = closeTags($str); // Эта функция выше
        return $str;
}

$text2= 'asdf; [i_tag]lkhj [color_tag=red]skl adfj[/color_tag] sdlfk sdafasd [/i_tag] sdfa sdf asdklhfjkashd';
echo (www.php.net/echo) conciseStr($text2, 50, " ");
Хотя в некоторых случаях и так не пойдёт... Но по крайней мере, если супер-пупер навороченных тэгов нет, то этот вариант сойдёт.
Back to top
View user's profile Send private message
WingedFox
Профессионал



Joined: 29 Apr 2003
Posts: 4064
Карма: 268
   поощрить/наказать

Location: Питер

PostPosted: Fri Jan 18, 2008 2:01 pm (спустя 6 дней 23 часа 41 минуту; написано за 9 минут 16 секунд)
   Post subject:
Reply with quote

Жизнь куда как проще:
Code (php): скопировать код в буфер обмена
/**
 * Function cuts string with the HTML tags by the specified number of chars and strips empty HTML tags from the output.
 *
 * @param string $txt text to cut
 * @param int $len number of chars to keep in the resulting string
 * @param string $delim optional string of the stop-chars, used to split the text when limit reached in the middle of the current word
 * @return string
 * @author Ilya Lebedev
 */

function breakword ($txt,$len,$delim='\s;,.!?:#') {
    $txt = preg_replace_callback (www.php.net/preg_replace_callback) ("#(</?[a-z]+(?:>|\s[^>]*>)|[^<]+)#mi"
                                  ,create_function (www.php.net/create_function)('$a'
                                                  ,'static $len = '.$len.';'
                                                  .'$len1 = $len-1;'
                                                  .'$delim = \''.str_replace("#","\\#",$delim).'\';'
                                                  .'if ("<" == $a[0]{0}) return $a[0];'
                                                  .'if ($len<=0) return "";'
                                                  .'$res = preg_split("#(.{0,$len1}+(?=[$delim]))|(.{0,$len}[^$delim]*)#ms",$a[0],2,PREG_SPLIT_DELIM_CAPTURE);'
                                                  .'if ($res[1]) { $len -= strlen($res[1])+1; $res = $res[1];}'
                                                  .'else         { $len -= strlen($res[2]); $res = $res[2];}'
                                                  .'$res = rtrim($res);/*preg_replace("#[$delim]+$#m","",$res);*/'
                                                  .'return $res;')
                                  ,$txt);
     while (preg_match (www.php.net/preg_match)("#<([a-z]+)[^>]*>\s*</\\1>#mi",$txt)) {
         $txt = preg_replace (www.php.net/preg_replace)("#<([a-z]+)[^>]*>\s*</\\1>#mi","",$txt);
     }
     return $txt;
}

$text = "<div>some:text;<a href=\"#\">some.url<em > emphased</em> some more url</a> <span > more text</span> ending.</div>";

echo (www.php.net/echo) "<pre>";
echo (www.php.net/echo) "text: ".htmlspecialchars($text);
echo (www.php.net/echo) "<hr />";
echo (www.php.net/echo) "len = 1 : ".htmlspecialchars(breakword($text,1));
echo (www.php.net/echo) "<hr />";
echo (www.php.net/echo) "len = 4 : ".htmlspecialchars(breakword($text,4));
echo (www.php.net/echo) "<hr />";
echo (www.php.net/echo) "len = 5 : ".htmlspecialchars(breakword($text,5));
echo (www.php.net/echo) "<hr />";
echo (www.php.net/echo) "len = 6 : ".htmlspecialchars(breakword($text,6));
echo (www.php.net/echo) "<hr />";
echo (www.php.net/echo) "len = 9 : ".htmlspecialchars(breakword($text,9));
echo (www.php.net/echo) "<hr />";
echo (www.php.net/echo) "len = 10 : ".htmlspecialchars(breakword($text,10));
echo (www.php.net/echo) "<hr />";
echo (www.php.net/echo) "len = 11 : ".htmlspecialchars(breakword($text,11));
echo (www.php.net/echo) "<hr />";
echo (www.php.net/echo) "len = 14 : ".htmlspecialchars(breakword($text,14));
echo (www.php.net/echo) "<hr />";
echo (www.php.net/echo) "len = 15 : ".htmlspecialchars(breakword($text,15));
echo (www.php.net/echo) "<hr />";
echo (www.php.net/echo) "len = 16 : ".htmlspecialchars(breakword($text,16));
echo (www.php.net/echo) "</pre>";
Результат:
Code (any language): скопировать код в буфер обмена
len = 1 : <div>some</div>
len = 4 : <div>some</div>
len = 5 : <div>some</div>
len = 6 : <div>some:text</div>
len = 9 : <div>some:text</div>
len = 10 : <div>some:text</div>
len = 11 : <div>some:text;<a href="#">some</a></div>
len = 14 : <div>some:text;<a href="#">some</a></div>
len = 15 : <div>some:text;<a href="#">some</a></div>
len = 16 : <div>some:text;<a href="#">some.url</a></div>
Back to top
View user's profile Send private message
Фуртуна Максим
Заглянувший



Joined: 21 Jun 2009
Posts: 2
Карма: 1
   поощрить/наказать

Location: Россия, Туапсе

PostPosted: Sun Jun 21, 2009 11:51 am (спустя 1 год 5 месяцев 2 дня 21 час 50 минут; написано за 1 минуту 41 секунду)
   Post subject:
Reply with quote

WingedFox Спасибо, решение гениальное. Только не могу понять зачем здесь $len1, если потом происходит rtrim над строкой. Думаю можно $len1 заменить на $len.
Back to top
View user's profile Send private message
WingedFox
Профессионал



Joined: 29 Apr 2003
Posts: 4064
Карма: 268
   поощрить/наказать

Location: Питер

PostPosted: Mon Jun 22, 2009 11:11 am (спустя 23 часа 19 минут; написано за 16 секунд)
   Post subject:
Reply with quote

Фуртуна Максим
На здоровье! =)

Посмотрите там preg_split
Back to top
View user's profile Send private message
PahaW
Заглянувший



Joined: 21 Jan 2006
Posts: 8
Карма: 0
   поощрить/наказать


PostPosted: Sat Jul 18, 2009 9:56 am (спустя 25 дней 22 часа 45 минут; написано за 1 минуту 53 секунды)
   Post subject:
Reply with quote

WingedFox
смотриТЕ, а не проще ли вынести в отдельную функцию ин е лепить в до кучи, не понятно же.
Code (php): скопировать код в буфер обмена
$delim='\s;,.!?:#';

function create2($a){
         global (www.php.net/global) $delim;
         static (www.php.net/static) $len = '.$len.';
         $len1 = $len-1;
         $delim = str_replace (www.php.net/str_replace)("#","\\#",$delim);
         if ("<" == $a[0]{0}) return $a[0];
         if ($len<=0) return "";
         $res = preg_split (www.php.net/preg_split)("#(.{0,$len1}+(?=[$delim]))|(.{0,$len}[^$delim]*)#ms",$a[0],2,PREG_SPLIT_DELIM_CAPTURE);
         if ($res[1]) {
            $len -= strlen (www.php.net/strlen)($res[1])+1; $res = $res[1];
         } else {
            $len -= strlen (www.php.net/strlen)($res[2]); $res = $res[2];
         }
         $res = rtrim (www.php.net/rtrim)($res);
         /*preg_replace("#[$delim]+$#m","",$res);*/
         return $res;

}

function breakword ($txt,$len,$delim='\s;,.!?:#') {
    $txt = preg_replace_callback (www.php.net/preg_replace_callback) ("#(</?[a-z]+(?:>|\s[^>]*>)|[^<]+)#mi", "create2", $txt);
     while (preg_match (www.php.net/preg_match)("#<([a-z]+)[^>]*>\s*</\\1>#mi",$txt)) {
         $txt = preg_replace (www.php.net/preg_replace)("#<([a-z]+)[^>]*>\s*</\\1>#mi","",$txt);
     }
     return $txt;
}
и не совсем понятна строчка:
Code (php): скопировать код в буфер обмена
$res = preg_split (www.php.net/preg_split)("#(.{0,$len1}+(?=[$delim]))|(.{0,$len}[^$delim]*)#ms",$a[0],2,PREG_SPLIT_DELIM_CAPTURE);
можно ее расшифровать?
PREG_SPLIT_DELIM_CAPTURE
Quote:
Если этот флаг будет установлен, то введенное выражение в образце разделителя будет захвачено и возвращено также.
я имею ввиду pattern и limit почему 2?

Last edited by PahaW on Mon Jul 20, 2009 6:19 am; edited 1 time in total
Back to top
View user's profile Send private message
bæv
Модератор «Дзена»



Joined: 27 Aug 2003
Posts: 7275
Карма: 9985
   поощрить/наказать


PostPosted: Mon Jul 20, 2009 1:38 am (спустя 1 день 15 часов 41 минуту; написано за 55 секунд)
   Post subject:
Reply with quote


М

PahaW, forum.dklab.ru/about/todo/PravilaEtogoForuma-ProchitayteObyazatelno.html — на форуме принято общение «на Вы».
Back to top
View user's profile Send private message
WingedFox
Профессионал



Joined: 29 Apr 2003
Posts: 4064
Карма: 268
   поощрить/наказать

Location: Питер

PostPosted: Mon Jul 20, 2009 11:51 am (спустя 10 часов 12 минут; написано за 2 минуты 3 секунды)
   Post subject:
Reply with quote

PahaW
Да можно много чего, но глобальная переменная с разделителем - это бессмысленное изменение, несущее больше вреда, нежели пользы.
Суть рега такова что он захватывает все слова и "остальное", которое должно оставаться на месте.
Back to top
View user's profile Send private message
PahaW
Заглянувший



Joined: 21 Jan 2006
Posts: 8
Карма: 0
   поощрить/наказать


PostPosted: Tue Jul 21, 2009 2:59 am (спустя 15 часов 8 минут; написано за 1 минуту 17 секунд)
   Post subject:
Reply with quote

WingedFox
тоесть массив символов что находят между тегами, как же тогда идет подсчет с длиной, тоже не совсем понятно, ведь между каждыми новыми тегами мы должны отсчитывать, новое кол-во символов.
Back to top
View user's profile Send private message
WingedFox
Профессионал



Joined: 29 Apr 2003
Posts: 4064
Карма: 268
   поощрить/наказать

Location: Питер

PostPosted: Tue Jul 21, 2009 9:30 am (спустя 6 часов 31 минуту; написано за 3 минуты 12 секунд)
   Post subject:
Reply with quote

PahaW
Посмотрите на рег - он либо режет строчку по разделителю, если требуемая длина приходится на середину текста, либо захватывает весь текст, если длина отрезаемого больше. После этого, корректируется длина для работы со следующим совпадением.
Back to top
View user's profile Send private message
bestsite4u
Заглянувший
Banned


Joined: 25 Jul 2009
Posts: 2
Карма: 0
   поощрить/наказать

Location: СПБ

PostPosted: Sat Jul 25, 2009 1:31 pm (спустя 4 дня 4 часа 37 секунд; написано за 32 секунды)
   Post subject:
Reply with quote

мне кажется лучший вариант - это использование регулярных выражение, как написал PahaW
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic All times are GMT + 3 Hours
Page 1 of 1    Email to a Friend.
You cannot post new topics in this forum. You cannot reply to topics in this forum. You cannot edit your posts in this forum. You cannot delete your posts in this forum. You cannot vote in polls in this forum. You cannot attach files in this forum. You can download files in this forum.
XML