
翻译:https://www.baeldung.com/java-diff-utils-intro
在现代软件开发中,跟踪和可视化文件或内容不同版本之间的变化至关重要。无论是构建版本控制系统、协作编辑器还是代码审查工具,高效地比较内容都是必不可少的。在 Java 中实现这一目标的流行方式之一是使用 Java Diff Utils(https://java-diff-utils.github.io/java-diff-utils/)。
本教程演示如何使用 Java Diff Utils 库执行各种任务,包括逐行比较文本内容、生成统一差异(unified diffs)、应用补丁以恢复或修改内容,以及构建并排差异视图。
Java Diff Utils 是一个轻量级且功能强大的库,用于计算文本数据之间的差异。它支持字符级和行级比较,并能生成统一差异输出。
它常用于版本控制系统中,并可通过补丁将某一版本的数据转换为另一版本。该工具提供了多种类和方法,简化了比较过程。
以下是 Java Diff Utils 的主要优点:
这些能力使 Java Diff Utils 成为在 Java 应用中实现可靠文本比较功能的理想选择。
在深入使用之前,我们先通过 Spring Initializr 创建一个简单的 Spring Boot Maven 项目。
虽然可以通过手动管理 JAR 和类路径的方式在纯 Java 中使用 Java Diff Utils,但使用 Spring Boot 和 Maven 可以通过 pom.xml 自动处理所有依赖关系。这种方式不仅减少了设置时间,还确保了跨环境的一致性和可移植性。Maven 会自动下载、管理并解析所有所需的构件。
首先,在 pom.xml 中添加 Java Diff Utils 依赖:
<dependency>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils</artifactId>
<version>4.12</version>
</dependency>
完成此设置后,Maven 会确保在编译和运行时都能使用正确版本的 Java Diff Utils。
现在,我们可以直接使用核心类,如 DiffUtils、Patch 和 UnifiedDiffUtils。这些类都是基于工具方法设计的,无需显式创建实例。这种设计使其可以轻松集成到服务类、控制器层或独立的 Java 组件中。
本节将逐步构建一些基础组件,学习如何在不同场景下使用 Java Diff Utils 库。我们将探索几种核心用例,并查看在 Java 应用中实现内容比较、生成差异、应用补丁以及并排查看变化的实现方式。
让我们进一步探索这些核心用例及其实现。
我们创建一个名为 TextComparatorUtil 的工具类,用于比较两个字符串列表,并生成一个表示其差异的补丁。该工具类简化了识别文本版本之间变化的过程:
class TextComparatorUtil {
public static Patch<String> compare(List<String> original, List<String> revised) {
return DiffUtils.diff(original, revised);
}
}
验证 TextComparatorUtil 是否能正确检测并报告两个字符串列表之间的变化:
@Test
void givenDifferentLines_whenCompared_thenDetectsChanges() {
var original = List.of("A", "B", "C");
var revised = List.of("A", "B", "D");
var patch = TextComparatorUtil.compare(original, revised);
assertEquals(1, patch.getDeltas().size());
assertEquals("C", patch.getDeltas().get(0).getSource().getLines().get(0));
assertEquals("D", patch.getDeltas().get(0).getTarget().getLines().get(0));
}
接下来,我们创建一个类 UnifiedDiffGeneratorUtil,用于生成两个字符串列表之间的统一差异,以标准补丁格式表示差异:
class UnifiedDiffGeneratorUtil {
public static List<String> generate(List<String> original, List<String> revised, String fileName) {
var patch = DiffUtils.diff(original, revised);
return UnifiedDiffUtils.generateUnifiedDiff(fileName, fileName + ".new", original, patch, 3);
}
}
当我们指定原始内容和修订内容以及文件名时,它会生成适用于代码审查或版本控制系统的统一差异输出。
编写一个 JUnit 测试,确保 UnifiedDiffGeneratorUtil 能在统一差异输出中正确高亮修改的行:
@Test
void givenModifiedText_whenUnifiedDiffGenerated_thenContainsExpectedChanges() {
var original = List.of("x", "y", "z");
var revised = List.of("x", "y-modified", "z");
var diff = UnifiedDiffGeneratorUtil.generate(original, revised, "test.txt");
assertTrue(diff.stream().anyMatch(line -> line.contains("-y")));
assertTrue(diff.stream().anyMatch(line -> line.contains("+y-modified")));
}
接下来,我们编写一个类 PatchUtil,用于生成并应用补丁以更新原始内容。它将原始字符串列表转换为修订版本:
class PatchUtil {
public static List<String> apply(List<String> original, List<String> revised) throws PatchFailedException {
var patch = DiffUtils.diff(original, revised);
return DiffUtils.patch(original, patch);
}
}
它首先计算差异,然后将生成的补丁应用于原始内容以完成更新。
验证 PatchUtil 是否正确应用补丁,使原始列表与修订列表一致:
@Test
void givenPatch_whenApplied_thenMatchesRevised() throws PatchFailedException {
var original = List.of("alpha", "beta", "gamma");
var revised = List.of("alpha", "beta-updated", "gamma");
var result = PatchUtil.apply(original, revised);
assertEquals(revised, result);
}
最后,我们创建一个 SideBySideViewUtil 类,提供一种可读的方式显示两个字符串列表之间的差异:
public class SideBySideViewUtil {
private static final Logger logger = Logger.getLogger(SideBySideViewUtil.class.getName());
public static void display(List<String> original, List<String> revised)
{
var patch = DiffUtils.diff(original, revised);
patch.getDeltas().forEach(delta -> {
logger.log(Level.INFO,"Change: " + delta.getType());
logger.log(Level.INFO,"Original: " + delta.getSource().getLines());
logger.log(Level.INFO,"Revised: " + delta.getTarget().getLines());
});
}
}
它会识别每个变化,并打印变化的类型以及原始版本和修订版本中的对应行。这样可以更容易地快速可视化和理解文本版本之间的修改。
虽然 Java Diff Utils 本身不直接生成 HTML 视图,但它会暴露每个差异(delta)的源行和目标行。这些数据可用于构建自定义的可视化表示,随后可转换为格式化的 HTML,用于基于 Web 的差异查看器。
以下测试验证了 SideBySideViewUtil.display() 方法在比较两个不同的字符串列表时不会抛出异常:
@Test
void givenDifferentLists_whenDisplayCalled_thenNoExceptionThrown() {
List<String> original = List.of("line1", "line2", "line3");
List<String> revised = List.of("line1", "line2-modified", "line3", "line4");
SideBySideViewUtil.display(original, revised);
}
本文探讨了 Java Diff Utils 及其各种功能。Java Diff Utils 为 Java 应用中的文本数据比较提供了灵活、开源的解决方案。从基本的逐行差异到完整的统一差异生成和补丁功能,它是构建健壮的版本控制或变更跟踪系统的基础工具。
凭借其最少的配置和高可读性的输出,Java Diff Utils 是处理版本数据、协作编辑工具或文件监控系统的开发者的必备工具。