Làm nổi bật sự khác biệt giữa hai chuỗi trong PHP


136

Cách dễ nhất để làm nổi bật sự khác biệt giữa hai chuỗi trong PHP là gì?

Tôi đang suy nghĩ dọc theo dòng của trang lịch sử chỉnh sửa Stack Overflow, trong đó văn bản mới có màu xanh lá cây và văn bản bị xóa có màu đỏ. Nếu có bất kỳ chức năng hoặc lớp viết sẵn nào, đó sẽ là lý tưởng.

Câu trả lời:


42

Bạn đã có thể sử dụng gói PHP Horde_Text_Diff.

Tuy nhiên gói này không còn nữa.


1
liên kết không hoạt động nữa. bây giờ có giải pháp nào khác trong năm 2011 không? ;-) có thể có được đầu ra như thế này tortoisesvn.tigris.org/images/TMerge2Diff.png
Glavić

3
Trang web đã biến mất, nhưng archive.org có một bản sao của trang web: web.archive.org/web/20080506155528/http://software.zuavra.net/ phỏng
R. Hill

15
Quá tệ, nó đòi hỏi PEAR. PEAR phụ thuộc hút.
Rudie

7
Từ trang web mới: "Cập nhật: trình kết xuất nội tuyến hiện là một phần nguyên gốc của gói Text_Diff PEAR. Bạn không cần phải sử dụng bản hack được trình bày ở đây nữa." Vì vậy, chỉ cần sử dụng Text_Diff ngay bây giờ.
Mat

11
GPL không chỉ miễn phí sử dụng. Nó buộc mô-đun / dự án của bạn là GPL.
Parris

76

Chỉ cần viết một lớp để tính số lượng chỉnh sửa nhỏ nhất (không được thực hiện theo nghĩa đen) để chuyển đổi một chuỗi thành một chuỗi khác:

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

Nó có chức năng tĩnh để hiển thị phiên bản HTML của diff.

Đây là phiên bản đầu tiên và có khả năng được cải thiện, nhưng hiện tại nó vẫn hoạt động tốt, vì vậy tôi sẽ ném nó ra khỏi đó trong trường hợp ai đó cần tạo ra một sự khác biệt nhỏ gọn hiệu quả, như tôi cần.

Chỉnh sửa: Bây giờ trên Github: https://github.com/gorhill/PHP-FineDiff


3
Tôi sẽ thử dùng ngã ba tại github.com/xrstf/PHP-FineDiff để nhận được hỗ trợ đa bào!
activout.se

1
@R. Hill - Công trình đẹp cho tôi quá. Đây thực sự là một câu trả lời tốt hơn so với câu trả lời hiện tại không còn tồn tại.
Wonko the Sane

Bất cứ cập nhập nào? Nó nói không bao gồm tệp "Texts / Diff.php" và nó không có trong zip.
SISYN

Kinh ngạc! Tôi có nghĩa là bản demo trực tuyến với mã ví dụ. Sự khác biệt hoàn hảo cấp char. Chỉ cần WoW! :Ô! Cảm ơn!
Filip OvertoneSinger Rydlo

2
Có vẻ như bây giờ, ngã ba github.com/BillyNate/PHP-FineDiff là ưu tiên nhất và nó hỗ trợ đa bào với các mã hóa khác nhau. github.com/xrstf/PHP-FineDiff là 404ing @ activout.se
Kangur

24

Nếu bạn muốn có một thư viện mạnh mẽ, Text_Diff (gói PEAR) có vẻ khá tốt. Nó có một số tính năng khá thú vị.


6
PHP Inline-Diff, được đề cập ở trên, "..sử dụng Text_Diff từ PEAR để tính toán một khác biệt". :)
MN

Liên kết bị hỏng. Không thể tìm thấy gói. Đây là gói Diff tương tự được sử dụng bởi phiên bản Wordpress mới nhất.
Basil Musa

24

Đây là một cái hay, cũng http://paulbutler.org/archives/a-simple-diff-alerskym-in-php/

Giải quyết vấn đề không đơn giản như vẻ ngoài của nó và vấn đề khiến tôi bận tâm khoảng một năm trước khi tôi nhận ra. Tôi quản lý để viết thuật toán của mình bằng PHP, trong 18 dòng mã. Nó không phải là cách hiệu quả nhất để làm khác, nhưng có lẽ là cách dễ hiểu nhất.

Nó hoạt động bằng cách tìm chuỗi dài nhất của các từ chung cho cả hai chuỗi và tìm đệ quy các chuỗi dài nhất của phần còn lại của chuỗi cho đến khi các chuỗi con không có từ chung. Tại thời điểm này, nó thêm các từ mới còn lại dưới dạng chèn và các từ cũ còn lại dưới dạng xóa.

Bạn có thể tải nguồn tại đây: PHP SimpleDiff ...


1
Tôi thấy điều này rất hữu ích là tốt! Không phức tạp như những thứ Pear.
dgavey

Nó cho tôi một lỗi ở đây:if($matrix[$oindex][$nindex] > $maxlen){ Undefined variable: maxlen
động

Ok bạn đã đăng một commetn để giải quyết điều đó. :) tại sao bạn không chỉnh sửa nó trong mã ban đầu? Dù sao cũng cảm ơn +1 ... hmm, bạn không phải là tác giả
động

1
Đây là phiên bản mới nhất từ ​​năm 2010: github.com/paulgb/simplingiff/blob/master/simplingiff.php
rsk82

Trên thực tế, +1 vì đơn giản
Parag Tyagi

17

Đây là một hàm ngắn bạn có thể sử dụng để tìm hai mảng. Nó thực hiện thuật toán 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);
}

