PHPの2つの文字列の違いを強調する


136

PHPで2つの文字列の違いを強調する最も簡単な方法は何ですか?

スタックオーバーフローの編集履歴ページに沿って考えています。新しいテキストは緑色で、削除されたテキストは赤色です。事前に記述された関数またはクラスが利用できる場合は、それが理想的です。

回答:


42

PHP Horde_Text_Diffパッケージを使用することができました。

ただし、このパッケージは使用できなくなりました。


1
リンクはもう機能しません。それは2011年に他の解決策ですか?それは、このような可能性行くGET出力;-)ですtortoisesvn.tigris.org/images/TMerge2Diff.png
Glavić

3
サイトは消えていますが、archive.orgは、サイトのコピーを持っている:web.archive.org/web/20080506155528/http://software.zuavra.net/...
R. Hillの

15
残念ながら、PEARが必要です。PEARに依存するのは大変です。
ルディー

7
新しいWebサイトから:「更新:インラインレンダラーはText_Diff PEARパッケージのネイティブ部分になりました。ここで紹介したハックを使用する必要はもうありません。」だから今すぐText_Diffを使ってください。
マット

11
GPLは無料で使用できるだけではありません。それはあなたのモジュール/プロジェクトもGPLにすることを強制します。
Parris 2013

76

ある文字列を別の文字列に変換するための編集(文字どおりに解釈されるべきではない)の最小数を計算するクラスを記述しただけです。

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

HTMLバージョンのdiffをレンダリングする静的関数があります。

これは最初のバージョンであり、改善される可能性がありますが、現時点では問題なく機能しているので、誰かが必要なようにコンパクトなdiffを効率的に生成する必要がある場合に備えて、それを捨てます。

編集:現在Githubにあります:https : //github.com/gorhill/PHP-FineDiff


3
github.com/xrstf/PHP-FineDiffでフォークを試して、マルチバイトサポートを取得します!
activout.se

1
@R。ヒル-私にとっても美しく機能します。これは、現存していないように見える現在のものより本当に良い答えです。
Wonko the Sane 2015

更新はありますか?「Texts / Diff.php」ファイルのインクルードに失敗したと表示されており、zipに含まれていません。
SISYN

すごい!サンプルコードを含むオンラインデモです。完全な文字レベルの違い。すごい!:Oありがとう!
Filip OvertoneSinger Rydlo

2
今ではgithub.com/BillyNate/PHP-FineDiffフォークが最も進んでおり、異なるエンコーディングのマルチバイトをサポートしているようです。 github.com/xrstf/PHP-FineDiffが 404ing @ activout.seある
Kangur

24

堅牢なライブラリが必要な場合は、Text_Diff(PEARパッケージ)が最適です。それはいくつかのかなりクールな機能を備えています。


6
上記のPHP Inline-Diffは、「.. PEARのText_Diffを使用してdiffを計算します」。:)
MN

リンクが壊れています。パッケージを見つけることができません。これは、Wordpressの最新バージョンで使用されているものと同じDiffパッケージです。
バジルムーサ

24

これは素晴らしいものです 。http://paulbutler.org/archives/a-simple-diff-algorithm-in-php/

問題を解決することは見かけほど簡単ではなく、問題が理解するまでに約1年間問題が発生しました。私はなんとかアルゴリズムをPHPで18行のコードで記述しました。diffを実行する最も効率的な方法ではありませんが、おそらく理解するのが最も簡単です。

これは、両方の文字列に共通する単語の最長シーケンスを見つけ、部分文字列に共通の単語がなくなるまで、残りの文字列の最長シーケンスを再帰的に見つけることによって機能します。この時点で、残りの新しい単語が挿入として、残りの古い単語が削除として追加されます。

ここからソースをダウンロードできます:PHP SimpleDiff ...


1
これもとても便利だと思いました!Pearのものほど複雑ではありません。
dgavey、2011年

それは私にここでエラーを与えます:if($matrix[$oindex][$nindex] > $maxlen){ Undefined variable: maxlen
ダイナミック

それを解決するためのコマンドを投稿しました。:)なぜ最初のコードでそれを編集しないのですか?とにかく+1 ...うーん、あなたは作者ではありません
ダイナミック

1
2010年の最新バージョンのようです:github.com/paulgb/simplediff/blob/master/simplediff.php
rsk82

実際には、+ 1で簡単に
Parag Tyagi 2014年

17

2つの配列を比較するために使用できる短い関数を次に示します。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);
}

