2011-04-09 18 views
6

¿Hay alguna manera de hacerlo sin escribir mi propia función?Cortar texto sin destruir las etiquetas html

Por ejemplo:

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

Necesito hacer esta función indestructible

Mi problema es similar al This thread pero necesito una mejor solución. Me gustaría mantener las etiquetas anidadas intactas.

Hasta ahora mi algoritmo es:

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; 
} 

Pero, por supuesto, no va a funcionar. Pensé en recordar las etiquetas abiertas y cerrarlas si no estaban cerradas, pero ¿tal vez hay una mejor manera?

Respuesta

1

Ok, resolví esto.

Dividí esto en 2 partes. En primer lugar texto de corte sin destruir 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; 
} 

Y lo siguiente que se está cerrando etiquetas sin cerrar:

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>"; 
    } 
} 

No es idiota prueba, pero tinymce aclarará esto fuera así una limpieza más profunda no es necesario.

Puede que sea un poco largo, pero no creo que vaya a consumir muchos recursos y debería ser más rápido que la expresión regular.

1

intentar algo como esto

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); 
    } 

Parámetros:

  • $ inputText - Entrada de texto
  • $ empiezan - posición del primer carácter
  • $ longitud - cómo los caracteres del menú w e desea eliminar


Ejemplo # 1 - Extracción primeros 3 caracteres

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

de salida (eliminado "Tes")

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

Extracción primeros 10 caracteres

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

de salida (eliminado "somet Test")

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

Ejemplo 2 - Extracción de caracteres interiores - "es" de "Prueba"

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

salida

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

Quitando "cosa algo el"

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

salida

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

Espero que esto ayude.

Bueno, quizás esta no sea la mejor solución pero es todo lo que puedo hacer en este momento.

+0

@Kaminari - He puesto esta función en un par de pruebas, pero todavía no puede garantizar que funciona en todas las situaciones posibles. – Wh1T3h4Ck5

+0

El problema es que no quiero cortar palabras a la mitad. Necesito hacer un contenido corto y limpio de todo el contenido sin destruir html – Kaminari

2

Esto funciona perfectamente para mí:

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); 
} 
Cuestiones relacionadas