Nó tạo ra hai mảng:

  • mảng giá trị: một danh sách các phần tử khi chúng xuất hiện trong diff.
  • mảng mặt nạ: chứa số. 0: không thay đổi, -1: đã xóa, 1: đã thêm.

Nếu bạn điền vào một mảng với các ký tự, nó có thể được sử dụng để tính toán sự khác biệt nội tuyến. Bây giờ chỉ là một bước duy nhất để làm nổi bật sự khác biệt:

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

Ví dụ.:

echo diffline('StackOverflow', 'ServerFault')

Sẽ xuất:

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

StackOngười tẩyfFaulcon nợt

Ghi chú bổ sung:

  • Ma trận diff yêu cầu các phần tử (m + 1) * (n + 1). Vì vậy, bạn có thể gặp phải lỗi bộ nhớ nếu bạn cố gắng để các chuỗi dài khác nhau. Trong trường hợp này, các phần khác nhau lớn hơn (ví dụ: dòng) trước, sau đó tìm nội dung của chúng trong một lượt thứ hai.
  • Thuật toán có thể được cải thiện nếu bạn cắt các phần tử phù hợp từ đầu và cuối, sau đó chỉ chạy thuật toán ở giữa khác nhau. Một phiên bản sau (cồng kềnh hơn) cũng chứa những sửa đổi này.

Đây là đơn giản, hiệu quả và đa nền tảng; Tôi đã sử dụng kỹ thuật này với explode () trên các ranh giới khác nhau (dòng hoặc từ) để có được đầu ra khác nhau khi thích hợp. Giải pháp rất hay, cảm ơn!
Chú mã khỉ

nó nóicomputeDiff is not found
ichimaru

@ichimaru Bạn đã dán cả hai chức năng?
Calmarius

@Calmarius không thấy chức năng khác ... tôi thề! nó làm việc bây giờ cảm ơn!
ichimaru

Cảm ơn, cái này khá tiện để tìm ra diff hơn câu trả lời được chấp nhận.
Karan Sharma

6

Ngoài ra còn có một phần mở rộng PECL cho xdiff:

Đặc biệt:

  • xdiff_opes_diff - Tạo sự khác biệt thống nhất của hai chuỗi

Ví dụ từ Hướng dẫn 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
Theo tiện ích pecl.php.net/package/xdiff , cuối cùng tôi đã đưa ra đề xuất bằng câu trả lời được chấp nhận vì nó mới hơn nhiều , horde.org/lologists/Horde_Text_Diff/doad
Mike Purcell

Có một quy trình cài đặt đơn giản cho XDiff của PHP? (dành cho Debian Linux)
Peter Krauss

@MikePurcell, như một vấn đề thực tế, nó vẫn được duy trì. Phiên bản ổn định mới nhất 2.0.1 hỗ trợ PHP 7 đã được phát hành vào ngày 2016-05-16.
dùng2513149

@PeterKrauss, vâng, có. Hãy xem câu hỏi này: serverfault.com/questions/362680/ từ
user2513149

5

Tôi đã gặp rắc rối khủng khiếp với cả hai lựa chọn thay thế dựa trên PEAR và đơn giản hơn được hiển thị. Vì vậy, đây là một giải pháp tận dụng lệnh Unix diff (rõ ràng, bạn phải ở trên hệ thống Unix hoặc có lệnh diff Windows hoạt động để nó hoạt động). Chọn thư mục tạm thời yêu thích của bạn và thay đổi ngoại lệ thành trả về mã nếu bạn thích.

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

Đây là cái tốt nhất tôi tìm thấy.

http://code.stephenmorley.org/php/diff-im THỰCation /

nhập mô tả hình ảnh ở đây


3
Không hoạt động đúng với UTF-8. Nó sử dụng truy cập mảng trên các chuỗi, coi mỗi ký tự là một byte rộng. Có thể dễ dàng sửa chữa khó khăn với mb_split.
Gellweiler

1
Đây là một sửa chữa nhanh chóng. Chỉ cần thay thế $sequence1 = $string1; $sequence2 = $string2; $end1 = strlen($string1) - 1; $end2 = strlen($string2) - 1;bằng$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

Lớp này hết bộ nhớ bằng chế độ ký tự trong hàm computeTable.
Andy

1
Liên kết hiện tại là code.iamkate.com/php/diff-imâyation . Tôi đã thử nghiệm và nó không hỗ trợ UTF-8.
Kangur

3

Những gì bạn đang tìm kiếm là một "thuật toán khác". Một tìm kiếm google nhanh chóng dẫn tôi đến giải pháp này . Tôi đã không kiểm tra nó, nhưng có lẽ nó sẽ làm những gì bạn cần.


Tôi vừa kiểm tra tập lệnh đó và nó hoạt động tốt - thao tác tìm khác hoàn thành rất nhanh (mất khoảng 10ms để xử lý đoạn ngắn tôi đã kiểm tra) và nó có thể phát hiện khi ngắt dòng được thêm vào. Chạy mã dưới dạng sẽ tạo ra một vài thông báo PHP mà bạn có thể muốn sửa, nhưng ngoài ra đó là một giải pháp rất tốt nếu bạn cần hiển thị các khác biệt nội tuyến thay vì sử dụng chế độ xem khác biệt truyền thống.
Noel Whitemore


2

Tôi khuyên bạn nên xem xét các chức năng tuyệt vời này từ lõi PHP:

Tương tự - Tính toán độ tương tự giữa hai chuỗi

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

levenshtein - Tính khoảng cách Levenshtein giữa hai chuỗi

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

soundex - Tính toán khóa soundex của chuỗi

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

metaphone - Tính toán khóa metaphone của chuỗi

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


0

Tôi đã bắt gặp lớp khác biệt PHP này của Chris Boulton dựa trên Python difflib có thể là một giải pháp tốt:

PHP Diff Lib


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.