PHP'de iki dize arasındaki farkı vurgulayın


136

PHP'de iki dize arasındaki farkı vurgulamanın en kolay yolu nedir?

Yeni metnin yeşil ve kaldırılan metnin kırmızı olduğu Yığın Taşması düzenleme geçmişi sayfasının satırları boyunca düşünüyorum. Önceden yazılmış fonksiyonlar veya sınıflar varsa, bu ideal olacaktır.

Yanıtlar:


42

PHP Horde_Text_Diff paketini kullanabildiniz.

Ancak bu paket artık mevcut değil.


1
bağlantı artık çalışmıyor. 2011'de başka bir çözüm var mı? ;-) bu gibi bir çıkış elde etmek mümkün

3
Site gitti, ancak archive.org sitesinin bir kopyası var: web.archive.org/web/20080506155528/http://software.zuavra.net/…
R. Hill

15
Çok kötü PEAR gerektirir. PEAR bağımlılığı berbat.
Rudie

7
Yeni web sitesinden: "Güncelleme: satır içi oluşturucu artık Text_Diff PEAR paketinin yerel bir parçası. Artık burada sunulan hack'i kullanmanıza gerek yok." Şimdi Text_Diff'i kullanın.
Mat

11
GPL sadece kullanımı serbest değildir. Modülünüzü / projenizi de GPL olmaya zorlar.
Parris

76

Bir dizeyi başka bir dizeye dönüştürmek için en az sayıda (tam anlamıyla alınmayacak) düzenlemeleri hesaplamak için bir sınıf yazdım:

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

Farkın HTML sürümünü oluşturmak için statik bir işlevi vardır.

Bu bir ilk versiyon ve geliştirilmesi muhtemel, ama şu andan itibaren gayet iyi çalışıyor, bu yüzden birinin ihtiyaç duyduğum gibi kompakt bir fark üretmesi gerektiğinde oraya atıyorum.

Düzenleme: Şimdi Github'da: https://github.com/gorhill/PHP-FineDiff


3
Çok baytlı destek almak için çatalı github.com/xrstf/PHP-FineDiff adresinde deneyeceğim !
activout.se

1
@R. Hill - Benim için de güzel çalışıyor. Bu gerçekten şu an geçersiz olandan daha iyi bir cevap.
Wone the Sane

Güncelleme var mı? Dosya "Texts / Diff.php" eklenemedi ve zip içinde değil diyor.
SISYN

İnanılmaz! Örnek kodlu çevrimiçi demosu kastediyorum. Mükemmel karakter seviyesi farklılıkları. Sadece Vay canına! : Ey teşekkürler!
Filip OvertoneSinger Rydlo

2
Görünüşe göre github.com/BillyNate/PHP-FineDiff çatalı en önde geliyor ve farklı kodlamalara sahip çokbaytları destekliyor. github.com/xrstf/PHP-FineDiff 404ing @ activout.se olduğunu
Kangur

24

Güçlü bir kütüphane istiyorsanız, Text_Diff (PEAR paketi) oldukça iyi görünüyor. Bazı oldukça güzel özelliklere sahiptir.


6
PHP Inline-Diff, yukarıda belirtilen, ".. bir fark hesaplamak için PEAR Text_Diff kullanır". :)
MN

Bağlantı koptu. Paketi bulamıyorum. Bu, Wordpress'in en son sürümü tarafından kullanılan aynı Diff paketidir.
Basil Musa

24

Bu hoş bir soru , ayrıca http://paulbutler.org/archives/a-simple-diff-algorithm-in-php/

Sorunu çözmek göründüğü kadar basit değil ve sorunu çözmeden önce yaklaşık bir yıl boyunca beni rahatsız etti. Algoritmamı 18 satırlık PHP'de yazmayı başardım. Bir fark yaratmanın en etkili yolu değildir, ancak muhtemelen en kolayıdır.

Her iki dizede ortak olan en uzun kelime dizisini bularak ve alt dizelerin ortak bir kelimesi bulunmayana kadar dizenin geri kalanlarının en uzun dizilerini özyineli olarak bularak çalışır. Bu noktada, kalan yeni kelimeleri bir ekleme olarak ve kalan eski kelimeleri bir silme olarak ekler.

Kaynağı buradan indirebilirsiniz: PHP SimpleDiff ...


1
Bunu da çok faydalı buldum! Armut gibi karmaşık değil.
dgavey

Bana burada bir hata veriyor:if($matrix[$oindex][$nindex] > $maxlen){ Undefined variable: maxlen
dinamik

Tamam, bunu çözmek için bir commetn gönderdin. :) neden ilk kodda düzenlemiyorsunuz? Neyse teşekkürler +1 ... hmm iyi sen yazar değilsin
dinamik

1
2010'dan en son sürüm gibi görünüyor: github.com/paulgb/simplediff/blob/master/simplediff.php
rsk82

Aslında, basitlik için +1
Parag Tyagi

17

İki diziyi ayırmak için kullanabileceğiniz kısa bir işlev. LCS algoritmasını uygular :

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

İki dizi oluşturur:

  • değerleri dizisi: farkta göründükleri gibi bir öğe listesi.
  • maske dizisi: sayılar içerir. 0: değişmedi, -1: kaldırıldı, 1: eklendi.