2つの配列が生成されます。

  • 値の配列:差分に表示される要素のリスト。
  • マスク配列:数値を含みます。0:変更なし、-1:削除、1:追加。

配列に文字を入力すると、インライン差分の計算に使用できます。違いを強調するための単一のステップ:

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

例えば。:

echo diffline('StackOverflow', 'ServerFault')

出力されます:

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

SタックO曲がるfファウルわーt

その他の注意事項:

  • 差分行列には、(m + 1)*(n + 1)要素が必要です。そのため、長いシーケンスを比較しようとすると、メモリ不足エラーが発生する可能性があります。この場合、最初に大きなチャンク(例:行)を比較し、次にその内容を2回目のパスで比較します。
  • 一致する要素を最初と最後からトリミングし、異なる中間部分のみでアルゴリズムを実行すると、アルゴリズムを改善できます。後者の(より肥大化)バージョンがあまりにもこれらの変更を含んでいます。

これはシンプルで効果的で、クロスプラットフォームです。この手法をさまざまな境界(行または単語)のexplode()で使用して、必要に応じて異なる出力を取得しました。とても良い解決策、ありがとう!
アンクルコードモンキー

それは言うcomputeDiff is not found
ichimaru

@ichimaru両方の機能を貼り付けましたか?
Calmarius、

@Calmariusは他の機能を見ていません...私は誓います!そのおかげで今すぐ機能します!
ichimaru 2018年

おかげで、これは受け入れられた答えよりも差分を見つけるのに非常に便利です。
Karan Sharma

6

xdiff用のPECL拡張機能もあります。

特に:

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

1
pecl.php.net/package/xdiffによると、xdiff pecl拡張機能はもはや維持されていません。明らかに安定したリリースは2008-07-01以降行われていません。私は、それがはるかに新しいため、受け入れられた回答による提案に行き着きました、horde.org / libraries / Horde_Text_Diff / download
Mike Purcell

PHPのXDiffの簡単なインストール手順はありますか?(Debian Linuxの場合)
Peter Krauss、2014年

@MikePurcell、実際のところ、まだ維持されています。PHP 7をサポートする最新の安定バージョン2.0.1が2016-05-16にリリースされました。
user2513149 2017年

@PeterKrauss、はい、あります。:この質問を見てみましょうserverfault.com/questions/362680/...
user2513149

5

表示されたPEARベースの方法とより単純な方法の両方で私はひどい問題を抱えていました。そこで、Unixのdiffコマンドを利用するソリューションを次に示します(明らかに、Unixシステムを使用しているか、機能するWindowsのdiffコマンドが必要です)。お気に入りの一時ディレクトリーを選択し、必要に応じて例外を戻りコードに変更します。

/**
 * @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

これは私が見つけた中で最高のものです。

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

ここに画像の説明を入力してください


3
UTF-8では正しく動作しません。文字列の配列アクセスを使用して、各文字を1バイト幅として扱います。mb_splitで簡単に修正できるはずです。
Gellweiler、2015年

1
ここに簡単な修正があります。次のものと交換$sequence1 = $string1; $sequence2 = $string2; $end1 = strlen($string1) - 1; $end2 = strlen($string2) - 1;してください$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 2015年

このクラスは、関数computeTableの文字モードを使用してメモリを使い果たします。
アンディ

1
現在のリンクはcode.iamkate.com/php/diff-implementationです。私はそれをテストしましたが、UTF-8をサポートしていません。
Kangur

3

あなたが探しているのは「diffアルゴリズム」です。簡単なグーグル検索は私にこの解決策を導きました。私はそれをテストしませんでした、しかし多分それはあなたが必要とすることをするでしょう。


私はそのスクリプトをテストしましたが、うまく機能します-diff操作は非常に速く完了し(テストした短い段落を処理するのに約10msかかります)、改行が追加されたことを検出できました。コードをそのまま実行すると、修正が必要になる可能性があるいくつかのPHP通知が生成されますが、それ以外は、従来のサイドバイサイドの差分ビューを使用するのではなく、インラインで違いを表示する必要がある場合に非常に良い解決策です。
ノエルホワイトモア2017


2

PHPコアからこれらの素晴らしい関数を確認することをお勧めします。

Similar_text — 2つの文字列間の類似性を計算する

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

levenshtein — 2つの文字列間のレーベンシュタイン距離を計算する

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

soundex —文字列のsoundexキーを計算する

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

metaphone —文字列のmetaphoneキーを計算する

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



弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.