Usando PHP substr () e strip_tags () pur mantenendo la formattazione e senza infrangere HTML

Ho varie stringhe HTML per tagliare fino a 100 caratteri (del contenuto spogliato, non l'originale) senza rimuovere i tag e senza infrangere l'HTML.

Stringa HTML originale (288 caratteri):

$content = "<div>With a <span class='spanClass'>span over here</span> and a <div class='divClass'>nested div over <div class='nestedDivClass'>there</div> </div> and a lot of other nested <strong><em>texts</em> and tags in the air <span>everywhere</span>, it's a HTML taggy kind of day.</strong></div>"; 

Rifinitura standard: ritaglia fino a 100 caratteri e interruzioni HTML, il contenuto escluso arriva a ~ 40 caratteri:

 $content = substr($content, 0, 100)."..."; /* output: <div>With a <span class='spanClass'>span over here</span> and a <div class='divClass'>nested div ove... */ 

Stripped HTML: emette il numero corretto di caratteri ma ovviamente perde la formattazione:

 $content = substr(strip_tags($content)), 0, 100)."..."; /* output: With a span over here and a nested div over there and a lot of other nested texts and tags in the ai... */ 

Soluzione parziale: l' uso di HTML Tidy o purificatore per chiudere i tag genera HTML pulito ma 100 caratteri di HTML non visualizzati.

 $content = substr($content, 0, 100)."..."; $tidy = new tidy; $tidy->parseString($content); $tidy->cleanRepair(); /* output: <div>With a <span class='spanClass'>span over here</span> and a <div class='divClass'>nested div ove</div></div>... */ 