Bir diziyi karakterlerle doldurursanız, satır içi farkı hesaplamak için kullanılabilir. Şimdi farklılıkları vurgulamak için tek bir adım var:

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

Örneğin.:

echo diffline('StackOverflow', 'ServerFault')

Çıktı olacak:

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

StackOerverfFaulowt

Ek Notlar:

  • Dif matrisi (m + 1) * (n + 1) elemanları gerektirir. Böylece, uzun dizileri ayırmaya çalışırsanız bellek yetersizliği ile karşılaşabilirsiniz. Bu durumda, önce daha büyük parçaları (örneğin, çizgiler), sonra içeriğini ikinci bir geçişte farklılaştırın.
  • Eşleşen öğeleri baştan ve sondan keserseniz, algoritmayı yalnızca farklı ortada çalıştırırsanız algoritma geliştirilebilir. Bir ikinci (daha şişirilmiş) sürümü de, bu değişiklikler içerir.

bu basit, etkili ve çapraz platformdur; Bu tekniği, uygun yerlerde farklı çıktılar elde etmek için çeşitli sınırlarda (satır veya kelime) patlayabilir () ile kullandım. Çok güzel bir çözüm, teşekkürler!
Kod Amca Maymun

diyorcomputeDiff is not found
ichimaru

@ichimaru Her iki işlevi de yapıştırdınız mı?
Calmarius

@ Calmarius diğer işlevi görmedi ... yemin ederim! şimdi çalışıyor teşekkürler!
ichimaru

Teşekkürler, Bu kabul edilen cevaptan farklı olduğunu bulmak için oldukça kullanışlıdır.
Karan Sharma

6

Xdiff için bir PECL uzantısı da vardır:

Özellikle:

PHP Manual'dan bir örnek:

<?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 uzantısı artık korunmuyor , görünüşe göre 2008-07-01'den beri istikrarlı bir sürüm yapılmadı, pecl.php.net/package/xdiff'e göre , daha yeni olduğu için kabul edilen cevapla öneriye gitmiştim , horde.org/libraries/Horde_Text_Diff/download
Mike Purcell

PHP XDiff için basit bir yükleme prosedürü var mı? (Debian Linux için)
Peter Krauss

@MikePurcell, aslında, hala korunmaktadır. PHP 7'yi destekleyen en son kararlı sürüm 2.0.1 2016-05-16'da yayınlandı.
user2513149

@PeterKrauss, evet, var. Şu soruya göz atın: serverfault.com/questions/362680/…
user2513149

5

Hem PEAR tabanlı hem de gösterilen daha basit alternatiflerle korkunç bir sorun yaşadım. Bu yüzden Unix diff komutunu kullanan bir çözüm (açıkçası, bir Unix sisteminde olmanız veya çalışması için çalışan bir Windows diff komutuna sahip olmanız gerekir). En sevdiğiniz geçici dizini seçin ve isterseniz kodları döndürmek için istisnaları değiştirin.

/**
 * @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 (!@file_put_contents($oldfile,$old)) {
    throw new Exception('diff failed to write temporary file: ' . 
         print_r(error_get_last(),true));
  }
  if (!@file_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);
  }
}

4

3
UTF-8 ile düzgün çalışmıyor. Her karaktere bir bayt genişliğinde davranan dizelerde dizi erişimi kullanır. Mb_split ile kolayca sabitlenebilir olmalıdır.
Gellweiler

1
İşte hızlı bir düzeltme. Sadece değiştirmek $sequence1 = $string1; $sequence2 = $string2; $end1 = strlen($string1) - 1; $end2 = strlen($string2) - 1;ile$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

Bu sınıf, computeTable işlevindeki karakter modunu kullanarak bellekte yer kalmaz.
Andy

1
Geçerli bağlantı code.iamkate.com/php/diff-implementation . Test ettim ve UTF-8'i desteklemiyor.
Kangur

3

Aradığınız bir "diff algoritması" dır. Hızlı bir google araması beni bu çözüme götürdü . Test etmedim, ama belki de ihtiyacınız olanı yapacak.


Ben sadece bu betiği test ettim ve iyi çalışıyor - diff işlemi çok hızlı bir şekilde tamamlandı (test ettiğim kısa paragrafı işlemek için yaklaşık 10 ms sürdü) ve bir satır sonu eklendiğinde tespit edebildi. Kodu olduğu gibi çalıştırmak, düzeltmek isteyebileceğiniz birkaç PHP bildirimi oluşturur, ancak farklılıkları geleneksel yan yana fark görünümünü kullanmak yerine satır içi göstermeniz gerekiyorsa bunun dışında çok iyi bir çözümdür.
Noel Whitemore


2

Ben PHP çekirdekten bu harika fonksiyonlara bakmanızı tavsiye ederim:

similar_text - İki dize arasındaki benzerliği hesapla

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

levenshtein - İki dize arasındaki Levenshtein mesafesini hesapla

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

soundex - Bir dizenin soundex anahtarını hesapla

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

metaphone - Bir dizenin metafon anahtarını hesapla

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



Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.