PHP에서 두 문자열의 차이점을 강조


136

PHP에서 두 문자열의 차이점을 강조 표시하는 가장 쉬운 방법은 무엇입니까?

새 텍스트가 녹색이고 제거 된 텍스트가 빨간색 인 스택 오버플로 편집 기록 페이지의 줄을 따라 생각하고 있습니다. 미리 작성된 함수 나 클래스가 있으면 이상적입니다.

답변:


42

PHP Horde_Text_Diff 패키지를 사용할 수있었습니다.

그러나이 패키지는 더 이상 사용할 수 없습니다.


1
링크가 더 이상 작동하지 않습니다. 2011 년에 다른 솔루션입니까? ;-) 다음과 같은 결과를 얻을 수
있습니까?

3
사이트는 사라졌지 만 archive.org에는 다음 사이트의 사본이 있습니다. web.archive.org/web/20080506155528/http://software.zuavra.net/…
R. Hill

15
너무 나쁘면 PEAR이 필요합니다. 배 의존이 빨라진다.
Rudie

7
새로운 웹 사이트에서 : "업데이트 : 인라인 렌더러는 이제 Text_Diff PEAR 패키지의 기본 요소입니다. 더 이상 여기에 제시된 핵을 사용할 필요가 없습니다." 이제 Text_Diff를 사용하십시오.
Mat 1

11
GPL은 무료로 사용할 수 없습니다. 모듈 / 프로젝트도 GPL이됩니다.
Parris

76

하나의 문자열을 다른 문자열로 변환하기 위해 가장 적은 수의 편집을 계산하기 위해 클래스를 작성했습니다.

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

diff의 HTML 버전을 렌더링하는 정적 함수가 있습니다.

그것은 첫 번째 버전이며 개선 될 가능성이 있지만 지금은 잘 작동하므로 누군가가 필요로하는 것처럼 컴팩트 한 diff를 효율적으로 생성 해야하는 경우에 대비하여 버리고 있습니다.

편집 : 그것은 지금 Github에 있습니다 : https://github.com/gorhill/PHP-FineDiff


3
멀티 바이트 지원을 받으려면 github.com/xrstf/PHP-FineDiff 에서 포크를 사용해보십시오 !
activout.se 2012

1
@아르 자형. Hill-나에게도 아름답게 작동합니다. 이것은 실제로 현재 기능이 없어 보이는 것보다 더 나은 대답입니다.
Sane Wonko

업데이트가 있습니까? "Texts / Diff.php"파일을 포함시키지 못했으며 압축 파일에 없습니다.
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 패키지입니다.
Basil Musa

24

이것은 또한 좋은 것입니다, http://paulbutler.org/archives/a-simple-diff-algorithm-in-php/

문제를 해결하는 것은 생각만큼 간단하지 않으며 문제를 파악하기 전에 약 1 년 동안 문제가 발생했습니다. 18 줄의 코드로 PHP로 알고리즘을 작성할 수있었습니다. diff를 수행하는 가장 효율적인 방법은 아니지만 아마도 이해하기가 가장 쉽습니다.

두 문자열에 공통적 인 가장 긴 단어 시퀀스를 찾고 하위 문자열에 공통된 단어가 없을 때까지 나머지 문자열의 가장 긴 시퀀스를 재귀 적으로 찾습니다. 이 시점에서 나머지 새 단어를 삽입으로, 나머지 오래된 단어를 삭제로 추가합니다.

여기에서 소스를 다운로드 할 수 있습니다 : PHP SimpleDiff ...


1
나는 이것이 매우 유용하다는 것을 알았습니다! 배 재료만큼 복잡하지는 않습니다.
dgavey

그것은 나에게 여기에 errror를 준다 :if($matrix[$oindex][$nindex] > $maxlen){ Undefined variable: maxlen
동적 인

Ok 당신은 그것을 해결하기 위해 경쟁을 게시했습니다. :) 왜 초기 코드에서 편집하지 않습니까? 어쨌든 고마워 +1 ... 흠 글쎄 당신은 저자가 아닙니다
역동적 인

1
다음은 2010 년의 최신 버전 인 것 같습니다 : github.com/paulgb/simplediff/blob/master/simplediff.php
rsk82

사실 단순함을 위해 +1
Parag Tyagi

17

다음은 두 배열을 비교하는 데 사용할 수있는 간단한 기능입니다. 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);
}

두 개의 배열을 생성합니다.

  • values ​​배열 : diff에 나타나는 요소 목록.
  • 마스크 배열 : 숫자를 포함합니다. 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> 

에스압정에버에프아야

추가 사항 :

  • diff 행렬에는 (m + 1) * (n + 1) 요소가 필요합니다. 긴 시퀀스를 비교하려고하면 메모리 부족 오류가 발생할 수 있습니다. 이 경우 먼저 더 큰 청크 (예 : 라인)를 확산시킨 다음 내용을 두 번째 패스로 확산시킵니다.
  • 일치하는 요소를 처음과 끝에서 다듬은 다음 다른 중간에서만 알고리즘을 실행하면 알고리즘을 개선 할 수 있습니다. 후자 (더 비 대한) 버전 도 이러한 수정이 포함되어 있습니다.

이것은 간단하고 효과적이며 크로스 플랫폼입니다. 이 기술을 다양한 경계 (선 또는 단어)에서 explode ()와 함께 사용하여 적절한 경우 다른 출력을 얻습니다. 아주 좋은 해결책, 감사합니다!
삼촌 코드 원숭이

그것은 말한다computeDiff is not found
ichimaru

@ichimaru 두 기능을 모두 붙여 넣었습니까?
Calmarius

@Calmarius는 다른 기능을 보지 못했습니다 ... 맹세합니다! 그것의 작동 지금 감사합니다!
ichimaru

고마워요, 이것은 받아 들인 대답보다 diff를 찾는 것이 매우 편리합니다.
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에 대한 간단한 설치 절차가 있습니까? (데비안 리눅스)
피터 크라우스

@MikePurcell은 사실 그대로 유지됩니다. PHP 7을 지원하는 최신 안정 버전 2.0.1이 2016-05-16에 릴리스되었습니다.
user2513149

@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

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

이 클래스는 함수 computeTable에서 문자 모드를 사용하여 메모리가 부족합니다.
Andy

1
현재 링크는 code.iamkate.com/php/diff-implementation 입니다. 테스트했으며 UTF-8을 지원하지 않습니다.
Kangur

3

당신이 찾고있는 것은 "diff algorithm"입니다. 빠른 Google 검색 으로이 솔루션으로 연결되었습니다 . 나는 그것을 테스트하지 않았지만 어쩌면 그것은 당신이 필요한 것을 할 것입니다.


방금 해당 스크립트를 테스트했으며 제대로 작동합니다. diff 작업이 매우 빠르게 완료되고 (테스트 한 짧은 단락을 처리하는 데 약 10ms 소요) 줄 바꿈이 추가 된 시점을 감지 할 수있었습니다. 코드를있는 그대로 실행하면 수정하려는 PHP 알림이 ​​몇 개 생성되지만 기존의 단계별 차이보기를 사용하지 않고 인라인 차이를 표시해야하는 경우에는 매우 좋은 솔루션입니다.
Noel Whitemore


2

PHP 코어 에서이 멋진 기능을 보는 것이 좋습니다.

similar_text — 두 문자열 사이의 유사성을 계산

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

levenshtein — 두 줄 사이의 Levenshtein 거리를 계산

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

soundex — 문자열의 soundex 키를 계산

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

metaphone — 문자열의 메타 폰 키를 계산합니다

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



당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.