2008-11-26 8 views
123

¿Cuál es la forma más fácil de resaltar la diferencia entre dos cadenas en PHP?Resalta la diferencia entre dos cadenas en PHP

Estoy pensando a lo largo de las líneas de la página del historial de edición de desbordamiento de pila, donde el nuevo texto está en verde y el texto eliminado está en rojo. Si hay funciones o clases preescritas disponibles, sería ideal.

Respuesta

38

Puede usar el paquete PHP Horde_Text_Diff. Se adapta a tus necesidades, y es bastante personalizable también.

También está bajo la licencia GPL, así que ¡disfruta!

+1

el enlace ya no funciona. ¿Hay alguna otra solución ahora en 2011? ;-) es posible obtener un resultado como este http://tortoisesvn.tigris.org/images/TMerge2Diff.png –

+3

El sitio se ha ido, pero archive.org tiene una copia del sitio: http: //web.archive .org/web/20080506155528/http: //software.zuavra.net/inline-diff/ –

+11

Lástima que requiera PEAR. PEAR-dependencia chupa. – Rudie

3

Lo que está buscando es un "algoritmo de diferencias". Una búsqueda rápida en Google me llevó al this solution. No lo probé, pero tal vez hará lo que necesita.

+0

yo sólo he probado esa secuencia de comandos y funciona bien - la operación de diferenciación completa muy rápidamente (teniendo alrededor de 10 ms para procesar el párrafo corto que probé) y fue capaz de detectar cuando se agregó un salto de línea. Ejecutar el código como está genera un par de avisos de PHP que puede querer corregir, pero aparte de eso, es una muy buena solución si necesita mostrar las diferencias en línea en lugar de utilizar la vista de diferencias tradicional lado a lado. –

25

Si desea una biblioteca robusta, Text_Diff (un paquete PEAR) parece ser bastante buena. Tiene algunas características muy interesantes.

+6

PHP Inline-Diff, mencionado anteriormente, "... usa Text_Diff de PEAR para calcular un diff". :) –

+0

El enlace está roto. No puedo encontrar el paquete Este es el mismo paquete Diff utilizado por la última versión de Wordpress. –

21

Ésta es una agradable, también http://paulbutler.org/archives/a-simple-diff-algorithm-in-php/

Resolver el problema no es tan simple como parece, y el problema me molestó durante aproximadamente un año antes lo he descubierto. Logré escribir mi algoritmo en PHP, en 18 líneas de código. No es la manera más eficiente de hacer una diferencia, pero es probablemente la más fácil de entender.

Funciona al encontrar la secuencia más larga de palabras comunes a ambas cadenas, y encontrar recursivamente las secuencias más largas de los restos de la cadena hasta que las subcadenas no tengan palabras en común. En este punto, agrega las palabras nuevas restantes como una inserción y las palabras antiguas restantes como una eliminación.

Puede descargar el código fuente aquí: PHP SimpleDiff ...

+1

¡Me pareció muy útil también! No es tan complicado como las cosas de Pear. – dgavey

+0

Me da un error aquí: 'if ($ matrix [$ oindex] [$ nindex]> $ maxlen) {' 'Variable indefinida: maxlen' – dynamic

+0

Ok, has publicado un comentario para resolver eso. :) ¿por qué no lo editas en el código inicial? Gracias de todos modos +1 ... hmm, bueno, usted no es el autor – dynamic

63

acabo de escribir una clase para calcular más pequeño (que no debe tomarse literalmente) número de ediciones para transformar una cadena dentro de otra:

http://www.raymondhill.net/finediff/

Tiene una función estática para representar una versión HTML de la diferencia.

Es una primera versión, y es probable que se mejore, pero funciona muy bien a partir de ahora, así que la estoy lanzando en caso de que alguien necesite generar un compact diff de manera eficiente, como lo necesitaba.

Editar: Es ahora en Github: https://github.com/gorhill/PHP-FineDiff

+3

¡Intentaré con el tenedor en https://github.com/xrstf/PHP-FineDiff para obtener soporte multibyte! –

+1

@R. Hill - Funciona muy bien para mí también. Esta es realmente una mejor respuesta que la actual que parece estar extinta. –

+0

¿Alguna actualización? Dice que no se incluyó el archivo "Texts/Diff.php" y que no está en el zip. – SISYN

3

