如何对比文本的差异 | 程序员必备小知识

4,829 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

在项目中遇到一个需求,要求对比两段文本的差异,删除的文字要添加删除线,新增的文字标红。本来想自己实现,奈何架不住工期紧迫,于是就去github上,找到这个 jsdiff 非常强大的文本对比JS库来实现,本文记录了 jsdiff 的使用过程。

安装和引入

执行命令 npm install diff --save 安装。

使用import Diff from 'diff'引入。

如何使用

通过 Diff 对比后返回的是一个数组,其数组项是个对象,对象有以下几个属性:

  • value 文本字段
  • added 为 true 时表示该字段是新增的
  • removed 为 true 时表示该字段是被删除的
  • count 文本字段的长度
const Diff = require('diff');
const a = 'This is a   very magical shoe. It costs 15 yuan.'
const b = 'this is a strong and beautiful shoe. It only costs 12 yuan.'
const result = Diff.diffChars(a, b);
console.log(result)

image.png

jsdiff中提供了很多对比文本的方法,每个方法对应各种的比较方法,每种方法对比后的结果各不相同,下面一一列出来,可以根据需求选择一个适合的方法。

  • Diff.diffChars(oldStr, newStr[, options]) 比较两个文本块,逐个字符进行比较。

    • options
      • ignoreCase:true忽略大小写差异。默认为false.
  • Diff.diffWords(oldStr, newStr[, options]) 比较两个文本块,逐字比较,忽略空格

    • options
      • ignoreCase:true忽略大小写差异。默认为false.
  • Diff.diffWordsWithSpace(oldStr, newStr[, options]) 比较两个文本块,逐字比较,空格也比较

  • Diff.diffLines(oldStr, newStr[, options]) 比较两个文本块,逐行比较。

    • options
      • ignoreWhitespace:true忽略文本头尾空格。
      • newlineIsToken:true将换行符视为单独的标记。
  • Diff.diffTrimmedLines(oldStr, newStr[, options]) 比较两个文本块,逐行比较,忽略文本头尾空格。

  • Diff.diffSentences(oldStr, newStr[, options]) 比较两个文本块,逐句比较。

展示文本差异

根据产品的需求,删除的文字要添加删除线,新增的文字标红,只要遍历对比后的结果拼成一串 DOM 元素通过appendChild添加到页面的容器的DOM节点中即可。

采用document.createDocumentFragment()创建一个DOM虚拟节点,遍历中生成的DOM元素都往这个虚拟节点中添加,遍历完成后在一次性添加到页面的容器的DOM节点中,避免页面一直重绘。

实现代码:

<template>
  <div id="display"></div>
</template>
<script>
const Diff = require('diff');
export default {
  data() {
    return {}
  },
  mounted() {
    const a = 'This is a   very magical shoe. It costs 15 yuan.'
    const b = 'This is a strong and beautiful shoe. It only costs 12 yuan.'
    const result = Diff.diffChars(a, b);
    const virtualDOM = document.createDocumentFragment();
    result.forEach((part) => {
      span = document.createElement('span');
      span.style.color = part.added ? 'red' : '#333333';;
      if (part.removed) {
        span.style['text-decoration'] = 'line-through';
      }
      span.appendChild(document.createTextNode(part.value));
      virtualDOM.appendChild(span);
    });
    document.getElementById('content').appendChild(virtualDOM);
  }
}
</script>

下面来展示一下调用各种Diff方法的效果。

  • diffChars image.png

  • diffWords

image.png

  • diffWordsWithSpace

image.png

  • diffTrimmedLines

image.png

  • diffSentences

image.png

根据需求来选择合适的Diff方法。