Java 获取 Word 文档中修订记录的实现方法

在文档协作与审阅场景中,Word 的"修订"功能是一个常用工具。启用该功能后,文档中的所有编辑操作------包括插入、删除、替换、格式更改------都会被记录为修订,并显示修改者、修改时间等信息。本文将介绍如何使用 Java 语言,借助 Spire.Doc for Java 库,从 Word 文档中提取所有的修订记录。

一、环境准备

在开始编码之前,需要在 Java 项目中引入 Spire.Doc for Java 库。

如果项目使用 Maven 管理依赖,可以在 pom.xml 中配置以下内容:

xml 复制代码
<repositories>
    <repository>
        <id>com.e-iceblue</id>
        <name>e-iceblue</name>
        <url>https://repo.e-iceblue.cn/repository/maven-public/</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>e-iceblue</groupId>
        <artifactId>spire.doc</artifactId>
        <version>14.4.9</version>
    </dependency>
</dependencies>

对于非 Maven 项目,可以手动下载 JAR 文件并将其添加到项目的 classpath 中。

二、修订获取的基本思路

Word 文档启用修订功能后,文档中的修改会以特定格式存储。获取修订信息的基本流程如下:

  1. 加载包含修订记录的 Word 文档
  2. 遍历文档中的所有节(Section)
  3. 遍历每个节中的文档对象,识别段落级别的修订
  4. 进一步遍历段落内部的文本区域,获取更细粒度的修订
  5. 提取修订的类型、作者等信息,并输出或存储

修订主要分为插入修订和删除修订两种类型,需要分别进行获取。格式更改类修订的获取方式略有不同。

三、段落与文本区域级别的修订获取

以下代码演示了如何从 Word 文档中获取段落级别和文本区域级别的插入修订与删除修订。

java 复制代码
import com.spire.doc.Document;
import com.spire.doc.DocumentObject;
import com.spire.doc.Section;
import com.spire.doc.documents.Paragraph;
import com.spire.doc.fields.TextRange;
import com.spire.doc.formatting.revisions.EditRevision;

import java.io.FileWriter;

public class GetInsertAndDeleteRevisions {
    public static void main(String[] args) throws Exception {
        // 加载包含修订记录的 Word 文档
        Document document = new Document();
        document.loadFromFile("RevisedDocument.docx");

        // 存储插入修订信息的 StringBuilder
        StringBuilder insertRevision = new StringBuilder();
        insertRevision.append("插入修订记录:\n");
        int insertIndex = 0;

        // 存储删除修订信息的 StringBuilder
        StringBuilder deleteRevision = new StringBuilder();
        deleteRevision.append("删除修订记录:\n");
        int deleteIndex = 0;

        // 遍历所有节
        for (Section section : (Iterable<Section>) document.getSections()) {
            // 遍历节中 Body 下的所有子对象
            for (DocumentObject docItem : (Iterable<DocumentObject>) section.getBody().getChildObjects()) {
                if (docItem instanceof Paragraph) {
                    Paragraph para = (Paragraph) docItem;

                    // 判断段落是否为插入修订
                    if (para.isInsertRevision()) {
                        insertIndex++;
                        insertRevision.append("索引: ").append(insertIndex).append("\n");
                        EditRevision insertRev = para.getInsertRevision();
                        insertRevision.append("类型: ").append(insertRev.getType()).append("\n");
                        insertRevision.append("作者: ").append(insertRev.getAuthor()).append("\n");
                        insertRevision.append("内容: ").append(para.getText()).append("\n\n");
                    }

                    // 判断段落是否为删除修订(整个段落被删除的情况)
                    if (para.isDeleteRevision()) {
                        deleteIndex++;
                        deleteRevision.append("索引: ").append(deleteIndex).append("\n");
                        EditRevision deleteRev = para.getDeleteRevision();
                        deleteRevision.append("类型: ").append(deleteRev.getType()).append("\n");
                        deleteRevision.append("作者: ").append(deleteRev.getAuthor()).append("\n\n");
                    }

                    // 遍历段落内部的子对象(TextRange 级别)
                    for (DocumentObject obj : (Iterable<DocumentObject>) para.getChildObjects()) {
                        if (obj instanceof TextRange) {
                            TextRange textRange = (TextRange) obj;

                            // 判断文本区域是否为插入修订
                            if (textRange.isInsertRevision()) {
                                insertIndex++;
                                insertRevision.append("索引: ").append(insertIndex).append("\n");
                                EditRevision insertRev = textRange.getInsertRevision();
                                insertRevision.append("类型: ").append(insertRev.getType()).append("\n");
                                insertRevision.append("作者: ").append(insertRev.getAuthor()).append("\n");
                                insertRevision.append("内容: ").append(textRange.getText()).append("\n\n");
                            }

                            // 判断文本区域是否为删除修订
                            if (textRange.isDeleteRevision()) {
                                deleteIndex++;
                                deleteRevision.append("索引: ").append(deleteIndex).append("\n");
                                EditRevision deleteRev = textRange.getDeleteRevision();
                                deleteRevision.append("类型: ").append(deleteRev.getType()).append("\n");
                                deleteRevision.append("作者: ").append(deleteRev.getAuthor()).append("\n");
                                deleteRevision.append("删除内容: ").append(textRange.getText()).append("\n\n");
                            }
                        }
                    }
                }
            }
        }

        // 将结果保存到文本文件
        try (FileWriter writer1 = new FileWriter("InsertRevisions.txt")) {
            writer1.write(insertRevision.toString());
        }
        try (FileWriter writer2 = new FileWriter("DeleteRevisions.txt")) {
            writer2.write(deleteRevision.toString());
        }

        System.out.println("修订记录已导出完成");
        System.out.println("插入修订数量: " + insertIndex);
        System.out.println("删除修订数量: " + deleteIndex);

        document.close();
    }
}