tuve un problema terrible con el tanto de la basada en PEAR y las alternativas más simples se muestra. Así que aquí hay una solución que aprovecha el comando diff de Unix (obviamente, tienes que estar en un sistema Unix o tener un comando de Windows diff que funcione para que funcione). Elija su directorio temporal favorito y cambie las excepciones para devolver los códigos si lo prefiere.

/** 
* @brief Find the difference between two strings, lines assumed to be separated by "\n| 
* @param $new string The new string 
* @param $old string The old string 
* @return string Human-readable output as produced by the Unix diff command, 
* or "No changes" if the strings are the same. 
* @throws Exception 
*/ 
public static function diff($new, $old) { 
    $tempdir = '/var/somewhere/tmp'; // Your favourite temporary directory 
    $oldfile = tempnam($tempdir,'OLD'); 
    $newfile = tempnam($tempdir,'NEW'); 
    if ([email protected]_put_contents($oldfile,$old)) { 
    throw new Exception('diff failed to write temporary file: ' . 
     print_r(error_get_last(),true)); 
    } 
    if ([email protected]_put_contents($newfile,$new)) { 
    throw new Exception('diff failed to write temporary file: ' . 
     print_r(error_get_last(),true)); 
    } 
    $answer = array(); 
    $cmd = "diff $newfile $oldfile"; 
    exec($cmd, $answer, $retcode); 
    unlink($newfile); 
    unlink($oldfile); 
    if ($retcode != 1) { 
    throw new Exception('diff failed with return code ' . $retcode); 
    } 
    if (empty($answer)) { 
    return 'No changes'; 
    } else { 
    return implode("\n", $answer); 
    } 
} 
6

También hay una extensión PECL para xdiff:

En particular:

Ejemplo de PHP Manual:

<?php 
$old_article = file_get_contents('./old_article.txt'); 
$new_article = $_POST['article']; 

$diff = xdiff_string_diff($old_article, $new_article, 1); 
if (is_string($diff)) { 
    echo "Differences between two articles:\n"; 
    echo $diff; 
} 
+1

xdiff pecl extensión ya no se mantiene, al parecer, una versión estable no se ha hecho desde 2008-07-01, de acuerdo con http://pecl.php.net/package/xdiff, terminé yendo con la sugerencia de aceptado respuesta, ya que es mucho más reciente, http://www.horde.org/libraries/Horde_Text_Diff/download –

+0

¿Hay un procedimiento de instalación simple para XDiff de PHP? (para Debian Linux) –

+0

@MikePurcell, como cuestión de hecho, todavía se mantiene. La última versión estable 2.0.1 compatible con PHP 7 se lanzó el 16-05-2016. – user2513149

6

Aquí es una función de resumen se puede utilizar a diferencia de dos matrices. Se implementa el algoritmo LCS:

function computeDiff($from, $to) 
{ 
    $diffValues = array(); 
    $diffMask = array(); 

    $dm = array(); 
    $n1 = count($from); 
    $n2 = count($to); 

    for ($j = -1; $j < $n2; $j++) $dm[-1][$j] = 0; 
    for ($i = -1; $i < $n1; $i++) $dm[$i][-1] = 0; 
    for ($i = 0; $i < $n1; $i++) 
    { 
     for ($j = 0; $j < $n2; $j++) 
     { 
      if ($from[$i] == $to[$j]) 
      { 
       $ad = $dm[$i - 1][$j - 1]; 
       $dm[$i][$j] = $ad + 1; 
      } 
      else 
      { 
       $a1 = $dm[$i - 1][$j]; 
       $a2 = $dm[$i][$j - 1]; 
       $dm[$i][$j] = max($a1, $a2); 
      } 
     } 
    } 

    $i = $n1 - 1; 
    $j = $n2 - 1; 
    while (($i > -1) || ($j > -1)) 
    { 
     if ($j > -1) 
     { 
      if ($dm[$i][$j - 1] == $dm[$i][$j]) 
      { 
       $diffValues[] = $to[$j]; 
       $diffMask[] = 1; 
       $j--; 
       continue;    
      } 
     } 
     if ($i > -1) 
     { 
      if ($dm[$i - 1][$j] == $dm[$i][$j]) 
      { 
       $diffValues[] = $from[$i]; 
       $diffMask[] = -1; 
       $i--; 
       continue;    
      } 
     } 
     { 
      $diffValues[] = $from[$i]; 
      $diffMask[] = 0; 
      $i--; 
      $j--; 
     } 
    }  

    $diffValues = array_reverse($diffValues); 
    $diffMask = array_reverse($diffMask); 

    return array('values' => $diffValues, 'mask' => $diffMask); 
} 

