问题
I've got a variable which is formatted with random HTML code. I call it to {$text}
and i truncate it.
The value is for example:
<div>Lorem <i>ipsum <b>dolor <span>sit </span>amet</b>, con</i> elit.</div>
If i truncate the text's first ~30 letters, I'll get this:
<div>Lorem <i>ipsum <b>dolor <span>sit
The problem is, I can't close the elements. So, I need a script, which check the <*>
elements in the code (where * could be anything), and if it dont have a close tag, close 'em.
Please help me in this. Thanks.
Solution after hours, and 4 vote-up @ stackoverflow:
PHP:
...
function closetags($content) {
preg_match_all('#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $content, $result);
$openedtags = $result[1];
preg_match_all('#</([a-z]+)>#iU', $content, $result);
$closedtags = $result[1];
$len_opened = count($openedtags);
if (count($closedtags) == $len_opened) {
return $content;
}
$openedtags = array_reverse($openedtags);
for ($i=0; $i < $len_opened; $i++) {
if (!in_array($openedtags[$i], $closedtags)) {
$content .= '</'.$openedtags[$i].'>';
} else {
unset($closedtags[array_search($openedtags[$i], $closedtags)]);
}
}
return $content;
}
...
the TPL:
{$pages[j].text|truncate:300|@closetags}
回答1:
Pull out all the open tags, push them into an array (array_1) one-by-one.
Pull out all of the closed tags, push them into an array (array_2) one-by-on (this includes self closing tags).
For the tags in the first array (array_1) that are not found in the second array (array_2), add them to the html.
[edit]
Of course, this method fails miserably if you do not write proper html... but whatchagonnado?
Another way would be to look ahead in the string to see which tags are closed and close them as needed.
回答2:
To simplify, if the code is valid XML before truncating and you don't cut off tags in half, the algorithm would be something like this:
- Push opening tags onto a stack
- Pop them off when you find the closing tag (which will match if the code is valid)
- When you get to the end, start popping to create closing. The remaining tags should be appended to the original (truncated) text.
Example:
<div>Lorem <i>ipsum <b>dolor <span>sit </span>amet</b><div>
- Push "div","i","b","span"
- Found closing tag "span"
- Pop "span"
- Found closing tag "b"
- Pop "b"
- Push "div"
- End of truncated text
- Pop "div" --> add
</div>
to text - Pop "b" --> add
</b>
to text - Pop "i" --> add
</i>
to text - Pop "div" --> add
</div>
to text - End
回答3:
for other people like me and you I found this code ,I think is a better solution
add this file named "modifier.html_substr.php"
<?php
/*
* Smarty plugin
*
-------------------------------------------------------------
* File: modifier.html_substr.php
* Type: modifier
* Name: html_substr
* Version: 1.0
* Date: June 19th, 2003
* Purpose: Cut a string preserving any tag nesting and matching.
* Install: Drop into the plugin directory.
* Author: Original Javascript Code: Benjamin Lupu <hide@address.com>
* Translation to PHP & Smarty: Edward Dale <hide@address.com>
* Modification to add a string: Sebastian Kuhlmann <hide@address.com>
* Modification to put the added string before closing <p> or <li> tags by Peter Carter http://www.podhawk.com
-------------------------------------------------------------
*/
function smarty_modifier_html_substr($string, $length, $addstring="")
{
//some nice italics for the add-string
if (!empty($addstring)) $addstring = "<i> " . $addstring . "</i>";
if (strlen($string) > $length) {
if( !empty( $string ) && $length>0 ) {
$isText = true;
$ret = "";
$i = 0;
$currentChar = "";
$lastSpacePosition = -1;
$lastChar = "";
$tagsArray = array();
$currentTag = "";
$tagLevel = 0;
$addstringAdded = false;
$noTagLength = strlen( strip_tags( $string ) );
// Parser loop
for( $j=0; $j<strlen( $string ); $j++ ) {
$currentChar = substr( $string, $j, 1 );
$ret .= $currentChar;
// Lesser than event
if( $currentChar == "<") $isText = false;
// Character handler
if( $isText ) {
// Memorize last space position
if( $currentChar == " " ) { $lastSpacePosition = $j; }
else { $lastChar = $currentChar; }
$i++;
} else {
$currentTag .= $currentChar;
}
// Greater than event
if( $currentChar == ">" ) {
$isText = true;
// Opening tag handler
if( ( strpos( $currentTag, "<" ) !== FALSE ) &&
( strpos( $currentTag, "/>" ) === FALSE ) &&
( strpos( $currentTag, "</") === FALSE ) ) {
// Tag has attribute(s)
if( strpos( $currentTag, " " ) !== FALSE ) {
$currentTag = substr( $currentTag, 1, strpos( $currentTag, " " ) - 1 );
} else {
// Tag doesn't have attribute(s)
$currentTag = substr( $currentTag, 1, -1 );
}
array_push( $tagsArray, $currentTag );
} else if( strpos( $currentTag, "</" ) !== FALSE ) {
array_pop( $tagsArray );
}
$currentTag = "";
}
if( $i >= $length) {
break;
}
}
// Cut HTML string at last space position
if( $length < $noTagLength ) {
if( $lastSpacePosition != -1 ) {
$ret = substr( $string, 0, $lastSpacePosition );
} else {
$ret = substr( $string, $j );
}
}
// Close broken XHTML elements
while( sizeof( $tagsArray ) != 0 ) {
$aTag = array_pop( $tagsArray );
// if a <p> or <li> tag needs to be closed, put the add-string in first
if (($aTag == "p" || $aTag == "li") && strlen($string) > $length) {
$ret .= $addstring;
$addstringAdded = true;
}
$ret .= "</" . $aTag . ">\n";
}
} else {
$ret = "";
}
// if we have not added the add-string already
if ( strlen($string) > $length && $addstringAdded == false) {
return( $ret.$addstring );
}
else {
return ( $ret );
}
}
else {
return ( $string );
}
}
?>
usage
{$yourdata|html_substr:300:' ...'}
来源:https://stackoverflow.com/questions/2671053/truncate-a-html-formatted-text-with-smarty