2011-04-09 41 views
6

有沒有辦法做到這一點,而不寫我自己的功能?切割文本而不破壞html標籤

例如:

$text = 'Test <span><a>something</a> something else</span>.'; 
$text = cutText($text, 2, null, 20, true); 
//result: Test <span><a>something</a></span> 

我需要這個功能堅不可摧

我的問題是類似 This thread 但我需要一個更好的解決方案。我想保持嵌套標籤不變。

到目前爲止,我的算法是:

function cutText($content, $max_words, $max_chars, $max_word_len, $html = false) { 
    $len = strlen($content); 
    $res = ''; 

    $word_count = 0; 
    $word_started = false; 
    $current_word = ''; 
    $current_word_len = 0; 

    if ($max_chars == null) { 
     $max_chars = $len; 
    } 
    $inHtml = false; 
    $openedTags = array(); 
    for ($i = 0; $i<$max_chars;$i++) { 

     if ($content[$i] == '<' && $html) { 
      $inHtml = true; 
     } 

     if ($inHtml) { 
      $max_chars++; 
     }  

     if ($html && !$inHtml) { 

      if ($content[$i] != ' ' && !$word_started) { 
       $word_started = true; 
       $word_count++; 
      } 

      $current_word .= $content[$i]; 
      $current_word_len++; 

      if ($current_word_len == $max_word_len) { 
       $current_word .= '- '; 
      } 

      if (($content[$i] == ' ') && $word_started) { 
       $word_started = false; 
       $res .= $current_word; 
       $current_word = ''; 
       $current_word_len = 0; 
       if ($word_count == $max_words) { 
        return $res; 
       } 
      } 
     } 

     if ($content[$i] == '<' && $html) { 
      $inHtml = true; 
     } 
    } 
    return $res; 
} 

但是,當然,它不會工作。我想過要記住已打開的標籤並關閉它們,如果它們未關閉,但也許有更好的方法?

回答

1

好的我解決了這個問題。

我把它分成了兩部分。 第一切割文本,而不破壞HTML:

function cutHtml($content, $max_words, $max_chars, $max_word_len) { 
    $len = strlen($content); 
    $res = ''; 

    $word_count = 0; 
    $word_started = false; 
    $current_word = ''; 
    $current_word_len = 0; 

    if ($max_chars == null) { 
     $max_chars = $len; 
    } 
    $inHtml = false; 
    $openedTags = array(); 
    $i = 0; 

    while ($i < $max_chars) { 

     //skip any html tags 
     if ($content[$i] == '<') { 
      $inHtml = true; 
      while (true) { 
       $res .= $content[$i]; 
       $i++; 
       while($content[$i] == ' ') { $res .= $content[$i]; $i++; } 

       //skip any values 
       if ($content[$i] == "'") { 
        $res .= $content[$i]; 
        $i++; 
        while(!($content[$i] == "'" && $content[$i-1] != "\\")) { 
         $res .= $content[$i]; 
         $i++; 
        }     
       } 

       //skip any values 
       if ($content[$i] == '"') { 
        $res .= $content[$i]; 
        $i++; 
        while(!($content[$i] == '"' && $content[$i-1] != "\\")) { 
         $res .= $content[$i]; 
         $i++; 
        }     
       } 
       if ($content[$i] == '>') { $res .= $content[$i]; $i++; break;} 
      } 
      $inHtml = false; 
     } 

     if (!$inHtml) { 

      while($content[$i] == ' ') { $res .= $content[$i]; $letter_count++; $i++; } //skip spaces 

      $word_started = false; 
      $current_word = ''; 
      $current_word_len = 0; 
      while (!in_array($content[$i], array(' ', '<', '.', ','))) { 

       if (!$word_started) { 
        $word_started = true; 
        $word_count++; 
       } 

       $current_word .= $content[$i]; 
       $current_word_len++; 

       if ($current_word_len == $max_word_len) { 
        $current_word .= '-'; 
        $current_word_len = 0; 
       } 

       $i++; 
      } 

      if ($letter_count > $max_chars) { 
       return $res; 
      } 

      if ($word_count < $max_words) { 
       $res .= $current_word; 
       $letter_count += strlen($current_word); 
      } 

      if ($word_count == $max_words) { 
       $res .= $current_word; 
       $letter_count += strlen($current_word); 
       return $res; 
      } 
     } 

    } 
    return $res; 
} 

而接下來的事情就是關閉未關閉的標籤:

function cleanTags(&$html) { 
    $count = strlen($html); 
    $i = -1; 
    $openedTags = array(); 

    while(true) { 
     $i++; 
     if ($i >= $count) break; 
     if ($html[$i] == '<') { 

      $tag = ''; 
      $closeTag = ''; 
      $reading = false; 
      //reading whole tag 
      while($html[$i] != '>') { 
       $i++; 

       while($html[$i] == ' ') $i++; //skip any spaces (need to be idiot proof) 
       if (!$reading && $html[$i] == '/') { //closing tag 
        $i++; 
        while($html[$i] == ' ') $i++; //skip any spaces 

        $closeTag = ''; 

        while($html[$i] != ' ' && $html[$i] != '>') { //start reading first actuall string 
         $reading = true; 
         $html[$i] = strtolower($html[$i]); //tags to lowercase 
         $closeTag .= $html[$i]; 
         $i++; 
        } 
        $c = count($openedTags); 
        if ($c > 0 && $openedTags[$c-1] == $closeTag) array_pop($openedTags); 
       } 

       if (!$reading) //read only tag 
       while($html[$i] != ' ' && $html[$i] != '>') { //start reading first actuall string 
        $reading = true; 
        $html[$i] = strtolower($html[$i]); //tags to lowercase 
        $tag .= $html[$i]; 
        $i++; 
       } 

       //skip any values 
       if ($html[$i] == "'") { 
        $i++; 
        while(!($html[$i] == "'" && $html[$i-1] != "\\")) { 
         $i++; 
        }     
       } 

       //skip any values 
       if ($html[$i] == '"') { 
        $i++; 
        while(!($html[$i] == '"' && $html[$i-1] != "\\")) { 
         $i++; 
        }     
       } 

       if ($reading && $html[$i] == '/') { //self closed tag 
        $tag = ''; 
        break; 
       } 
      } 
      if (!empty($tag)) $openedTags[] = $tag; 

     } 

    } 

    while (count($openedTags) > 0) { 
     $tag = array_pop($openedTags); 
     $html .= "</$tag>"; 
    } 
} 

這不是白癡的證據,但TinyMCE的將清除這個東西出來所以進一步的清洗是沒有必要的。

它可能有點長,但我不認爲它會吃掉很多資源,它應該比正則表達式更快。

1

嘗試是這樣的

function cutText($inputText, $start, $length) { 
    $temp = $inputText; 
    $res = array(); 
    while (strpos($temp, '>')) { 
     $ts = strpos($temp, '<'); 
     $te = strpos($temp, '>'); 
     if ($ts > 0) $res[] = substr($temp, 0, $ts); 
     $res[] = substr($temp, $ts, $te - $ts + 1); 
     $temp = substr($temp, $te + 1, strlen($temp) - $te); 
     } 
    if ($temp != '') $res[] = $temp; 
    $pointer = 0; 
    $end = $start + $length - 1; 
    foreach ($res as &$part) { 
     if (substr($part, 0, 1) != '<') { 
     $l = strlen($part); 
     $p1 = $pointer; 
     $p2 = $pointer + $l - 1; 
     $partx = ""; 
     if ($start <= $p1 && $end >= $p2) $partx = ""; 
     else { 
      if ($start > $p1 && $start <= $p2) $partx .= substr($part, 0, $start-$pointer); 
      if ($end >= $p1 && $end < $p2) $partx .= substr($part, $end-$pointer+1, $l-$end+$pointer); 
      if ($partx == "") $partx = $part; 
      } 
     $part = $partx; 
     $pointer += $l; 
     } 
     } 
    return join('', $res); 
    } 

參數:

  • $ inputText的 - 輸入文本
  • $開始 - 第一個字符
  • 的位置
  • $長度 - 菜單字符如何È要移除


實施例#1 - 卸下前3個字符

$text = 'Test <span><a>something</a> something else</span>.'; 
    $text = cutText($text, 0, 3); 
    var_dump($text); 

輸出(除去 「維護設備」)

string(47) "t <span><a>something</a> something else</span>." 

卸下前10個字符

$text = cutText($text, 0, 10); 

輸出(除去 「測試SOMET」)

string(40) "<span><a>hing</a> something else</span>." 

實施例2 - 刪除內的字符 - 「ES」 從 「測試」

$text = cutText($text, 1, 2); 

輸出

string(48) "Tt <span><a>something</a> something else</span>." 

刪除「東西el」

$text = cutText($text, 9, 18); 

輸出

string(32) "Test <span><a>some</a>se</span>." 

希望這有助於。

好吧,也許這不是最好的解決方案,但這是我目前可以做的一切。

+0

@Kaminari - 我已經把這個功能在一些測試,但仍不能保證它在所有可能的情況。 – Wh1T3h4Ck5 2011-04-09 02:11:10

+0

問題是,我不想把言語減半。我需要從整個內容中製作一個簡潔的內容而不會破壞HTML – Kaminari 2011-04-09 18:26:15

2

這完美的作品對我來說:

function trimContent ($str, $trimAtIndex) { 

    $beginTags = array();  
    $endTags = array(); 

    for($i = 0; $i < strlen($str); $i++) { 
     if($str[$i] == '<') 
      $beginTags[] = $i; 
     else if($str[$i] == '>') 
      $endTags[] = $i; 
    } 

    foreach($beginTags as $k=>$index) { 
     // Trying to trim in between tags. Trim after the last tag 
     if(($trimAtIndex >= $index) && ($trimAtIndex <= $endTags[$k]) ) { 
      $trimAtIndex = $endTags[$k]; 
     } 
    } 

    return substr($str, 0, $trimAtIndex); 
} 
相關問題