Se genera dos matrices:

  • valores matriz: una lista de elementos que aparecen en el diff.
  • matriz de máscara: contiene números. 0: sin cambios, -1: eliminado, 1: agregado.

Si completa una matriz con caracteres, se puede utilizar para calcular la diferencia en línea. Ahora un solo paso para resaltar las diferencias:

function diffline($line1, $line2) 
{ 
    $diff = computeDiff(str_split($line1), str_split($line2)); 
    $diffval = $diff['values']; 
    $diffmask = $diff['mask']; 

    $n = count($diffval); 
    $pmc = 0; 
    $result = ''; 
    for ($i = 0; $i < $n; $i++) 
    { 
     $mc = $diffmask[$i]; 
     if ($mc != $pmc) 
     { 
      switch ($pmc) 
      { 
       case -1: $result .= '</del>'; break; 
       case 1: $result .= '</ins>'; break; 
      } 
      switch ($mc) 
      { 
       case -1: $result .= '<del>'; break; 
       case 1: $result .= '<ins>'; break; 
      } 
     } 
     $result .= $diffval[$i]; 

     $pmc = $mc; 
    } 
    switch ($pmc) 
    { 
     case -1: $result .= '</del>'; break; 
     case 1: $result .= '</ins>'; break; 
    } 

    return $result; 
} 

Ej .:

salida
echo diffline('StackOverflow', 'ServerFault') 

Will:

S<del>tackO</del><ins>er</ins>ver<del>f</del><ins>Fau</ins>l<del>ow</del><ins>t</ins> 

S Tacko erver f Faul flujo t

Notas adicionales:

  • La matriz de diferencias requiere (m + 1) * (n + 1) elementos. Por lo tanto, puede ejecutar errores sin memoria si intenta diferir secuencias largas. En este caso, difiera trozos más grandes (por ejemplo, líneas) primero, luego difunda su contenido en un segundo pase.
  • El algoritmo se puede mejorar si recorta los elementos coincidentes desde el principio y el final, luego ejecuta el algoritmo solo en el medio diferente. Un latter (more bloated) version contiene estas modificaciones también.
+0

esto es simple, efectivo y multiplataforma; Utilicé esta técnica con explode() en varios límites (línea o palabra) para obtener resultados diferentes cuando corresponde. Muy buena solución, gracias! –

1

Yo recomiendo mirar estas funciones impresionantes de PHP núcleo:

similar_text - Se calcula la similitud entre dos cadenas

http://www.php.net/manual/en/function.similar-text.php

levenshtein - Calcular Levenshtein distancia entre dos cadenas

http://www.php.net/manual/en/function.levenshtein.php

soundex - Calcula la clave soundex de una cadena

http://www.php.net/manual/en/function.soundex.php

metaphone - Calcula la clave metaphone de una cadena

http://www.php.net/manual/en/function.metaphone.php

0

me encontré con esta clase PHP diff por Chris Boulton basado en Python difflib que podría ser una buena solución:

PHP Diff Lib

5

Este es el mejor que he encontrado.

http://code.stephenmorley.org/php/diff-implementation/

enter image description here

+2

No funciona correctamente con UTF-8. Utiliza acceso de matriz en cadenas, que trata a cada carácter como ancho de un byte. Debería ser difícil de reparar fácilmente con mb_split. – Gellweiler

+1

Aquí hay una solución rápida. Simplemente reemplace '$ sequence1 = $ string1; $ sequence2 = $ string2; $ end1 = strlen ($ cadena1) - 1; $ end2 = strlen ($ string2) - 1; 'con' $ sequence1 = preg_split ('// u', $ string1, -1, PREG_SPLIT_NO_EMPTY); $ sequence2 = preg_split ('// u', $ string2, -1, PREG_SPLIT_NO_EMPTY); $ end1 = count ($ sequence1) - 1; $ end2 = count ($ sequence2) - 1; ' – Gellweiler

+0

Esta clase se queda sin memoria usando el modo de caracteres en la función computeTable. –

Cuestiones relacionadas