POI解析word文件,并为特定规则的key替换值

摘要:
***通常我们在Word文档中做的标记${mark_1},在文档中会被分割成多个XWPFRun,所以我们没法使用一个XWPFRun来进行标记文本替换。在这里,*我们想到一个方法,就是使用类似于找到字符串中子串下标的方法,找到段落XWPFRun中子Run下标,记录起始和终止下标,在终止下标后insertNewRun*,然后再从终止下标往前xwpfParagraph.removeRun;到起始下标。然后遍历文档中所有的段落进行替换。

转载:https://www.aliyun.com/jiaocheng/778166.html

模板替换内容key是: ${enforcername1}

package com.jsy.test.pdf;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;

public class WordReplace {

/*
* 在Word文档中段落的最小的操作单位是XWPFRun,正常的一个段落,会被分割成多个小的XWPFRun,这些XWPFRun组合在一起就是一个完整的段落。
*
*
* 通常我们在Word文档中做的标记${mark_1},在文档中会被分割成多个XWPFRun,所以我们没法使用一个XWPFRun来进行标记文本替换。在这里,
* 我们想到一个方法,就是使用类似于找到字符串中子串下标的方法,找到段落XWPFRun中子Run下标,记录起始和终止下标,在终止下标后insertNewRun
* (int pos),然后再从终止下标往前xwpfParagraph.removeRun(i);到起始下标。
*
*
* 这个方法可以以整个段落位单位进行标记文本替换。然后遍历文档中所有的段落进行替换。 全部代码如下:
*/

/**
* 替换所有段落中的标记
*
* @param xwpfParagraphList
* @param params
*/
public static void replaceInAllParagraphs(List<XWPFParagraph> xwpfParagraphList, Map<String, String> params) {
for (XWPFParagraph paragraph : xwpfParagraphList) {
if (paragraph.getText() == null || paragraph.getText().equals(""))
continue;
for (String key : params.keySet()) {
if (paragraph.getText().contains(key)) {
System.err.println("旧值: "+key);
replaceInParagraph(paragraph, key, params.get(key));
}
}
}
}

/**
* 替换段落中的字符串
*
* @param xwpfParagraph
* @param oldString
* @param newString
*/
public static void replaceInParagraph(XWPFParagraph xwpfParagraph, String oldString, String newString) {
Map<String, Integer> pos_map = findSubRunPosInParagraph(xwpfParagraph, oldString);
System.err.println(pos_map.toString());
if (pos_map != null) {
System.out.println("start_pos:" + pos_map.get("start_pos"));
System.out.println("end_pos:" + pos_map.get("end_pos"));
List<XWPFRun> runs = xwpfParagraph.getRuns();
XWPFRun modelRun = runs.get(pos_map.get("end_pos"));
XWPFRun xwpfRun = xwpfParagraph.insertNewRun(pos_map.get("end_pos") + 1);
System.err.println(newString);
xwpfRun.setText(newString);
System.out.println("字体大小:" + modelRun.getFontSize());
if (modelRun.getFontSize() != -1)
xwpfRun.setFontSize(modelRun.getFontSize());// 默认值是五号字体,但五号字体getFontSize()时,返回-1
xwpfRun.setFontFamily(modelRun.getFontFamily());
for (int i = pos_map.get("end_pos"); i >= pos_map.get("start_pos"); i--) {
System.out.println("remove run pos in :" + i);
xwpfParagraph.removeRun(i);
}
}
}

/**
* 找到段落中子串的起始XWPFRun下标和终止XWPFRun的下标
*
* @param xwpfParagraph
* @param substring
* @return
*/
public static Map<String, Integer> findSubRunPosInParagraph(XWPFParagraph xwpfParagraph, String substring) {
List<XWPFRun> runs = xwpfParagraph.getRuns();
int start_pos = 0;
int end_pos = 0;
String subtemp = "";
for (int i = 0; i < runs.size(); i++) {
subtemp = "";
start_pos = i;
for (int j = i; j < runs.size(); j++) {
if (runs.get(j).getText(runs.get(j).getTextPosition()) == null)
continue;
subtemp += runs.get(j).getText(runs.get(j).getTextPosition());
if (subtemp.equals(substring)) {
end_pos = j;
Map<String, Integer> map = new HashMap<>();
map.put("start_pos", start_pos);
map.put("end_pos", end_pos);
return map;
}
}
}
return null;
}

// 对表格中标记文本的替换
// 有些标记做在表格单元格中,每个单元格中的内容都是一个普通的段落,所以,我们只需遍历出所有的单元格,然后遍历出每个单元格中的所有段落,再调用以上方法进行标记文本替换即可。代码如下

/**
* 替换所有的表格
*
* @param xwpfTableList
* @param params
*/
public static void replaceInTables(List<XWPFTable> xwpfTableList, Map<String, String> params) {
for (XWPFTable table : xwpfTableList) {
replaceInTable(table, params);
}
}

/**
* 替换一个表格中的所有行
*
* @param xwpfTable
* @param params
*/
public static void replaceInTable(XWPFTable xwpfTable, Map<String, String> params) {
List<XWPFTableRow> rows = xwpfTable.getRows();
replaceInRows(rows, params);
}

/**
* 替换表格中的一行
*
* @param rows
* @param params
*/
public static void replaceInRows(List<XWPFTableRow> rows, Map<String, String> params) {
for (int i = 0; i < rows.size(); i++) {
XWPFTableRow row = rows.get(i);
replaceInCells(row.getTableCells(), params);
}
}

/**
* 替换一行中所有的单元格
*
* @param xwpfTableCellList
* @param params
*/
public static void replaceInCells(List<XWPFTableCell> xwpfTableCellList, Map<String, String> params) {
for (XWPFTableCell cell : xwpfTableCellList) {
replaceInCell(cell, params);
}
}

/**
* 替换表格中每一行中的每一个单元格中的所有段落
*
* @param cell
* @param params
*/
public static void replaceInCell(XWPFTableCell cell, Map<String, String> params) {
List<XWPFParagraph> cellParagraphs = cell.getParagraphs();
replaceInAllParagraphs(cellParagraphs, params);
}

// 调用方法测试
public static void main(String[] args) throws IOException, Exception {
// TODO Auto-generated method stub
String filepathString = "C:\Users\Administrator\Desktop\现场笔录.docx";
String destpathString = "C:\Users\Administrator\Desktop\现场笔录_new.docx";
Map<String, String> map = new HashMap<String, String>();
map.put("${enforcername1}", "小白鼠");
map.put("${enforcername2}", "喵喵喵");
map.put("${drivername}", "卡特琳娜");
map.put("${driverphone}", "15112345678");

OPCPackage pack = POIXMLDocument.openPackage(filepathString);
XWPFDocument document = new XWPFDocument(pack);
/**
* 对段落中的标记进行替换
*/
List<XWPFParagraph> parasList = document.getParagraphs();
replaceInAllParagraphs(parasList, map);
/**
* 对表格中的标记进行替换
*/
List<XWPFTable> tables = document.getTables();
replaceInTables(tables, map);
FileOutputStream outStream = null;
try {
outStream = new FileOutputStream(destpathString);
document.write(outStream);
outStream.flush();
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

}

个人感觉有点慢,15KB的文件需要750ms,希望如果有大佬有好的方案可以评论区告诉我!因为我这个还要转PDF然后立即预览的,所以尽量缩减时间.

我现在的方法网页预览需要5164毫秒.

免责声明:文章转载自《POI解析word文件,并为特定规则的key替换值》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇CDHtmlDialog探索----Javascript与窗体交互Django——Session源码分析下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

kettle 连接Hadoop 遇错

kettle从windows中往hdfs中写文件 One 2016/07/19 14:14:53 - Spoon - 正在开始任务... 2016/07/19 14:14:53 - load_hdfs - 开始执行任务 2016/07/19 14:14:53 - load_hdfs - 开始项[Hadoop Copy Files] 2016/07/19...

Maven里头的pom.xml配置详解

正常的pom配置文件如下所示: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0...

apache+php+mysql配置

---------------------------------------------------- ★软件工具:(下载时注意下载相应版本,不同版本安装细节可能会有差异!!) 1>httpd-2.4.18-win64-VC14.zip下载地址:http://www.apachelounge.com/download/ 2>php-5.6.16...

Java 使用POI操作EXCEL及测试框架搭建、测试开发的一些想法

无论是UI自动化测试还是接口自动化测试都需要进行数据驱动,一般很常见的一种方式就是用excel来管理数据,那么就涉及到一些代码对EXCEL的操作,之前我们介绍过用CSV来处理EXCEL,但是它的功能还不够强大。比如接口自动化测试框架搭建的时候我们用excel来进行数据驱动,用excel来进行用例的管理和测试结果的统计,那么我们就需要对excel进行读取,写...

使用POI导出excel基础篇

最近搞了下POI导出Excel,听说很多次,却是第一次搞。 在pom.xml中引入依赖 <dependency>   <groupId>org.apache.poi</groupId>   <artifactId>poi</artifactId>   <version>3.10-FI...

Apache 性能配置优化

前言 最近在进行apache性能优化设置。在修改apache配置)文件之前需要备份原有的配置文件夹conf,这是网站架设的好习惯。以下的apache配置调优均是在red had的环境下进行的。 httpd 相关查看命令了解 查看当前安装模块mpm(多路处理器) [root@localhost ~]# httpd -l 查看httpd进程数(即各个mpm模...