Soroti perbedaan antara dua string di PHP

136

Apa cara termudah untuk menyoroti perbedaan antara dua string di PHP?

Saya berpikir di sepanjang baris halaman riwayat edit Stack Overflow, di mana teks baru berwarna hijau dan teks yang dihapus berwarna merah. Jika ada fungsi atau kelas yang tersedia sebelumnya, itu akan ideal.

Philip Morton
sumber

Jawaban:

43

Anda dapat menggunakan paket PHP Horde_Text_Diff.

Namun paket ini tidak lagi tersedia.

M N
sumber
1
tautannya tidak berfungsi lagi. apakah sekarang ada solusi lain di tahun 2011? ;-) apakah mungkin mendapatkan hasil seperti ini tortoisesvn.tigris.org/images/TMerge2Diff.png
Glavić
3
Situs hilang, tetapi archive.org memiliki salinan situs: web.archive.org/web/20080506155528/http://software.zuavra.net/…
R. Hill
15
Sayang sekali itu membutuhkan PEAR. Ketergantungan PEAR menyebalkan.
Rudie
7
Dari situs web baru: "Perbarui: perender sebaris sekarang merupakan bagian asli dari paket PEAR Text_Diff. Anda tidak perlu menggunakan peretasan yang disajikan di sini lagi." Jadi gunakan saja Text_Diff sekarang.
Mat
11
GPL tidak hanya gratis untuk digunakan. Ini memaksa modul / proyek Anda menjadi GPL juga.
Parris
78

Baru saja menulis kelas untuk menghitung jumlah pengeditan terkecil (tidak secara harfiah) untuk mengubah satu string menjadi string lain:

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

Ini memiliki fungsi statis untuk membuat versi HTML dari diff.

Ini adalah versi pertama, dan kemungkinan akan ditingkatkan, tetapi berfungsi dengan baik seperti sekarang, jadi saya membuangnya jika seseorang perlu menghasilkan diff kompak secara efisien, seperti yang saya butuhkan.

Edit: Sekarang ada di Github: https://github.com/gorhill/PHP-FineDiff

R. Hill
sumber
3
Saya akan mencoba garpu di github.com/xrstf/PHP-FineDiff untuk mendapatkan dukungan multibyte!
activout .se
1
@R. Hill - Bekerja dengan baik untukku juga. Ini benar-benar jawaban yang lebih baik daripada jawaban saat ini yang tampaknya sudah tidak berfungsi.
Wonko the Waras
Ada pembaruan? Dikatakan gagal menyertakan file "Texts / Diff.php" dan tidak ada di zip.
SISYN
Luar biasa! Maksud saya demo online dengan kode contoh. Perbedaan tingkat karakter yang sempurna. Cuma wow! : O Terima Kasih!
Filip OvertoneSinger Rydlo
2
Tampaknya sekarang garpu github.com/BillyNate/PHP-FineDiff adalah yang paling maju dan mendukung multibyte dengan penyandiaksaraan berbeda. github.com/xrstf/PHP-FineDiff adalah 404ing @ activout.se
Kangur
25

Ini bagus, juga http://paulbutler.org/archives/a-simple-diff-algorithm-in-php/

Memecahkan masalah tidak sesederhana kelihatannya, dan masalah itu mengganggu saya selama sekitar satu tahun sebelum saya mengetahuinya. Saya berhasil menulis algoritme saya dalam PHP, dalam 18 baris kode. Ini bukan cara yang paling efisien untuk melakukan diff, tetapi mungkin paling mudah untuk dipahami.

Ini bekerja dengan menemukan urutan kata terpanjang yang umum untuk kedua string, dan secara rekursif menemukan urutan terpanjang dari sisa string sampai substring tidak memiliki kata yang sama. Pada titik ini ia menambahkan kata-kata baru yang tersisa sebagai penyisipan dan kata-kata lama yang tersisa sebagai penghapusan.

Anda dapat mengunduh sumbernya di sini: PHP SimpleDiff ...

Orang yg lembek
sumber
1
Saya menemukan ini sangat berguna juga! Tidak serumit barang Pear.
dgavey
Ini memberi saya kesalahan di sini:if($matrix[$oindex][$nindex] > $maxlen){ Undefined variable: maxlen
dinamis
Oke, Anda memposting kometn untuk menyelesaikannya. :) mengapa Anda tidak mengeditnya di kode awal? Terima kasih +1 ... hmm baiklah Anda bukan penulisnya
dinamis
1
inilah yang tampaknya menjadi versi terbaru dari 2010: github.com/paulgb/simplediff/blob/master/simplediff.php
rsk82
Sebenarnya, +1 untuk kesederhanaan
Parag Tyagi
24

Jika Anda menginginkan pustaka yang kuat, Text_Diff (paket PEAR) tampaknya cukup bagus. Ini memiliki beberapa fitur yang cukup keren.

Wickethewok
sumber
6
PHP Inline-Diff, disebutkan di atas, "..menggunakan Text_Diff dari PEAR untuk menghitung diff". :)
MN
Tautannya rusak. Tidak dapat menemukan paketnya. Ini adalah paket Diff yang sama yang digunakan oleh versi terbaru Wordpress.
Basil Musa
18

Berikut adalah fungsi singkat yang dapat Anda gunakan untuk membedakan dua larik. Ini mengimplementasikan algoritma 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);
}

Ini menghasilkan dua array:

  • nilai array: daftar elemen seperti yang muncul di diff.
  • array topeng: berisi angka. 0: tidak berubah, -1: dihapus, 1: ditambahkan.