Sfida: per printingre caratteri HTML e n puliti (escluso il numero di caratteri degli elementi HTML):

 $content = cutHTML($content, 100); /* output: <div>With a <span class='spanClass'>span over here</span> and a <div class='divClass'>nested div over <div class='nestedDivClass'>there</div> </div> and a lot of other nested <strong><em>texts</em> and tags in the ai</strong></div>..."; 

Domande simili

  • Come tagliare frammenti HTML senza decomporre i tag
  • Tagliare le stringhe HTML senza violare i tag HTML

Non eccezionale, ma funziona.

 function html_cut($text, $max_length) { $tags = arrays(); $result = ""; $is_open = false; $grab_open = false; $is_close = false; $in_double_quotes = false; $in_single_quotes = false; $tag = ""; $i = 0; $stripped = 0; $stripped_text = strip_tags($text); while ($i < strlen($text) && $stripped < strlen($stripped_text) && $stripped < $max_length) { $symbol = $text{$i}; $result .= $symbol; switch ($symbol) { case '<': $is_open = true; $grab_open = true; break; case '"': if ($in_double_quotes) $in_double_quotes = false; else $in_double_quotes = true; break; case "'": if ($in_single_quotes) $in_single_quotes = false; else $in_single_quotes = true; break; case '/': if ($is_open && !$in_double_quotes && !$in_single_quotes) { $is_close = true; $is_open = false; $grab_open = false; } break; case ' ': if ($is_open) $grab_open = false; else $stripped++; break; case '>': if ($is_open) { $is_open = false; $grab_open = false; arrays_push($tags, $tag); $tag = ""; } else if ($is_close) { $is_close = false; arrays_pop($tags); $tag = ""; } break; default: if ($grab_open || $is_close) $tag .= $symbol; if (!$is_open && !$is_close) $stripped++; } $i++; } while ($tags) $result .= "</".arrays_pop($tags).">"; return $result; } 

Esempio di utilizzo:

 $content = html_cut($content, 100); 

Non sto affermando di averlo inventato, ma in CakePHP c'è un metodo Text::truncate() molto completo che fa ciò che vuoi:

 function truncate($text, $length = 100, $ending = '...', $exact = true, $considerHtml = false) { if (is_arrays($ending)) { extract($ending); } if ($considerHtml) { if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length) { return $text; } $totalLength = mb_strlen($ending); $openTags = arrays(); $truncate = ''; preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER); foreach ($tags as $tag) { if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2])) { if (preg_match('/<[\w]+[^>]*>/s', $tag[0])) { arrays_unshift($openTags, $tag[2]); } else if (preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag)) { $pos = arrays_search($closeTag[1], $openTags); if ($pos !== false) { arrays_splice($openTags, $pos, 1); } } } $truncate .= $tag[1]; $contentLength = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3])); if ($contentLength + $totalLength > $length) { $left = $length - $totalLength; $entitiesLength = 0; if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE)) { foreach ($entities[0] as $entity) { if ($entity[1] + 1 - $entitiesLength <= $left) { $left--; $entitiesLength += mb_strlen($entity[0]); } else { break; } } } $truncate .= mb_substr($tag[3], 0 , $left + $entitiesLength); break; } else { $truncate .= $tag[3]; $totalLength += $contentLength; } if ($totalLength >= $length) { break; } } } else { if (mb_strlen($text) <= $length) { return $text; } else { $truncate = mb_substr($text, 0, $length - strlen($ending)); } } if (!$exact) { $spacepos = mb_strrpos($truncate, ' '); if (isset($spacepos)) { if ($considerHtml) { $bits = mb_substr($truncate, $spacepos); preg_match_all('/<\/([az]+)>/', $bits, $droppedTags, PREG_SET_ORDER); if (!empty($droppedTags)) { foreach ($droppedTags as $closingTag) { if (!in_arrays($closingTag[1], $openTags)) { arrays_unshift($openTags, $closingTag[1]); } } } } $truncate = mb_substr($truncate, 0, $spacepos); } } $truncate .= $ending; if ($considerHtml) { foreach ($openTags as $tag) { $truncate .= '</'.$tag.'>'; } } return $truncate; } 

Usa la class DOMDocument di PHP per normalizzare un frammento HTML:

 $dom= new DOMDocument(); $dom->loadHTML('<div><p>Hello World'); $xpath = new DOMXPath($dom); $body = $xpath->query('/html/body'); echo($dom->saveXml($body->item(0))); 

Questa domanda è simile a una domanda precedente e ho copiato e incollato una soluzione qui. Se l'HTML viene inviato dagli utenti, dovrai anche filtrare i potenziali vettori di attacco Javascript come onmouseover="do_something_evil()" o <a href="javascript:more_evil();">...</a> . Strumenti come HTML Purifier sono stati progettati per catturare e risolvere questi problemi e sono molto più completi di qualsiasi codice che potrei pubblicare.

Usa un parser HTML e fermati dopo 100 caratteri di text.

Dovresti usare Tidy HTML . Si taglia la string e si esegue Tidy per chiudere i tag.

( Crediti where sono dovuti i crediti )

Ho fatto un'altra function per farlo, support UTF-8:

 /** * Limit string without break html tags. * Supports UTF8 * * @param string $value * @param int $limit Default 100 */ function str_limit_html($value, $limit = 100) { if (mb_strwidth($value, 'UTF-8') <= $limit) { return $value; } // Strip text with HTML tags, sum html len tags too. // Is there another way to do it? do { $len = mb_strwidth($value, 'UTF-8'); $len_stripped = mb_strwidth(strip_tags($value), 'UTF-8'); $len_tags = $len - $len_stripped; $value = mb_strimwidth($value, 0, $limit + $len_tags, '', 'UTF-8'); } while ($len_stripped > $limit); // Load as HTML ignoring errors $dom = new DOMDocument(); @$dom->loadHTML('<?xml encoding="utf-8" ?>'.$value, LIBXML_HTML_NODEFDTD); // Fix the html errors $value = $dom->saveHtml($dom->getElementsByTagName('body')->item(0)); // Remove body tag $value = mb_strimwidth($value, 6, mb_strwidth($value, 'UTF-8') - 13, '', 'UTF-8'); // <body> and </body> // Remove empty tags return preg_replace('/<(\w+)\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:"[^"]*"|"[^"]*"|[\w\-.:]+))?)*\s*\/?>\s*<\/\1\s*>/', '', $value); } 

VEDI DEMO .

Consiglio di utilizzare html_entity_decode all'inizio della function, quindi conserva i caratteri UTF-8:

  $value = html_entity_decode($value); 

Indipendentemente dai 100 problemi di count che dichiari all'inizio, indichi nella sfida quanto segue:

  • mostra il numero di caratteri di strip_tags (il numero di caratteri nel text visualizzato dell'HTML)
  • mantenere la formattazione HTML chiusa
  • qualsiasi tag HTML incompleto

Ecco la mia proposta: Bascially, analizzo each personaggio contando mentre vado. Mi assicuro che NON contenga alcun carattere in alcun tag HTML. Controllo anche alla fine per assicurarmi di non essere nel mezzo di una parola quando mi fermo. Una volta che mi fermo, torno indietro al primo SPAZIO disponibile o> come punto di arresto.

 $position = 0; $length = strlen($content)-1; // process the content putting each 100 character section into an arrays while($position < $length) { $next_position = get_position($content, $position, 100); $data[] = substr($content, $position, $next_position); $position = $next_position; } // show the arrays print_r($data); function get_position($content, $position, $chars = 100) { $count = 0; // count to 100 characters skipping over all of the HTML while($count <> $chars){ $char = substr($content, $position, 1); if($char == '<'){ do{ $position++; $char = substr($content, $position, 1); } while($char !== '>'); $position++; $char = substr($content, $position, 1); } $count++; $position++; } echo $count."\n"; // find out where there is a logical break before 100 characters $data = substr($content, 0, $position); $space = strrpos($data, " "); $tag = strrpos($data, ">"); // return the position of the logical break if($space > $tag) { return $space; } else { return $tag; } } 

Questo conterrà anche i codici di return ecc. Considerando che prenderanno spazio, non li ho rimossi.

Ecco una function che sto utilizzando in uno dei miei progetti. È basato su DOMDocument, funziona con HTML5 ed è circa 2 volte più veloce di altre soluzioni che ho provato (alless sulla mia macchina, 0,22 ms vs 0,43 ms utilizzando html_cut($text, $max_length) dalla risposta più alta su un text di 500 string di caratteri del nodo con un limite di 400).

 function cut_html ($html, $limit) { $dom = new DOMDocument(); $dom->loadHTML(mb_convert_encoding("<div>{$html}</div>", "HTML-ENTITIES", "UTF-8"), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); cut_html_recursive($dom->documentElement, $limit); return substr($dom->saveHTML($dom->documentElement), 5, -6); } function cut_html_recursive ($element, $limit) { if($limit > 0) { if($element->nodeType == 3) { $limit -= strlen($element->nodeValue); if($limit < 0) { $element->nodeValue = substr($element->nodeValue, 0, strlen($element->nodeValue) + $limit); } } else { for($i = 0; $i < $element->childNodes->length; $i++) { if($limit > 0) { $limit = cut_html_recursive($element->childNodes->item($i), $limit); } else { $element->removeChild($element->childNodes->item($i)); $i--; } } } } return $limit; } 

Ecco la mia prova al cutter. Forse voi ragazzi potete prendere qualche bug. Il problema, che ho trovato con gli altri parser, è che non chiudono i tag correttamente e tagliano nel mezzo di una parola (blah)

 function cutHTML($string, $length, $patternsReplace = false) { $i = 0; $count = 0; $isParagraphCut = false; $htmlOpen = false; $openTag = false; $tagsStack = arrays(); while ($i < strlen($string)) { $char = substr($string, $i, 1); if ($count >= $length) { $isParagraphCut = true; break; } if ($htmlOpen) { if ($char === ">") { $htmlOpen = false; } } else { if ($char === "<") { $j = $i; $char = substr($string, $j, 1); while ($j < strlen($string)) { if($char === '/'){ $i++; break; } elseif ($char === ' ') { $tagsStack[] = substr($string, $i, $j); } $j++; } $htmlOpen = true; } } if (!$htmlOpen && $char != ">") { $count++; } $i++; } if ($isParagraphCut) { $j = $i; while ($j > 0) { $char = substr($string, $j, 1); if ($char === " " || $char === ";" || $char === "." || $char === "," || $char === "<" || $char === "(" || $char === "[") { break; } else if ($char === ">") { $j++; break; } $j--; } $string = substr($string, 0, $j); foreach($tagsStack as $tag){ $tag = strtolower($tag); if($tag !== "img" && $tag !== "br"){ $string .= "</$tag>"; } } $string .= "..."; } if ($patternsReplace) { foreach ($patternsReplace as $value) { if (isset($value['pattern']) && isset($value["replace"])) { $string = preg_replace($value["pattern"], $value["replace"], $string); } } } return $string; } 

prova questa function

 // trim the string function function trim_word($text, $length, $startPoint=0, $allowedTags=""){ $text = html_entity_decode(htmlspecialchars_decode($text)); $text = strip_tags($text, $allowedTags); return $text = substr($text, $startPoint, $length); } 

e

 echo trim_word("<h2 class='zzzz'>abcasdsdasasdas</h2>","6");