使用Node.js實現(xiàn)Word文檔差異比較并高亮標(biāo)注工具
引言
當(dāng)「找不同」遇上程序員的智慧
你是否經(jīng)歷過這樣的場景?
法務(wù)同事發(fā)來合同第8版修改版,卻說不清改了哪里
導(dǎo)師在論文修改稿里標(biāo)注了十幾處調(diào)整,需要逐一核對
團(tuán)隊協(xié)作文檔頻繁更新,版本差異讓人眼花繚亂
傳統(tǒng)的手動比對不僅效率低下,還容易遺漏關(guān)鍵修改。今天,我將揭秘如何用30行Node.js代碼,打造一個智能Word文檔差異比對工具,實現(xiàn):
- 自動識別文本差異
- 智能標(biāo)注修改痕跡
- 生成專業(yè)比對報告
一、安裝所需依賴
npm install docx mammoth diff fs-extra
二、代碼注釋與技術(shù)原理詳解
1、完整代碼以及注釋
/** * Word文檔差異比較工具 * 本腳本使用Node.js比較兩個Word(.docx)文件的內(nèi)容差異,并生成帶有顏色標(biāo)注的比較結(jié)果文檔 */ const fs = require('fs'); const path = require('path'); const { Document, Paragraph, TextRun, HeadingLevel, Packer } = require('docx'); const mammoth = require('mammoth'); const diff = require('diff'); /** * 比較兩個Word文件并生成差異報告 * @param {string} file1Path - 第一個Word文件路徑(原始文件) * @param {string} file2Path - 第二個Word文件路徑(修改后的文件) * @param {string} outputPath - 差異報告輸出路徑 * * 技術(shù)原理: * 1. 使用mammoth庫提取兩個Word文檔的純文本內(nèi)容 * 2. 使用diff庫進(jìn)行文本差異比較,識別添加、刪除和未更改的部分 * 3. 使用docx庫構(gòu)建新的Word文檔,用不同顏色和樣式標(biāo)注差異部分 */ async function compareWordFiles(file1Path, file2Path, outputPath) { try { // 提取兩個Word文件的文本內(nèi)容 // mammoth.extractRawText會解析docx文件的XML結(jié)構(gòu),提取所有文本內(nèi)容 const file1Content = await extractTextFromDocx(file1Path); const file2Content = await extractTextFromDocx(file2Path); // 使用diff庫比較文本差異 // diffWords函數(shù)會將文本分解為單詞級別的差異,返回一個差異對象數(shù)組 // 每個差異對象包含value(文本內(nèi)容)和added/removed標(biāo)志 const differences = diff.diffWords(file1Content, file2Content); // 使用docx庫創(chuàng)建新的Word文檔 // Document對象表示整個Word文檔,包含一個或多個sections const doc = new Document({ sections: [{ properties: {}, children: [ // 文檔標(biāo)題 new Paragraph({ text: "Word文檔差異比較結(jié)果", heading: HeadingLevel.HEADING_1, spacing: { after: 200 } // 設(shè)置段后間距(單位:twip,1/20磅) }), // 原始文件信息 new Paragraph({ text: `原文件: ${path.basename(file1Path)}`, spacing: { after: 100 } }), // 修改后文件信息 new Paragraph({ text: `新文件: ${path.basename(file2Path)}`, spacing: { after: 200 } }), // 插入差異內(nèi)容段落 ...generateDiffParagraphs(differences) ] }] }); // 將Document對象轉(zhuǎn)換為Buffer并寫入文件 // Packer.toBuffer內(nèi)部使用JSZip庫打包docx文件(本質(zhì)上是ZIP格式的XML文件集合) const buffer = await Packer.toBuffer(doc); fs.writeFileSync(outputPath, buffer); console.log(`差異比較結(jié)果已保存到: ${outputPath}`); } catch (error) { console.error('比較過程中出錯:', error); } } /** * 從Word文檔中提取純文本內(nèi)容 * @param {string} filePath - Word文件路徑 * @returns {Promise<string>} 提取的純文本內(nèi)容 * * 技術(shù)原理: * mammoth庫解析docx文件(實際是ZIP壓縮的XML文件), * 遍歷document.xml中的段落和文本節(jié)點,拼接成純文本字符串 */ async function extractTextFromDocx(filePath) { const result = await mammoth.extractRawText({ path: filePath }); return result.value; } /** * 根據(jù)差異結(jié)果生成帶有樣式標(biāo)注的段落數(shù)組 * @param {Array} differences - diff庫生成的差異數(shù)組 * @returns {Array} 包含Paragraph對象的數(shù)組 * * 技術(shù)原理: * 1. 處理diff庫輸出的差異數(shù)組,每個部分可能是added/removed/unchanged * 2. 按換行符分割文本,確保每個段落獨立 * 3. 為不同差異類型創(chuàng)建不同樣式的TextRun: * - 新增內(nèi)容: 藍(lán)色(#1800a1)、加粗 * - 刪除內(nèi)容: 紅色(#FF0000)、刪除線 * - 未更改內(nèi)容: 黑色(#000000) */ function generateDiffParagraphs(differences) { const paragraphs = []; let currentParagraph = []; // 遍歷每個差異部分 differences.forEach(part => { // 按換行符分割文本,處理多行內(nèi)容 const lines = part.value.split('\n'); lines.forEach((line, lineIndex) => { // 跳過空行 if (line.trim() === '') return; let textRun; if (part.added) { // 新增內(nèi)容樣式 textRun = new TextRun({ text: line, color: '1800a1', // 藍(lán)色表示新增 bold: true // 加粗突出顯示 }); } else if (part.removed) { // 刪除內(nèi)容樣式 textRun = new TextRun({ text: line, color: 'FF0000', // 紅色表示刪除 strike: true // 刪除線表示已移除 }); } else { // 未更改內(nèi)容樣式 textRun = new TextRun({ text: line, color: '000000' // 黑色表示未更改 }); } // 將文本段添加到當(dāng)前段落 currentParagraph.push(textRun); // 如果不是最后一行,創(chuàng)建新段落 if (lineIndex < lines.length - 1) { paragraphs.push(new Paragraph({ children: currentParagraph })); currentParagraph = []; // 重置當(dāng)前段落 } }); }); // 添加最后一個未完成的段落 if (currentParagraph.length > 0) { paragraphs.push(new Paragraph({ children: currentParagraph })); } return paragraphs; } // 使用示例 const file1 = path.join(__dirname, './word/dl_v1.docx'); const file2 = path.join(__dirname, './word/js_xg_v1.docx'); const output = path.join(__dirname, './word/comparison_result.docx'); // 執(zhí)行比較 compareWordFiles(file1, file2, output);
2、關(guān)鍵技術(shù)原理詳解
Word文檔解析:
mammoth
庫解析.docx文件(實際是ZIP壓縮包),提取其中的document.xml
文件內(nèi)容- 解析XML結(jié)構(gòu),提取所有文本節(jié)點,忽略格式、圖片等非文本元素
差異比較算法:
diff
庫使用Myers差分算法找出兩個文本序列的最長公共子序列(LCS)diffWords
方法進(jìn)行單詞級別的比較,比字符級比較更符合文檔比較的需求
Word文檔生成:
docx
庫構(gòu)建符合Office Open XML標(biāo)準(zhǔn)的Word文檔結(jié)構(gòu)- 文檔結(jié)構(gòu)包含段落(Paragraph)、文本塊(TextRun)等元素
- 顏色使用16進(jìn)制RGB格式表示(如
1800a1
表示藍(lán)色)
差異可視化:
- 新增內(nèi)容用藍(lán)色加粗顯示,便于識別添加的內(nèi)容
- 刪除內(nèi)容用紅色刪除線顯示,表示已移除的內(nèi)容
- 保留原始文本的段落結(jié)構(gòu),確??勺x性
文件處理:
- 最終生成的.docx文件實際上是包含多個XML文件的ZIP壓縮包
Packer.toBuffer
方法將內(nèi)存中的文檔結(jié)構(gòu)打包成符合標(biāo)準(zhǔn)的.docx文件
結(jié)語:讓機(jī)器做枯燥的事,讓人做有創(chuàng)意的事
通過這個項目,我們不僅實現(xiàn)了一個實用的文檔比對工具,更演示了如何:
- 組合現(xiàn)有工具解決實際問題
- 用少量代碼創(chuàng)造高價值產(chǎn)出
- 將復(fù)雜技術(shù)轉(zhuǎn)化為簡單接口
下次當(dāng)同事還在用"Ctrl+F"艱難找不同時,你可以優(yōu)雅地說:“試試我的智能比對工具?”
到此這篇關(guān)于使用Node.js實現(xiàn)Word文檔差異比較并高亮標(biāo)注工具的文章就介紹到這了,更多相關(guān)Node.js Word差異比較并標(biāo)注內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
node schedule實現(xiàn)定時任務(wù)的示例代碼
實際工作中,可能會遇到定時清除某個文件夾內(nèi)容,本文主要介紹了node schedule實現(xiàn)定時任務(wù)的示例代碼,具有一定的參考價值,感興趣的可以了解一下2024-08-08Node.js的MongoDB驅(qū)動Mongoose基本使用教程
這篇文章主要介紹了Node.js的MongoDB驅(qū)動Mongoose的基本使用教程,前端js+后端Node.js+數(shù)據(jù)庫MongoDB是當(dāng)下流行的JavaScript全棧開發(fā)方案,需要的朋友可以參考下2016-03-03