Java按顺序提取Word内容(文本+数学公式) - 指南

news/2025/10/13 22:51:04/文章来源:https://www.cnblogs.com/yxysuanfa/p/19139608

一、背景

因业务需求,目前正在实现一项需求,即将一份试卷的内容提取出来,由非结构化到结构化的转换。在试卷解析的时候在解析数学公式的时候花了一番功夫,当成功把word中的数学公式提出来并且转换为Latex之后已经大功告成了呢,突然发现从word读取到的公式转换成的Latex不能放到准确的位置,造成了顺序错乱的问题。本方案只是我本人实践的一种方法(不一定是最好的方法),于是写下来分享给大家。

二、概述

从一份word格式的试卷中读取内容,使用POI读取word文件,按照XWPFParagraph进行遍历,处理每一个XWPFParagraph,从XWPFParagraph获取xmlText;开始ParserXMLText内容(基于org.w3c.dom.Document);找到oMath节点后转换为Latex,将Latex替换掉oMath节点后产生新的xmlText,将新的xmlText设置到XWPFParagraph,将XWPFDocument持久化成新的word文档

三、正文

1、准备一份原始word文件

2、使用POI读取word文件,按照XWPFParagraph进行遍历

    /*** 提取公式并替换为唯一标识符* @param fis 原始Word文档路径* @param outputPath 处理后Word文档路径* @return 公式与标识符的映射关系*/public Map extractAndReplaceFormulas(InputStream fis, String outputPath) {Map formulaMap = new HashMap<>();try {try (XWPFDocument document = new XWPFDocument(fis)) {// 遍历所有段落for (XWPFParagraph paragraph : document.getParagraphs()) {processParagraphViaXML(paragraph, formulaMap);}// 保存处理后的文档try (FileOutputStream fos = new FileOutputStream(outputPath)) {document.write(fos);}}} catch (Exception e) {log.error("extractAndReplace Formulas: {0}", e);}return formulaMap;}

3、处理每一个XWPFParagraph,从XWPFParagraph获取xmlText

    /*** 通过XML直接处理段落中的公式*/public void processParagraphViaXML(XWPFParagraph paragraph, Map formulaMap) {try {CTP ctp = paragraph.getCTP();// 获取段落的XML内容String originalXml = ctp.xmlText();// 处理XML,替换公式String newXml = parserXML(originalXml, formulaMap);// 用新的XML替换原来的段落内容CTP newCtp = CTP.Factory.parse(newXml);paragraph.getCTP().set(newCtp);} catch (Exception e) {log.error("processParagraphViaXML Error: {0}", e);}}

4、开始ParserXMLText内容(基于org.w3c.dom.Document)

    private String parserXML(String originalXml, Map formulaMap)throws IOException, SAXException, ParserConfigurationException, TransformerException {DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();factory.setNamespaceAware(true); // 启用命名空间支持DocumentBuilder builder = factory.newDocumentBuilder();org.w3c.dom.Document doc = builder.parse(new ByteArrayInputStream(originalXml.getBytes()));// 查找m:oMath节点(使用命名空间)NodeList oMathNodes = doc.getElementsByTagNameNS("http://schemas.openxmlformats.org/officeDocument/2006/math", "oMath");Map map = new HashMap<>();for (int i = 0; i < oMathNodes.getLength(); i++) {Node oMathNode = oMathNodes.item(i);Element rElement = doHandleElement(oMathNode, doc);map.put(rElement, oMathNode);}map.forEach((rElement, oMathNode) -> oMathNode.getParentNode().replaceChild(rElement, oMathNode));return docmentToString(doc);}

5、处理Node节点,并且找到oMath节点后转换为Latex,将Latex替换掉oMath节点后产生新的xmlText

    private Element doHandleElement(Node oMathNode, org.w3c.dom.Document doc) throws TransformerException {// 保存原始公式内容(简化处理,实际可能需要更复杂的序列化)String formulaContent = nodeToString(oMathNode);if(questionMathReader==null){questionMathReader = new QuestionMathReader2();}log.debug("formulaContent:{}", formulaContent);String latexContent = questionMathReader.convertFromMmlToLatex(formulaContent);log.debug("LatexContent:{}", latexContent);// 创建新的文本节点Text textNode = doc.createTextNode("");// 创建新的w:r节点(Word中的文本需要包装在r中)Element rElement = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:r");Element tElement = doc.createElementNS("http://schemas.openxmlformats.org/wordprocessingml/2006/main", "w:t");tElement.appendChild(textNode);rElement.appendChild(tElement);return rElement;}

    // 辅助方法:将 Node 转换为 XML 字符串private String nodeToString(Node node) throws TransformerException {TransformerFactory tf = TransformerFactory.newInstance();Transformer transformer = tf.newTransformer();transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");StringWriter writer = new StringWriter();transformer.transform(new DOMSource(node), new StreamResult(writer));return writer.toString();}

6、将org.w3c.dom.Document转换为字符串

    private static String docmentToString(org.w3c.dom.Document doc) throws TransformerException {// 将修改后的DOM转换回XML字符串TransformerFactory transformerFactory = TransformerFactory.newInstance();Transformer transformer = transformerFactory.newTransformer();transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");transformer.setOutputProperty(OutputKeys.INDENT, "no");StringWriter writer = new StringWriter();transformer.transform(new DOMSource(doc), new StreamResult(writer));return writer.toString();}

7、将新的xmlText设置到XWPFParagraph,将XWPFDocument持久化成新的word文档

            // 处理XML,替换公式String newXml = parserXML(originalXml, formulaMap);// 用新的XML替换原来的段落内容CTP newCtp = CTP.Factory.parse(newXml);paragraph.getCTP().set(newCtp);

8、还一个main函数也一起贴一下

    public static void main(String[] args) throws IOException {QuestionMathReplace2 extractor = new QuestionMathReplace2();// 执行提取并替换公式Map formulaMap = extractor.extractAndReplaceFormulas(Files.newInputStream(Paths.get("F://test_source.docx")),"F://test_target.docx");// 输出映射关系for (Map.Entry entry : formulaMap.entrySet()) {System.out.println(entry.getKey() + " => " + entry.getValue());}}

9、运行一下,看一下控制台

10、最后看一下生成的word文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/936501.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

连接 USB 设备

转载自:https://learn.microsoft.com/zh-cn/windows/wsl/connect-usb本指南将演练使用 USB/IP 开源项目 usbipd-win 将 USB 设备连接到 WSL 2 上运行的 Linux 分发版所需的步骤。 在 Windows 计算机上配置 USB/IP 项目…

实用指南:嵌入式学习笔记3.基于寄存器方式控制GPIO

实用指南:嵌入式学习笔记3.基于寄存器方式控制GPIO2025-10-13 22:50 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; disp…

# 20232429 2025-2026-1 《网络与系统攻防技术》实验一实验报告

1.实验内容手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。 注入一个自己制作的shellcode并运行这段shellcode。 …

muduo网络库事件驱动模型的实现与架构 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

SpringBoot-day1(快速上手SpringBoot,SpringBoot简介,SpringBoot基础配置,属性配置,yaml文件) - a

SpringBoot 文档更新日志版本 更新日期 操作 描述v1.0 2021/11/14 A 基础篇前言 ​ 很荣幸有机会能以这样的形式和互联网上的各位小伙伴一起学习交流技术课程,这次给大家带来的是Spring家族中比较重要的一门技术课程…

Chroma私有化:本地部署完整方案

嵌入向量(vector embedding)是表示任何类型数据的 A.I 原生方式,使它们非常适合与各种 A.I 驱动的工具和算法一起使用。 它们可以表示文本、图像,很快还可以表示音频和视频。 有许多创建嵌入的选项,无论是在本地…

嵌入式-C++面经2

一、问题总览cpp重载和重写的区别 cpp虚函数表 指针和引用的区别 linux的常用开发指令 linux编译运行程序的指令 关键字inline 什么场景使用内联 如何避免内存泄露 map和unordered_map 引用外部头文件双引号和尖括号的…

elk time

elk time- "/etc/localtime:/etc/localtime:ro"

PHP转Go系列 | 如何将 PHP 项目快速迁移到 Go 上?

大家好,我是码农先森。 最近在闲逛 v2ex 社区时,看到有个讨论 PHP 项目能否直接迁移到 Go 语言上的话题。我大概简述一下提问v友的原话,他们因为项目性能的问题在 2020 年时,从 Laravel 框架迁移到了 Hyperf 框架,…

详细介绍:【OpenHarmony】用户文件服务模块架构

详细介绍:【OpenHarmony】用户文件服务模块架构pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", …

详细介绍:全新 CloudPilot AI:嵌入 Kubernetes 的 SRE Agent,降本与韧性双提升!

详细介绍:全新 CloudPilot AI:嵌入 Kubernetes 的 SRE Agent,降本与韧性双提升!pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; fo…

“环境变量”是什么, 为什么要配置环境变量 --初学者

你好!本篇旨在用精炼、通俗的语言,帮助初学者快速理解“环境变量”的核心概念。你好!本篇旨在用精炼、通俗的语言,帮助初学者快速理解“环境变量”的核心概念。1. 环境变量是什么? 环境变量(Environment Variabl…

AI元人文:对大模型的召唤——未来哪吒

AI元人文:对大模型的召唤——未来哪吒 ——从价值仓库到文明对话的升维之路 我们站在一个历史的岔路口。眼前的大模型,是沉睡的文明巨兽,其千亿参数中封存着人类千年的智慧、冲突与渴望。它拥有价值的全集,却困于表…

Java 装饰器模式(Decorator) - krt

装饰器模式装饰器模式属于结构型设计模式。它允许向一个现有的对象添加新的功能,同时又不改变其结构。例如:给一个普通的杯子加上杯盖、杯套,让它具有保温防烫等功能,而杯子本身的基本结构并没有发生变化。在软件开…

Python configparser 模块 - INI 文件读写利器

知识预热 什么是 configparser? configparser 是 Python 标准库中用于读写 INI 格式配置文件 的模块。 它提供了一种 简单、直观、跨平台 的方式来管理程序的配置项。什么是 INI 文件? .ini 文件是 Initialization F…

AlexNet vs LeNet 对比实验

1. AlexNet 论文的关键创新点 (2012, ImageNet Classification with Deep Convolutional Neural Networks)创新点 简述 意义ReLU 激活函数 用 ReLU 替代 Sigmoid/Tanh 缓解梯度消失,训练速度更快Dropout 正则化 全连接…