Jika Anda mengisi array dengan karakter, itu bisa digunakan untuk menghitung perbedaan sebaris. Sekarang hanya satu langkah untuk menyoroti perbedaannya:

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

Misalnya.:

echo diffline('StackOverflow', 'ServerFault')

Akan menghasilkan:

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

StackOerverfFaulowt

Catatan tambahan:

  • Matriks diff membutuhkan elemen (m + 1) * (n + 1). Jadi Anda dapat mengalami kesalahan memori jika Anda mencoba membedakan urutan panjang. Dalam hal ini diff potongan yang lebih besar (mis. Baris) terlebih dahulu, kemudian bedakan isinya dalam lintasan kedua.
  • Algoritme dapat ditingkatkan jika Anda memangkas elemen yang cocok dari awal dan akhir, lalu menjalankan algoritme hanya di tengah yang berbeda. Versi terakhir (lebih membengkak) berisi modifikasi ini juga.
Calmarius
sumber
ini sederhana, efektif, dan lintas platform; Saya menggunakan teknik ini dengan explode () pada berbagai batasan (baris atau kata) untuk mendapatkan keluaran yang berbeda jika sesuai. Solusi yang sangat bagus, terima kasih!
Paman Kode Monyet
dikatakancomputeDiff is not found
ichimaru
@ichimaru Sudahkah Anda menempelkan kedua fungsi?
Calmarius
@Calmarius tidak melihat fungsi lainnya ... aku bersumpah! sekarang bekerja, terima kasih!
ichimaru
Terima kasih, Yang ini cukup berguna untuk mengetahui perbedaan dari jawaban yang diterima.
Karan Sharma
6

Ada juga ekstensi PECL untuk xdiff:

Khususnya:

Contoh dari Manual PHP:

<?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;
}
Gordon
sumber
1
ekstensi pecl xdiff tidak lagi dipertahankan, tampaknya rilis stabil belum dilakukan sejak 2008-07-01, menurut pecl.php.net/package/xdiff , saya akhirnya menggunakan saran dengan jawaban yang diterima karena jauh lebih baru , horde.org/libraries/Horde_Text_Diff/download
Mike Purcell
Ada prosedur instalasi sederhana untuk XDiff PHP? (untuk Debian Linux)
Peter Krauss
@MikePurcell, nyatanya masih dipertahankan. Versi stabil terbaru 2.0.1 yang mendukung PHP 7 telah dirilis pada 2016-05-16.
user2513149
@PeterKrauss, ya, ada. Lihatlah pertanyaan ini: serverfault.com/questions/362680/…
user2513149
5

Saya mengalami masalah besar dengan alternatif berbasis PEAR dan alternatif sederhana yang ditampilkan. Jadi, inilah solusi yang memanfaatkan perintah diff Unix (jelas, Anda harus menggunakan sistem Unix atau memiliki perintah Windows diff yang berfungsi agar berfungsi). Pilih direktori sementara favorit Anda, dan ubah pengecualian untuk mengembalikan kode jika Anda mau.

/**
 * @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);
  }
}
xgretsch.dll
sumber
4

Ini yang terbaik yang pernah saya temukan.

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

masukkan deskripsi gambar di sini

Andrew
sumber
3
Tidak berfungsi dengan baik dengan UTF-8. Ini menggunakan akses array pada string, yang memperlakukan setiap karakter sebagai satu byte lebar. Harus mudah diperbaiki dengan mb_split.
Gellweiler
1
Berikut ini perbaikan cepat. Ganti saja $sequence1 = $string1; $sequence2 = $string2; $end1 = strlen($string1) - 1; $end2 = strlen($string2) - 1;dengan$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
Kelas ini kehabisan memori menggunakan mode karakter dalam fungsi computeTable.
Andrew
1
Tautan saat ini adalah code.iamkate.com/php/diff-implementation . Saya sudah mengujinya dan tidak mendukung UTF-8.
Kangur
3

Apa yang Anda cari adalah "algoritma beda". Pencarian google cepat membawa saya ke solusi ini . Saya tidak mengujinya, tapi mungkin itu akan melakukan apa yang Anda butuhkan.

Peter Bailey
sumber
Saya baru saja menguji skrip itu dan berfungsi dengan baik - operasi diff selesai dengan sangat cepat (membutuhkan waktu sekitar 10ms untuk memproses paragraf pendek yang saya uji) dan dapat mendeteksi ketika jeda baris ditambahkan. Menjalankan kode apa adanya menghasilkan beberapa pemberitahuan PHP yang mungkin ingin Anda perbaiki, tetapi selain itu, ini adalah solusi yang sangat baik jika Anda perlu menunjukkan perbedaan sebaris daripada menggunakan tampilan diff berdampingan tradisional.
Noel Whitemore
2

Saya akan merekomendasikan untuk melihat fungsi-fungsi luar biasa ini dari inti PHP:

Similar_text - Menghitung kesamaan antara dua string

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

levenshtein - Hitung jarak Levenshtein antara dua string

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

soundex - Menghitung kunci soundex dari sebuah string

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

metaphone - Hitung kunci metaphone dari sebuah string

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

Lukas Liesis
sumber
0

Saya menemukan kelas diff PHP ini oleh Chris Boulton berdasarkan Python difflib yang bisa menjadi solusi yang baik:

PHP Diff Lib

Shubhojoy Mitra
sumber