【问题标题】:Highlight the difference between two strings in PHP突出PHP中两个字符串的区别
【发布时间】:2010-09-24 04:36:47
【问题描述】:

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

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

【问题讨论】:

    标签: php string diff word-diff


    【解决方案1】:

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

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

    它有一个静态函数来呈现 HTML 版本的差异。

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

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

    【讨论】:

    • 我会在github.com/xrstf/PHP-FineDiff 尝试fork 以获得多字节支持!
    • @R。希尔 - 对我来说也很漂亮。这确实是一个比当前似乎已失效的答案更好的答案。
    • 有什么更新吗?它说未能包含文件“Texts/Diff.php”并且它不在 zip 中。
    • 太棒了!我的意思是带有示例代码的在线演示。完美的字符级差异。哇! :O 谢谢!
    • 现在看来github.com/BillyNate/PHP-FineDiff fork 是最领先的,它支持不同编码的多字节。 github.com/xrstf/PHP-FineDiff 是 404ing @activout.se
    【解决方案2】:

    您可以使用 PHP Horde_Text_Diff 包。

    但是这个包不再可用。

    【讨论】:

    • 链接失效了。 2011 年现在还有其他解决方案吗? ;-) 是否有可能得到这样的输出tortoisesvn.tigris.org/images/TMerge2Diff.png
    • 网站已消失,但 archive.org 有该网站的副本:web.archive.org/web/20080506155528/http://software.zuavra.net/…
    • 太糟糕了,它需要 PEAR。 PEAR 依赖很糟糕。
    • 来自新网站:“更新:内联渲染器现在是 Text_Diff PEAR 包的本地部分。您不再需要使用此处介绍的 hack。”所以现在就使用 Text_Diff。
    • GPL 不仅可以免费使用。它也强制您的模块/项目使用 GPL。
    【解决方案3】:

    这个也不错 http://paulbutler.org/archives/a-simple-diff-algorithm-in-php/

    解决这个问题并不像看起来那么简单,这个问题困扰了我大约一年才弄清楚。我设法用 PHP 用 18 行代码编写了我的算法。这不是进行 diff 最有效的方法,但它可能是最容易理解的。

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

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

    【讨论】:

    【解决方案4】:

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

    【讨论】:

    • PHP Inline-Diff,上面提到过,“..使用 PEAR 中的 Text_Diff 来计算差异”。 :)
    • 链接已损坏。找不到包裹。这与最新版本的 Wordpress 使用的 Diff 包相同。
    【解决方案5】:

    这是一个可以用来区分两个数组的简短函数。它实现了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 数组:出现在差异中的元素列表。
    • 掩码数组:包含数字。 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> 
    

    StackOerverfFaulowt

    补充说明:

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

    【讨论】:

    • 这个简单、有效、跨平台;我在各种边界(行或单词)上使用了这种技术和 explode() 来在适当的地方获得不同的输出。非常好的解决方案,谢谢!
    • 上面写着computeDiff is not found
    • @ichimaru 两个函数都粘贴了吗?
    • @Calmarius 没有看到其他功能...我发誓!现在可以工作了,谢谢!
    • 谢谢,这个比公认的答案很容易找出差异。
    【解决方案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;
    }
    

    【讨论】:

    【解决方案7】:

    【讨论】:

    • 不适用于 UTF-8。它对字符串使用数组访问,将每个字符视为一个字节宽。使用 mb_split 应该可以轻松修复。
    • 这是一个快速修复。只需将$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;
    • 该类在函数computeTable中使用字符模式耗尽内存。
    • 当前链接是code.iamkate.com/php/diff-implementation。我已经测试过了,它不支持 UTF-8。
    【解决方案8】:

    我在显示基于 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);
      }
    }
    

    【讨论】:

      【解决方案9】:

      您正在寻找的是“差异算法”。快速的谷歌搜索让我找到了this solution。我没有测试它,但也许它会做你需要的。

      【讨论】:

      • 我刚刚测试了该脚本,它运行良好 - 差异操作完成得非常快(处理我测试的短段落大约需要 10 毫秒)并且它能够检测到何时添加了换行符.按原样运行代码会生成一些您可能想要修复的 PHP 通知,但除此之外,如果您需要内联显示差异而不是使用传统的并排差异视图,这是一个非常好的解决方案。
      【解决方案10】:

      【讨论】:

        【解决方案11】:

        我建议从 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

        【讨论】:

          【解决方案12】:

          我尝试了一种简单的方法,其中包含两个文本框和一些颜色样式。 注意:我的差异检查器只会突出单词而不是字符的差异。

              <?php
              $valueOne = $_POST['value'] ?? "";
              $valueTwo = $_POST['valueb'] ?? "" ;
              
              $trimValueOne = trim($valueOne);
              $trimValueTwo = trim($valueTwo);
          
              $arrayValueOne = explode(" ",$trimValueOne);
              $arrayValueTwo = explode(" ",$trimValueTwo);
          
              $allDiff = array_merge(array_diff($arrayValueOne, $arrayValueTwo), array_diff($arrayValueTwo, $arrayValueOne));
              if(array_intersect($arrayValueOne,$allDiff) && array_intersect($arrayValueTwo,$allDiff)){
          
                  if(array_intersect($arrayValueOne,$allDiff)){
                      $highlightArr = array_intersect($arrayValueOne,$allDiff);
                      $highlightArrValue = array_values($highlightArr);
                      for ($i=0; $i <count($arrayValueOne) ;$i++) { 
                          for ($j=0; $j <count($highlightArrValue) ; $j++) { 
                              if($arrayValueOne[$i] == $highlightArrValue[$j]){
                                  $arrayValueOne[$i] = "<span>".$arrayValueOne[$i]."</span>";
                              }
                          }
                      }
                      $strOne = implode(" ",$arrayValueOne);
                      echo "<p class = \"one\">{$strOne}</p>";
                  }if(array_intersect($arrayValueTwo,$allDiff)){
                  $highlightArr = array_intersect($arrayValueTwo,$allDiff);
                  $highlightArrValue = array_values($highlightArr);
                  for ($i=0; $i <count($arrayValueTwo) ;$i++) { 
                      for ($j=0; $j <count($highlightArrValue) ; $j++) { 
                              if($arrayValueTwo[$i] == $highlightArrValue[$j]){
                                  $arrayValueTwo[$i] = "<span>".$arrayValueTwo[$i]."</span>";
                              }
                          }
                  }
                  $strTwo = implode(" ",$arrayValueTwo);
                  echo "<p class = \"two\">{$strTwo}</p>";
                  }
              }elseif(!(array_intersect($arrayValueOne,$allDiff) && array_intersect($arrayValueTwo,$allDiff))){
                  if($trimValueOne == $trimValueTwo){
                      echo"<p class = \"one green\">$trimValueOne</p></p>";
                      echo"<p class = \"two green\">$trimValueTwo</p></p>";
                  }
                  else{
                      echo"<p class = \"one \">$trimValueOne</p></p>";
                      echo"<p class = \"two \">$trimValueTwo</p></p>";
                  }
          
              }
          ?>
          
          
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta http-equiv="X-UA-Compatible" content="IE=edge">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <title>Document</title>
              <link rel="stylesheet" href="./style.css">
          </head>
          <body>
              <form method="post" action="">
              <textarea type="text" name="value" placeholder="enter first text"></textarea>
              <textarea type="text" name="valueb" placeholder="enter second text"></textarea>
              <input type="submit">
              </form>
          </body>
          </html>
          

          【讨论】:

            【解决方案13】:

            我遇到了 Chris Boulton 基于 Python difflib 编写的 PHP diff 类,这可能是一个很好的解决方案:

            PHP Diff Lib

            【讨论】:

              【解决方案14】:

              另一种解决方案(用于并排比较而不是统一视图):https://github.com/danmysak/side-by-side

              【讨论】:

                【解决方案15】:

                对于那些只是寻找一个非常简单的函数来在字符串 A 中而不是在字符串 B 中查找字符的人,我编写了这个快速且非常简单的函数。

                function strdiff($a,$b){
                
                    $a = str_split($a);
                    $b = str_split($b);
                
                    return array_diff($a,$b);
                
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2020-12-25
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-07-28
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多