标题:PHP中的文件对比扩展
PHP中的文件对比扩展
文件对比这个扩展现在用得比较少,因为大部分情况下我们都在使用一些代码管理工具,比如 Git 或者 Svn 之类的,其实它的作用就非常类似这类工具,另外还有一个非常常用的 Beyond Compare 工具也能方便地让我们能够进行文件的对比。
安装及准备工作
在 PHP 中的这个文件扩展叫做 xdiff 扩展,我们可以直接在 pecl 中下载并安装。
需要注意的是,安装这个扩展需要操作系统安装 libxdiff 工具,在文章最下方的参考链接中有这个工具的官网地址。libxdiff 无法使用默认的 yum 安装,所以需要下载之后自行安装。和其它的 Linux 工具一样,安装过程非常简单,这里就不多赘述了。
xdiff 扩展支持字符串和文件两种形式的差异对比以及一些相关的操作,这里我们以字符串的操作为主进行讲解,文件相关的操作将在最后给出全部的操作函数用法。首先,我们需要定义一些字符串以及相关的文件便于后续的操作。
$old_article = "我本无为野客,飘飘浪迹人间。
一时被命住名山。未免随机应变。
识破尘劳扰扰,何如乐取清闲。
流霞细酌咏诗篇。且与白云为伴。";
$new_article = "我本无为野客,飘飘浪迹人间。
一时被命住名山。未免随机应变。
识破尘劳扰扰,何如乐取清闲。一
流霞细酌咏诗篇。且与白云为伴。";
$new_article1 = "我本无为野客,飘飘浪迹人间。
一时被命住名山。未免随机应变。二
识破尘劳扰扰,何如乐取清闲。
流霞细酌咏诗篇。且与白云为伴。
三一四一";
file_put_contents('old_file.txt', $old_article);
file_put_contents('new_file.txt', $new_article);
file_put_contents('new_file1.txt', $new_article1);
字符串差别
$diff = xdiff_string_diff($old_article, $new_article);
var_dump($diff);
// string(273) "@@ -1,4 +1,4 @@
// 我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。
// -识破尘劳扰扰,何如乐取清闲。
// +识破尘劳扰扰,何如乐取清闲。一
// 流霞细酌咏诗篇。且与白云为伴。
// \ No newline at end of file
// "
使用 xdiff_string_diff() 函数就可以获得两段字符串中的差异信息。可以看到它的内容结构和 Git 的文件差异对比返回的内容非常相似。像用 + 、 - 号表示的那一行的差异,我们只要使用过 Git 或 Svn 就一定不会陌生。
合并字符串
var_dump(xdiff_string_merge3($old_article, $new_article, $new_article1, $error));
// string(180) "我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。
// 识破尘劳扰扰,何如乐取清闲。一
// 流霞细酌咏诗篇。且与白云为伴。"
var_dump($error); // NULL
xdiff_string_merge3() 函数用于将三个字符串合并到一起,也是类似于 Git 中的 merge 功能。不过这个函数需要三个字符串,但是通过测试我们发现只有第一个 \$new_article 和原始的 $old_article 合并成功了。第三个 $new_article1 并没有合并到最后返回的字符串中。关于这个函数的功能和实际的效果并不一致的问题并没有找到任何相关的参考资料,官方文档的介绍也非常地简单,所以如果大家如果有知道这个函数的真实具体情况的,可以留言一起讨论哦!
$error 参数是一个可选的引用参数,如果合并过程中出现任何问题它将返回错误信息。
修补数据(补丁)
var_dump(xdiff_string_patch($old_article, $diff, XDIFF_PATCH_NORMAL, $errors));
// string(180) "我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。
// 识破尘劳扰扰,何如乐取清闲。一
// 流霞细酌咏诗篇。且与白云为伴。"
var_dump($errors); // NULL
从函数的名称中的 patch 就能看出,这个 xdiff_string_patch() 是为差异字符串打补丁用的。在曾经的桌面时代,不管是操作系统还是各种游戏,都经常会更新各种补丁。这里的补丁其实和合并差异比较类似。它的第一个参数是原始的字符串,第二个参数是 xdiff_string_diff() 生成的差异数据,打补丁的结果就是返回正式的全并差异之后的字符串。
第三个参数是可选的,它还可以定义成 XDIFF_PATCH_REVERSE ,也就是反转补丁,只返回原始的数据,不返回差异合并后的结果。反过来说,使用这个参数我们可以将第一个参数设置为修改后的 $new_article ,然后反转回原始的数据,大家可以自行尝试一下。最后的参数同样是可选的引用类型的错误变量。
二进制修补数据
$patchBinary = xdiff_string_bdiff($old_article, $new_article);
var_dump($patchBinary);
// string(44) "�{�N��一
// 流霞细酌�!"
var_dump(xdiff_string_bdiff_size($patchBinary)); // int(180)
var_dump(xdiff_string_bpatch($old_article, $patchBinary));
// string(180) "我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。
// 识破尘劳扰扰,何如乐取清闲。一
// 流霞细酌咏诗篇。且与白云为伴。"
除了原文的字符串操作之外,我们还可以使用 xdiff_string_bdiff() 返回二进制的字符串差异结果。同样地,使用 xdiff_string_bpatch() 可以对这个二进制的字符串操作结果打补丁,也就是合并差异。另外在二进制操作中还有一个函数 xdiff_string_bdiff_size() 用于返回二进制差异函数所返回的结果中的字符长度。
$raPatchBinary = xdiff_string_rabdiff($old_article, $new_article1);
var_dump($raPatchBinary);
// string(46) "�{�N�X二XY
// 三一四一"
var_dump(xdiff_string_bdiff_size($raPatchBinary)); // int(193)
var_dump(xdiff_string_bpatch($old_article, $raPatchBinary));
// string(193) "我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。二
// 识破尘劳扰扰,何如乐取清闲。
// 流霞细酌咏诗篇。且与白云为伴。
// 三一四一"
最后还有一个 xdiff_string_rabdiff() ,也是返回二进制的数据差异信息的。它和 xdiff_string_bdiff() 的区别主要是使用的算法不同。
文件操作
上面我们详细地介绍了 xdiff 扩展对于字符串的操作。它同时还提供了一系列的针对文件的操作,使用这些直接操作文件的函数就真的和我们的 Git 之类的工具非常类似了。
$old_file = 'old_file.txt';
$new_file = 'new_file.txt';
$new_file1 = 'new_file1.txt';
$diff_file = 'file.diff';
$merge_file = 'merge.txt';
$patch_file = 'patch.diff';
echo "File Diff: ", PHP_EOL;
$patch = xdiff_file_diff($old_file, $new_file, $diff_file);
var_dump($patch); // bool(true)
var_dump(file_get_contents($diff_file));
// string(273) "@@ -1,4 +1,4 @@
// 我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。
// -识破尘劳扰扰,何如乐取清闲。
// +识破尘劳扰扰,何如乐取清闲。一
// 流霞细酌咏诗篇。且与白云为伴。
// \ No newline at end of file
// "
echo 'File Merge: ', PHP_EOL;
var_dump(xdiff_file_merge3($old_file, $new_file, $new_file1, $merge_file));
// string(307) "@@ -1,4 +1,5 @@
// 我本无为野客,飘飘浪迹人间。
// -一时被命住名山。未免随机应变。
// +一时被命住名山。未免随机应变。二
// 识破尘劳扰扰,何如乐取清闲。
// -流霞细酌咏诗篇。且与白云为伴。+流霞细酌咏诗篇。且与白云为伴。
// +三一四一"
var_dump(file_get_contents($merge_file));
// string(180) "我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。
// 识破尘劳扰扰,何如乐取清闲。一
// 流霞细酌咏诗篇。且与白云为伴。"
echo "File Patch: ", PHP_EOL;
var_dump(xdiff_file_patch($old_file, $diff_file, $patch_file, XDIFF_PATCH_NORMAL)); // bool(true)
var_dump(file_get_contents($patch_file));
// string(180) "我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。
// 识破尘劳扰扰,何如乐取清闲。一
// 流霞细酌咏诗篇。且与白云为伴。"
echo "File Binary Diff: ", PHP_EOL;
$patchBinary = xdiff_file_bdiff($old_file, $new_file, $diff_file);
var_dump($patchBinary); // bool(true)
var_dump(file_get_contents($diff_file));
// string(44) "�{�N��一
// 流霞细酌�!"
var_dump(xdiff_file_bdiff_size($diff_file)); // int(180)
var_dump(xdiff_file_bpatch($old_file,$patchBinary, $patch_file)); // bool(false)
var_dump(file_get_contents($patch_file));
// string(180) "我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。
// 识破尘劳扰扰,何如乐取清闲。一
// 流霞细酌咏诗篇。且与白云为伴。"
echo "File RA Binary Diff: ", PHP_EOL;
$raPatchBinary = xdiff_file_rabdiff($old_file, $new_file1, $diff_file);
var_dump($raPatchBinary); // bool(true)
var_dump(file_get_contents($diff_file));
// string(46) "�{�N�X二XY
// 三一四一"
var_dump(xdiff_file_bdiff_size($diff_file)); // int(193)
var_dump(xdiff_file_bpatch($old_file, $raPatchBinary, $patch_file)); // bool(false)
var_dump(file_get_contents($patch_file));
// string(193) "我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。二
// 识破尘劳扰扰,何如乐取清闲。
// 流霞细酌咏诗篇。且与白云为伴。
// 三一四一"
这里我们就不一一讲解了,这些函数的操作和功能与字符串操作的相关函数都是类似的,只是参数略有不同。比如它们在对比或者合并、补丁之后都会生成一个文件,所有函数的参数都是以文件为基础的。大家可以自行运行一下测试代码并参考官方文档进行学习。
总结
关于这个 xdiff 扩展其实我们使用得并不多,不过曾经看过有一套开源的使用 PHP 来做的 CMS 系统中管理前端模板页面的功能中就使用到了这一套扩展。任何工具的存在都有它的意义,或许你在为某个功能而苦恼的时候正好就看到了这篇文章,从而轻松地解决了手头上的问题也说不准,了解并有个大概的印象,在工作中才不至于摸瞎,这就是我们刷文档的意义。
测试代码:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202010/source/10.PHP中的文件对比扩展.php
参考文档:
https://www.php.net/manual/zh/book.xdiff.php
https://directory.fsf.org/wiki/LibXDiff