四、代码逻辑说明

上述代码的核心逻辑分为三个层次:

段落级别检测 :通过 Paragraph.isInsertRevision()Paragraph.isDeleteRevision() 方法判断整个段落是否为修订内容。如果是,则使用 getInsertRevision()getDeleteRevision() 获取修订对象,进而得到修订类型(getType())和作者(getAuthor())。

文本区域检测 :段落内部的文本区域也需要单独遍历,因为修订可能发生在段落内部的局部区域(而非整段)。通过 TextRange.isInsertRevision()TextRange.isDeleteRevision() 方法进行判断。

修订类型区分 :插入修订类型的枚举值为 Insertion,删除修订类型的枚举值为 Deletion

五、获取格式更改修订

对于格式更改类的修订(如字体、字号、颜色的修改),上述基础代码无法直接获取。较新版本的 API 提供了 RevisionInfoCollection 来获取更全面的修订信息:

java 复制代码
import com.spire.doc.Document;
import com.spire.doc.revision.RevisionInfo;
import com.spire.doc.revision.RevisionType;

public class GetAllRevisionsWithFormat {
    public static void main(String[] args) {
        Document document = new Document();
        document.loadFromFile("RevisedDocument.docx");

        com.spire.doc.revision.RevisionInfoCollection revisions = document.getRevisionInfos();
        
        for (RevisionInfo revision : (Iterable<RevisionInfo>) revisions) {
            System.out.println("修订类型: " + revision.getRevisionType());
            System.out.println("作者: " + revision.getAuthor());
            System.out.println("时间: " + revision.getDateTime());
            
            if (revision.getRevisionType() == RevisionType.Format_Change) {
                System.out.println("该条修订为格式更改类型");
            }
        }
        
        document.close();
    }
}

六、注意事项

  • 修订与批注的区别:修订记录的是对文档内容的实际修改(插入、删除、格式更改),而批注是附加的注释信息。两者是不同的概念,获取方式也有所不同。

  • 版本兼容性:获取格式更改修订的功能在较新版本中才得到支持。如果使用的是较早版本,格式更改可能无法被正确识别。建议查阅所使用的版本对应的 API 文档。

  • 性能考虑:对于包含大量修订的长文档,遍历全部修订可能需要一定时间。建议在需要完整修订记录时使用完整遍历,若仅需特定类型的修订,可以在遍历过程中增加筛选条件。

  • 文档格式 :上述方法主要适用于 .docx 格式的文档。对于旧版 .doc 格式,修订信息的读取可能存在兼容性问题,建议提前转换格式。

总结

通过上述示例可以看出,使用 Java 获取 Word 文档中的修订记录,主要涉及文档加载、遍历段落和文本区域、判断修订类型并提取信息等步骤。根据需求的不同,可以选择获取段落级别的修订,也可以进一步获取文本区域级别的细粒度修订。对于格式更改类的修订,则需要使用较新版本的 API 通过 RevisionInfoCollection 进行获取。这些方法可以应用于文档审阅、版本对比、修改追踪等业务场景。在实际项目开发中,建议根据文档的复杂程度和修订类型需求,选择合适的遍历粒度和 API 版本,以确保获取到的修订信息完整且准确。此外,对于需要批量处理多个文档或生成修订报告的情况,可以在现有代码基础上进一步封装,形成可复用的工具方法。

相关推荐
Dicky-_-zhang2 小时前
Redis集群模式详解与实战配置
java·jvm
你的保护色2 小时前
ensp之STP、RSTP、MSTP协议实验
java·服务器·数据库
JAVA学习通3 小时前
《大营销平台系统设计实现》 - 营销服务 第5节:抽奖前置规则过滤
java·数据库·github
斯特凡今天也很帅3 小时前
新建数据源报错No bean named ‘SqlSessionFactorykf‘ available
java·数据库·spring boot·mybatis
带刺的坐椅3 小时前
用 Solon AI 从零构建 MCP 工具服务:让 AI Agent 拥有真实世界的能力
java·ai·solon·mcp·solon-ai
TheRouter3 小时前
PromptCaching 工程实践:把LLM 调用成本砍掉80%
java·后端·spring·ai
寻道码路3 小时前
LangChain4j Java AI 应用开发实战(二):大模型参数调优实战:Temperature、TopP、MaxTokens 深度解析
java·开发语言·人工智能·aigc
Gopher_HBo3 小时前
Go pprof性能剖析
后端
IT策士3 小时前
Django 从 0 到 1 打造完整电商平台:个人中心与用户信息修改
后端·python·django