一、背景
因业务需求,目前正在实现一项需求,即将一份试卷的内容提取出来,由非结构化到结构化的转换。在试卷解析的时候在解析数学公式的时候花了一番功夫,当成功把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内容的资料请关注代码网其它相关文章!
发表评论