koz*_*zmo 2 java docx apache-poi docx4j xwpf
我想使用正则表达式 (java RegEx)在MS Word ( .docx ) 文档中进行替换:
\n\nExample: \n \xe2\x80\xa6, \xd1\x81 \xd0\xbe\xd0\xb4\xd0\xbd\xd0\xbe\xd0\xb9 \xd1\x81\xd1\x82\xd0\xbe\xd1\x80\xd0\xbe\xd0\xbd\xd1\x8b, \xd0\xb8 %SOME_TEXT% \xd0\xb8\xd0\xbc\xd0\xb5\xd0\xbd\xd1\x83\xd0\xb5\xd0\xbc\xd0\xbe\xd0\xb5 \xd0\xb2 \xd0\xb4\xd0\xb0\xd0\xbb\xd1\x8c\xd0\xbd\xd0\xb5\xd0\xb9\xd1\x88\xd0\xb5\xd0\xbc \xc2\xab\xd0\x97\xd0\xb0\xd0\xba\xd0\xb0\xd0\xb7\xd1\x87\xd0\xb8\xd0\xba\xc2\xbb, \xd0\xb2 \n \xd0\xbb\xd0\xb8\xd1\x86\xd0\xb5 %SOME_TEXT% \xd0\xb4\xd0\xb5\xd0\xb9\xd1\x81\xd1\x82\xd0\xb2\xd1\x83\xd1\x8e\xd1\x89\xd0\xb5\xd0\xb3\xd0\xbe \xd0\xbd\xd0\xb0 \xd0\xbe\xd1\x81\xd0\xbd\xd0\xbe\xd0\xb2\xd0\xb0\xd0\xbd\xd0\xb8\xd0\xb8 %SOME_TEXT% \xd1\x81 \xd0\xb4\xd1\x80\xd1\x83\xd0\xb3\xd0\xbe\xd0\xb9 \xd1\x81\xd1\x82\xd0\xbe\xd1\x80\xd0\xbe\xd0\xbd\xd1\x8b, \n \xd0\xb7\xd0\xb0\xd0\xba\xd0\xbb\xd1\x8e\xd1\x87\xd0\xb8\xd0\xbb\xd0\xb8 \xd0\xbd\xd0\xb0\xd1\x81\xd1\x82\xd0\xbe\xd1\x8f\xd1\x89\xd0\xb8\xd0\xb9 \xd0\x94\xd0\xbe\xd0\xb3\xd0\xbe\xd0\xb2\xd0\xbe\xd1\x80 \xd0\xbe \xd0\xbd\xd0\xb8\xd0\xb6\xd0\xb5\xd1\x81\xd0\xbb\xd0\xb5\xd0\xb4\xd1\x83\xd1\x8e\xd1\x89\xd0\xb5\xd0\xbc: \xe2\x80\xa6\nRun Code Online (Sandbox Code Playgroud)\n\n我尝试使用Apache POI - XWPF获取文本模板(如%SOME_TEXT%) 并替换文本,但不能保证替换,因为 POI 分隔运行 => 我得到类似这样的内容():System.out.println(run.getText(0))
\xe2\x80\xa6\n, \xd1\x81 \xd0\xbe\xd0\xb4\xd0\xbd\xd0\xbe\xd0\xb9 \xd1\x81\xd1\x82\xd0\xbe\xd1\x80\xd0\xbe\xd0\xbd\xd1\x8b, \xd0\xb8 \n%\nSOME_TEXT\n%\n\n\xd0\xb8\xd0\xbc\xd0\xb5\xd0\xbd\xd1\x83\xd0\xb5\xd0\xbc\xd0\xbe\xd0\xb5 \n\xd0\xb2 \xd0\xb4\xd0\xb0\xd0\xbb\xd1\x8c\xd0\xbd\xd0\xb5\xd0\xb9\xd1\x88\xd0\xb5\xd0\xbc \xc2\xab\xd0\x97\xd0\xb0\xd0\xba\xd0\xb0\xd0\xb7\xd1\x87\xd0\xb8\xd0\xba\xc2\xbb, \xd0\xb2 \xd0\xbb\xd0\xb8\xd1\x86\xd0\xb5\n\n%\nSOME\n_\nTEXT\n%\nRun Code Online (Sandbox Code Playgroud)\n\n代码示例:
\n\nFileInputStream fis = new FileInputStream(new File("document.docx"));\nXWPFDocument document = new XWPFDocument(fis);\nList<XWPFParagraph> paragraphs = document.getParagraphs();\nparagraphs.forEach(para -> {\n para.getRuns().forEach(run -> {\n String text = run.getText(0);\n if (text != null) {\n System.out.println(text);\n // text replacement process\n // run.setText(newText,0);\n }\n });\n});\nRun Code Online (Sandbox Code Playgroud)\n\n我发现了许多类似的问题(例如“ Replacing a text in Apache POI XWPF\n ”),但没有找到我的问题的答案\n(此处回答“ Seperated text line in Apache POI XWPFRun object ”提供了不方便的解决方案)。
\n\n我尝试使用docx4j和此示例 =>“ docx4j 查找和替换”,但docx4j 的工作原理类似。
\n\n\n\n\n对于 docx4j,请参阅stackoverflow.com/questions/17093781/\xe2\x80\xa6 \xe2\x80\x93 JasonPlutext
\n
我尝试使用docx4j => documentPart.variableReplace(mappings);,但不能保证替换(plutext/docx4j)。
\n\n\n您使用了VariablePrepare吗?stackoverflow.com/a/17143488/1031689 \xe2\x80\x93 JasonPlutext
\n
是的,没有结果:
\n\nWordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File("test.docx"));\nHashMap<String, String> mappings = new HashMap<>();\nVariablePrepare.prepare(wordMLPackage);//see notes\nmappings.put("SOME_TEXT", "XXXX");\nwordMLPackage.getMainDocumentPart().variableReplace(mappings);\nwordMLPackage.save(new File("out.docx"));\nRun Code Online (Sandbox Code Playgroud)\n\n输入\输出文本:
\n\nInput:\n\xe2\x80\xa6, \xd1\x81 \xd0\xbe\xd0\xb4\xd0\xbd\xd0\xbe\xd0\xb9 \xd1\x81\xd1\x82\xd0\xbe\xd1\x80\xd0\xbe\xd0\xbd\xd1\x8b, \xd0\xb8 ${SOME_TEXT} \xd0\xb8\xd0\xbc\xd0\xb5\xd0\xbd\xd1\x83\xd0\xb5\xd0\xbc\xd0\xbe\xd0\xb5 \xd0\xb2 \xd0\xb4\xd0\xb0\xd0\xbb\xd1\x8c\xd0\xbd\xd0\xb5\xd0\xb9\xd1\x88\xd0\xb5\xd0\xbc \xc2\xab\xd0\x97\xd0\xb0\xd0\xba\xd0\xb0\xd0\xb7\xd1\x87\xd0\xb8\xd0\xba\xc2\xbb ...\nOutput:\n\xe2\x80\xa6, \xd1\x81 \xd0\xbe\xd0\xb4\xd0\xbd\xd0\xbe\xd0\xb9 \xd1\x81\xd1\x82\xd0\xbe\xd1\x80\xd0\xbe\xd0\xbd\xd1\x8b, \xd0\xb8 SOME_TEXT \xd0\xb8\xd0\xbc\xd0\xb5\xd0\xbd\xd1\x83\xd0\xb5\xd0\xbc\xd0\xbe\xd0\xb5 \xd0\xb2 \xd0\xb4\xd0\xb0\xd0\xbb\xd1\x8c\xd0\xbd\xd0\xb5\xd0\xb9\xd1\x88\xd0\xb5\xd0\xbc \xc2\xab\xd0\x97\xd0\xb0\xd0\xba\xd0\xb0\xd0\xb7\xd1\x87\xd0\xb8\xd0\xba\xc2\xbb ...\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\n要查看 VariablePrepare 之后的运行情况,请打开 VariablePrepare 的 INFO 级别日志记录,或者仅
\nSystem.out.println(wordMLPackage.getMainDocumentPart().getXML())
我知道模板被分离到不同的Runs,但主题的主要问题是如何不将模板分离到不同的Runs。我使用System.out.println(wordMLPackage.getMainDocumentPart().getXML())并看到:
<w:r>\n <w:t xml:space="preserve">, \xd1\x81 \xd0\xbe\xd0\xb4\xd0\xbd\xd0\xbe\xd0\xb9 \xd1\x81\xd1\x82\xd0\xbe\xd1\x80\xd0\xbe\xd0\xbd\xd1\x8b, \xd0\xb8 </w:t>\n</w:r>\n<w:r><w:t>$</w:t></w:r>\n<w:r><w:t>{</w:t></w:r>\n<w:r>\n <w:rPr>\n <w:rFonts w:eastAsia="Times-Roman"/>\n <w:color w:val="000000" w:themeColor="text1"/>\n <w:lang w:val="en-US"/>\n </w:rPr>\n <w:t>SOME</w:t> <!-- First part of template: "SOME" -->\n</w:r>\n<w:r>\n <w:rPr>\n <w:rFonts w:eastAsia="Times-Roman"/>\n <w:color w:val="000000" w:themeColor="text1"/>\n </w:rPr>\n <w:t>_</w:t> <!-- Second part of template: "_" -->\n</w:r>\n<w:r>\n <w:rPr>\n <w:rFonts w:eastAsia="Times-Roman"/>\n <w:color w:val="000000" w:themeColor="text1"/>\n <w:lang w:val="en-US"/>\n </w:rPr>\n <w:t>TEXT</w:t> <!-- Third part of template: "TEXT" -->\n</w:r>\n<w:r>\n <w:rPr>\n <w:rFonts w:eastAsia="Times-Roman"/>\n <w:color w:val="000000" w:themeColor="text1"/>\n </w:rPr>\n <w:t>}</w:t>\n</w:r>\nRun Code Online (Sandbox Code Playgroud)\n\n,该模板位于不同的 xml 标签中,我不明白为什么......
\n\n请帮助我找到替换文本的便捷方法......
\n正如您所看到的,“使用正则表达式 (java RegEx) 在 MS Word (.docx) 文档中进行替换”的方法并不是很好,因为您永远无法确定要替换的文本是否会在一次文本运行中在一起。更好的方法是使用 Word 中的字段(合并字段或表单字段)或内容控件。
\n对于此类要求,我最喜欢的仍然是Word.
第一个优点是,即使没有文档保护,也不可能对表单字段内容的各个部分进行不同的格式化,从而将表单字段内容分成不同的运行(但请参见注释 1)。第二个优点是,由于灰色背景,表单字段在文档内容中清晰可见。另一个优点是可以应用文档保护,以便仅填写表单字段,即使在 Word 的 GUI 中也是如此。这对于保护此类合同文件免遭不必要的更改确实很有好处。
\n(注1):至少Word可以防止表单字段内容的部分格式不同,从而将表单字段内容分成不同的运行。但其他文字处理软件(Writer例如)可能不遵守此限制。
所以我会有这样的Word模板:
\n\n灰色字段是、 、和中的良好旧格式文本字段。文本字段块看起来像:WordText1Text2Text3
<xml-fragment w:rsidR="00833656" \n ...\n xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" \n ... >\n <w:rPr>\n <w:rFonts w:eastAsia="Times-Roman"/>\n <w:color w:themeColor="text1" w:val="000000"/>\n <w:lang w:val="en-US"/>\n </w:rPr>\n <w:fldChar w:fldCharType="begin">\n <w:ffData>\n <w:name w:val="Text1"/>\n <w:enabled w:val="0"/>\n <w:calcOnExit w:val="0"/>\n <w:textInput>\n <w:default w:val="<\xd0\xb2\xd0\xb2\xd0\xb5\xd0\xb4\xd0\xb8\xd1\x82\xd0\xb5 \xd0\xb7\xd0\xb0\xd0\xba\xd0\xb0\xd0\xb7\xd1\x87\xd0\xb8\xd0\xba\xd0\xb0>"/>\n </w:textInput>\n </w:ffData>\n </w:fldChar>\n </xml-fragment>\n</xml-fragment>\nRun Code Online (Sandbox Code Playgroud)\n然后是下面的代码:
\nimport java.io.FileOutputStream;\nimport java.io.FileInputStream;\n\nimport org.apache.poi.xwpf.usermodel.*;\n\nimport org.apache.xmlbeans.XmlObject;\nimport org.apache.xmlbeans.XmlCursor;\nimport org.apache.xmlbeans.SimpleValue;\nimport javax.xml.namespace.QName;\n\npublic class WordReplaceTextInFormFields {\n\n private static void replaceFormFieldText(XWPFDocument document, String ffname, String text) {\n boolean foundformfield = false;\n for (XWPFParagraph paragraph : document.getParagraphs()) {\n for (XWPFRun run : paragraph.getRuns()) {\n XmlCursor cursor = run.getCTR().newCursor();\n cursor.selectPath("declare namespace w=\'http://schemas.openxmlformats.org/wordprocessingml/2006/main\' .//w:fldChar/@w:fldCharType");\n while(cursor.hasNextSelection()) {\n cursor.toNextSelection();\n XmlObject obj = cursor.getObject();\n if ("begin".equals(((SimpleValue)obj).getStringValue())) {\n cursor.toParent();\n obj = cursor.getObject();\n obj = obj.selectPath("declare namespace w=\'http://schemas.openxmlformats.org/wordprocessingml/2006/main\' .//w:ffData/w:name/@w:val")[0];\n if (ffname.equals(((SimpleValue)obj).getStringValue())) {\n foundformfield = true;\n } else {\n foundformfield = false;\n }\n } else if ("end".equals(((SimpleValue)obj).getStringValue())) {\n if (foundformfield) return;\n foundformfield = false;\n }\n }\n if (foundformfield && run.getCTR().getTList().size() > 0) {\n run.getCTR().getTList().get(0).setStringValue(text);\n foundformfield = false;\n//System.out.println(run.getCTR());\n }\n }\n }\n }\n\n public static void main(String[] args) throws Exception {\n\n XWPFDocument document = new XWPFDocument(new FileInputStream("WordTemplate.docx"));\n\n replaceFormFieldText(document, "Text1", "\xd0\x9c\xd0\xbe\xd1\x8f \xd0\x9a\xd0\xbe\xd0\xbc\xd0\xbf\xd0\xb0\xd0\xbd\xd0\xb8\xd1\x8f");\n replaceFormFieldText(document, "Text2", "\xd0\x90\xd0\xba\xd1\x81\xd0\xb5\xd0\xbb\xd1\x8c \xd0\x94\xd0\xb6\xd0\xbe\xd0\xb0\xd1\x87\xd0\xb8\xd0\xbc\xd0\xbe\xd0\xb2\xd0\xb8\xd1\x87 \xd0\xa0\xd0\xb8\xd1\x85\xd1\x82\xd0\xb5\xd1\x80");\n replaceFormFieldText(document, "Text3", "\xd0\x94\xd0\xbe\xd0\xb2\xd0\xb5\xd1\x80\xd0\xb5\xd0\xbd\xd0\xbd\xd0\xbe\xd1\x81\xd1\x82\xd1\x8c");\n\n FileOutputStream out = new FileOutputStream("WordReplaceTextInFormFields.docx");\n document.write(out);\n out.close();\n document.close();\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n此代码需要FAQ-N10025ooxml-schemas-1.3.jar中提到的所有模式的完整 jar 。
生产:
\n\n