一、背景
因业务需求,目前正在实现一项需求,即将一份试卷的内容提取出来,由非结构化到结构化的转换。在试卷解析的时候在解析数学公式的时候花了一番功夫,当成功把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<string, string> extractandreplaceformulas(inputstream fis, string outputpath) {
map<string, string> 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<string, string> 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<string, string> 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<element,node> 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("<katextext value=\""+latexcontent+"\"></katextext>");
// 创建新的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<string, string> formulamap = extractor.extractandreplaceformulas(
files.newinputstream(paths.get("f://test_source.docx")),
"f://test_target.docx"
);
// 输出映射关系
for (map.entry<string, string> entry : formulamap.entryset()) {
system.out.println(entry.getkey() + " => " + entry.getvalue());
}
}9、运行一下,看一下控制台

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

以上就是java按顺序提取word内容的方法步骤(文本+数学公式)的详细内容,更多关于java按顺序提取word内容的资料请关注代码网其它相关文章!
发表评论