突出显示PHP中两个字符串之间的差异

Phi*_*ton 134 php string diff word-diff

在PHP中突出显示两个字符串之间差异的最简单方法是什么?

我正在考虑Stack Overflow编辑历史记录页面,其中新文本为绿色,删除的文本为红色.如果有任何预先编写的函数或类可用,那将是理想的.

R. *_*ill 74

刚写了一个类来计算最小的(不是字面意思)编辑数,将一个字符串转换成另一个字符串:

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

它有一个静态函数来呈现diff的HTML版本.

这是第一个版本,可能会有所改进,但它现在工作得很好,所以我把它扔出去,以防有人需要有效地生成一个紧凑的差异,就像我需要的那样.

编辑:现在在Github上:https: //github.com/gorhill/PHP-FineDiff

  • 我将尝试使用https://github.com/xrstf/PHP-FineDiff上的fork来获得多字节支持! (3认同)
  • 似乎现在 https://github.com/BillyNate/PHP-FineDiff fork 是最领先的,它支持不同编码的多字节。https://github.com/xrstf/PHP-FineDiff 是 404ing @activout.se (2认同)

M.N*_*M.N 43

您可以使用PHP Horde_Text_Diff包.它适合您的需求,也可以定制.

它也是根据GPL许可的,所以尽情享受!

  • 太糟糕了它需要PEAR.梨子依赖很糟糕. (15认同)
  • GPL不仅可以免费使用.它会强制您的模块/项目也是GPL. (11认同)
  • 从新网站:"更新:内联渲染器现在是Text_Diff PEAR包的原生部分.您不需要再使用此处提供的黑客了." 所以现在就使用Text_Diff. (7认同)
  • 网站已经消失,但archive.org有一个网站的副本:http://web.archive.org/web/20080506155528/http://software.zuavra.net/inline-diff/ (3认同)

Wic*_*wok 24

如果你想要一个健壮的库,Text_Diff(一个PEAR包)看起来相当不错.它有一些非常酷的功能.

  • PHP Inline-Diff,如上所述,"..来自PEAR的Text_Diff来计算差异".:) (6认同)

小智 24

这是一个很好的,也是 http://paulbutler.org/archives/a-simple-diff-algorithm-in-php/

解决这个问题并不像看起来那么简单,在我弄清楚问题之前,这个问题困扰了我一年左右.我设法用PHP编写我的算法,用18行代码编写.它不是最有效的差异方法,但它可能是最容易理解的.

它的工作原理是找到两个字符串共有的最长的单词序列,并递归地找到字符串剩余部分的最长序列,直到子字符串没有相同的单词.此时,它将剩余的新单词添加为插入,将剩余的旧单词添加为删除.

你可以在这里下载源代码:PHP SimpleDiff ...


Cal*_*ius 16

这是一个可用于区分两个数组的简短函数.它实现了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);
}
Run Code Online (Sandbox Code Playgroud)

它生成两个数组:

  • 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;
}
Run Code Online (Sandbox Code Playgroud)

例如.:

echo diffline('StackOverflow', 'ServerFault')
Run Code Online (Sandbox Code Playgroud)

将输出:

S<del>tackO</del><ins>er</ins>ver<del>f</del><ins>Fau</ins>l<del>ow</del><ins>t</ins> 
Run Code Online (Sandbox Code Playgroud)

小号tackOerverF福勒Ť

补充说明:

  • diff矩阵需要(m + 1)*(n + 1)个元素.因此,如果您尝试区分长序列,则可能会遇到内存不足错误.在这种情况下,首先区分较大的块(例如,线),然后在第二遍中区分它们的内容.
  • 如果从开头和结尾修剪匹配元素,则可以改进算法,然后仅在不同的中间运行算法.一个后(更臃肿)版本包含这些修改过.


Gor*_*don 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;
}
Run Code Online (Sandbox Code Playgroud)


xgr*_*sch 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);
  }
}
Run Code Online (Sandbox Code Playgroud)