DIffer:一个可重用的 C# 差异工具和类库






4.50/5 (9投票s)
2005年4月30日
4分钟阅读

57348

1361
灵活的 C# 目录树比较工具。
引言
Differ 是一个全新、完全用 C# 编写的文件“diff”工具。其内部文本文件差异算法借鉴自 CodeProject 上另一个项目 DiffEngine。
Differ 之所以与众不同,有几个原因。首先,其代码组织方式使得实际的目录树扫描和 diff 算法是平台无关的。它可以用于控制台程序、GUI 应用或服务,因为它通过事件分发来传达所有结果。
Differ 的第二个主要区别在于它接受一个 XML 参数文件,该文件指定了应被忽略的文件类型和目录位置。这对于开发人员很有帮助,因为您可以指定在扫描过程中忽略生成的产品(.exe、.dll 等)。此外,参数文件指示哪些文件扩展名应被视为“文本”,这可以避免直接的文件扫描。默认的 XML 参数文件已包含,如果需要,Differ 会在内部生成一个。这些“忽略”列表可以指定为静态字符串或正则表达式。
最后,作为此方法有用性的一个示例,Differ 工具(可选)可以生成一个标准的 Windows 批处理文件,用于同步两个目录树的内容。我在批处理文件中使用了 xcopy
、del
、rmdir
和 attrib
命令。
背景
在尝试使用其他几个 diff 工具(包括 CygWin 端口)并使用 PERL 脚本过滤其结果后,我发现过程中可能会发生许多细微的错误。而且,许多类型的文件可以安全地被忽略。由于我远程维护一个网站,我需要保持我的开发树与 Web 服务器树同步,而这个项目就是我解决这个问题的答案。
要理解单个文件中的文本差异集是如何被发现的,请参考原始的 DiffEngine 文章。我使用的版本(包含在下载文件中)与发布时 DiffEngine 文章相比只有一个小小的修改(见下文)。
使用演示
将文件 DifferDemo.zip 解压到您常用路径下的一个目录中。然后键入“differ -?”可以查看选项列表。如果您键入“differ -p”,您将看到来自 XML 文件 differParams.xml 的默认“忽略”列表和文本扩展名映射的内容。格式很简单,应该很容易理解。
文件 differParams.xml 默认位于 Differ.exe 及其 DLL 所在的目录中。由于这些是 .NET 二进制文件,因此无需执行“regsvr”。
使用代码
使用 Visual Studio 2003 下载并构建 Differ。解决方案是 Differ 目录中的 Differ.sln。
您可以通过编辑 DifferMain.cs 来扩展该工具。它包含了所有主要的控制台输出、显示逻辑和错误处理。
或者,您可以使用 DifferCore
类来创建自己的 diff 工具,并具有适合您环境的任何行为。由于 DifferCore
类(以及底层的 DiffEngine 支持)不访问 Console
对象,因此它可以嵌入到 WinForms(GUI)应用程序、系统服务甚至 ASPX 页面中(当然,这取决于安全性)。如果您想这样做,请将 DifferCore
移到它自己的 DLL 项目中,并在您的应用程序中引用它。
在 DifferProject ZIP 文件中,您会找到一个名为 CodeComments 的目录。这些 HTML 页面是由 Visual Studio 2003 的自动文档生成功能生成的。如果您在浏览器中导航到该目录下的文件 Solution_Differ.HTM,您将能够查看整个项目的文档。(请注意,IE 安全设置的近期更改可能会导致页面渲染不正确。)
下载的 VS2003 .NET“解决方案”中有三个项目
- Differ。包含 Differ 工具的主要和核心模块。
- DifferenceEngine。包含来自 CodeProject 的原始文件 diff 逻辑。
- ZipParams。这个小型项目包含文件/目录“忽略”集合对象及其 XML 序列化逻辑。
简而言之,DifferCommand
类是一个命令行实用程序 Shell 对象,它创建一个 DifferCore
对象,并用所需的文件或目录名对其进行参数化。然后,DifferCommand
调用 DifferCore
的 Execute
方法。
这是 DifferCommand
对象的核心部分
//
// Create the Differ object; parameterize it and attach event listeners
//
DifferCore dcore = new DifferCore( zpb, sLeft, sRight );
// Indicate wheter we want files/dirs ignored or not
dcore.ObeyIgnored = bIgnore;
// Hook the standard events
dcore.DifferBinaryNotify += new
Differ.DifferCore.DifferBinaryNotifyEvent(differNotificationBinary);
dcore.DifferDirectoryNotify += new
Differ.DifferCore.DifferDirectoryEvent(differNotificationDirectory);
dcore.DifferTextNotify += new
Differ.DifferCore.DifferTextNotifyEvent(differNotificationText);
dcore.DifferExceptionNotify += new
Differ.DifferCore.DifferExceptionNotifyEvent(differExceptionNotify);
// If we're to show tracking info, attach to the event
if ( bTracking )
dcore.DifferTrackNotify += new
Differ.DifferCore.DifferTrackNotifyEvent(differTrackNotify);
if ( bShowIgnore )
dcore.DifferIgnoreNotify += new
Differ.DifferCore.DifferIgnoreNotifyEvent(differIgnoreNotify);
// Perform the recursive diff search
em = dcore.Execute();
调用一次 Execute
会返回一个指示目录是否匹配的信息,然后使用该信息设置返回给 Windows 命令 Shell 的值。所有其他信息和状态更改都通过 DifferCore
生成的事件进行通信。如果应用程序不需要某个事件,则应将其“取消挂钩”。
关注点
DiffEngine 的二进制“diff”算法对我来说太慢了,所以我创建了一个替代的(非常简单的)匹配例程。您可以在命令行中强制 Differ 使用原始算法。但是,DifferCommand
对象不会显示二进制文件差异,即使它接收到了通知。
我对 DiffEngine 的当前版本所做的唯一更改是将其最大文本行长度设置为 4096,并通过访问器公开该值。
Differ 项目演示了 .NET 和 C# 的以下主要元素
- XML 序列化。
- 文件 I/O、目录和属性处理。
- 事件和事件分发。
- 用于 Windows 函数的 DLL Import 声明。
- 简单的正则表达式。
- 异常处理。
历史
- 初始发布。