代码直接看结尾的3.0 最新版本,以及注意事项
项目背景:
当我们对一些word文档(该文档包含很多的标题比如 1.1 ,1.2 , 1.2.1.1, 1.2.2.3)当我们删除其中一项或者几项时,需要手动的对后续的进行补充。该功能主要是对标题进行自动的补充。
具体步骤:
导入依赖:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
</dependency>
官网网址:(觉得麻烦不可也许)
Apache Poi 官方链接 可以看官方文档,其实更方便的可以直接导入依赖后,下载源代码,直接看源码的注释也许
跑一下代码熟悉一下
首先把下面的代码复制到编译器跑一下,看看是否正常运行,顺便了解基本使用
package codeByZyc;
import org.apache.poi.xwpf.usermodel.*;
import java.io.FileInputStream;
import java.io.IOException;
public class rederWordTest {
public static void main(String[] args) throws IOException {
FileInputStream file = new FileInputStream("输入你的word文档地址");
XWPFDocument document = new XWPFDocument(file);
// 获取word中的段落,无法获取表格
System.out.println("获取到的段落");
for (XWPFParagraph paragraph : document.getParagraphs()) {
System.out.println(paragraph.getText());
}
// 这是只能获取word中的表格
System.out.println("获取到的表给内容");
for (XWPFTable table : document.getTables()) {
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
System.out.print(cell.getText() + " \t");
}
System.out.println();
}
}
document.close();
file.close();
}
}
api说明
通过上面的代码,我们可以知道poi是用过XWPFDocument这个类去获取的word内容。 下面从段落和表格两部分进行代码说明
段落api说明
对于word中的段落他的操作如下:
XWPFDocument(最大的模块).getParagraphs->Paragraph(负责每一个段落).getRuns->Run(这是最小处理的元素)
下面是进行调式的图片,配合图片更好理解:
XWPFDocument:就是最大的那个模块 信息很大
Paragraphs:这是所有的段落集合
Paragraph:存放的就是每一段了 里面的runs 是按照格式进行分割的
Runs run的集合
具体看下面
Run(最基础的元素)
这是最重要的那个元素,他是构成所有段落和表格的最小单位。
一个段落他是如何划分成几个run的?
他是按照每个字的前后 是否同一个格式(字体,加粗否,大小等)必须完全一样才能分到一个run里面。具体分割还得调式看
下图是一些run的切割:
这个就是特殊的符号会被划开
这个更离谱 注意 1.3.1后面的空格没有 ,这个的空格是被划开的。是因为空格的格式和标题不一样
段落代码(直接看结尾的整合代码,写得更详细注释更全面):里面有注释 应该算比较清楚了 有问题可以下面评论
文件路径自己填写一下
package codeByZyc;
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 java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class word {
// 匹配数字开头,小数点隔开 空格后接内容
static Pattern headerPattern = Pattern.compile("^(\\d+(?:\\.\\d+)*)(\\s+.*)");
//用于计算层级
static ArrayList<Integer> headerlist= new ArrayList();
//用于存放段落开头有单个特殊符号的
static ArrayList<String> kaitou =new ArrayList<>();
// 匹配中文括号列表项(如 (1))
// static Pattern listItemPattern = Pattern.compile("^((\\d+))(.*)$");
public static void main(String[] args) {
String path="文件路径";
//初始化计算器
for (int i = 0; i < 10; i++) {
headerlist.add(0);
}
//初始化开头特殊字符
kaitou.add("★");
kaitou.add("*");
//执行代码
updateWord(path);
}
public static void updateWord(String path){
try {
// 1. 读取 .docx 文件
FileInputStream fis = new FileInputStream(path);
XWPFDocument document = new XWPFDocument(fis);
//获取word每一个段落元素(不包含表格的)
List<XWPFParagraph> paragraphs = document.getParagraphs();
// //一个段落一个段落的处理
for (int i = 0; i < paragraphs.size(); i++) {
wordDuanluo(paragraphs.get(i));
}
//开始村存回去
FileOutputStream out = new FileOutputStream("文件路径代码生成.docx");
document.write(out);
// 4. 关闭流
document.close();
fis.close();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//处理段落
private static void wordDuanluo(XWPFParagraph paragraph) {
//每个段落下面还有更小的元素,叫run 所以处理run
List<XWPFRun> runs = paragraph.getRuns();
//直接匹配第一个 是那就是标题 不是那就是正文
if (runs.size()==1){
wordRun(runs.get(0));
}else if (runs.size()==2){
//只有两个识别开头是不是包含特殊 时走一个通道 不是走二通道
//标志是否匹配
boolean flagkaitou= false;
for (int i = 0; i < kaitou.size(); i++) {
if (kaitou.get(i).equals(runs.get(0).text())){
flagkaitou=true;
break;
}
}
if (flagkaitou==true){
//证明开头匹配走一同到
wordRun(runs.get(1));
}else {
//不匹配
wordRun(runs.get(0),runs.get(1));
}
}else if (runs.size()>2){
//数量在三个及以上
//标志是否匹配
boolean flagkaitou= false;
for (int i = 0; i < kaitou.size(); i++) {
if (kaitou.get(i).equals(runs.get(0).text())){
flagkaitou=true;
break;
}
}
if (flagkaitou==true){
//证明开头匹配
wordRun(runs.get(1),runs.get(2));
}else {
//不匹配
wordRun(runs.get(0),runs.get(1));
}
}
}
// 处理非常特殊的 标题后面跟的空格字体和标题不一样分开了 进行拼接
private static void wordRun(XWPFRun run1, XWPFRun run2) {
//run是作为操作word的非常小的元素了,他会把每个段落换分成几个run组成 具体划分规则我也不知道 (我看案列 是按格式进行 格式一样的情况大概率是在一起)
//采用正则表达式进行匹配
Matcher matcher = headerPattern.matcher(run1.text()+run2.text());
if (matcher.find()) {
//匹配成功
//保存序号后面的文章用于拼接
String contex = matcher.group(2);
//按照.进行切割
String[] originalParts = matcher.group(1).split("\\.");
//根据长度判断层级 一个就一级
int length = originalParts.length;
//文档按照顺序1 1.1 1.1.1
//将子层级覆盖掉
for (int i = length; i < headerlist.size(); i++) {
headerlist.set(i,0);
}
headerlist.set(length - 1, (headerlist.get(length - 1) + 1));
StringBuffer result = new StringBuffer();
//拼接正确的序号
for (int i = 0; i < length; i++) {
result.append(headerlist.get(i));
result.append(".");
}
//多出一个. 进行删除
result.deleteCharAt(result.length()-1);
//序号放到run1 空格+正文放到run2
run1.setText(result.toString(),0);
run2.setText(contex,0);
}
}
private static void wordRun(XWPFRun run) {
//run是作为操作word的非常小的元素了,他会把每个段落换分成几个run组成 具体划分规则我也不知道 (我看案列 是按格式进行 格式一样的情况大概率是在一起)
//采用正则表达式进行匹配
Matcher matcher = headerPattern.matcher(run.text());
if (matcher.find()) {
//匹配成功
//保存序号后面的文章用于拼接
String contex = matcher.group(2);
//按照.进行切割
String[] originalParts = matcher.group(1).split("\\.");
//根据长度判断层级 一个就一级
int length = originalParts.length;
//文档按照顺序1 1.1 1.1.1
//将子层级覆盖掉
for (int i = length; i < headerlist.size(); i++) {
headerlist.set(i,0);
}
headerlist.set(length - 1, (headerlist.get(length - 1) + 1));
StringBuffer result = new StringBuffer();
//拼接正确的序号
for (int i = 0; i < length; i++) {
result.append(headerlist.get(i));
result.append(".");
}
//多出一个. 进行删除
result.deleteCharAt(result.length()-1);
result.append(contex);
//将内容替换到run
run.setText(result.toString(),0);
}
}
}
表格api说明 基本上和段落一样 有一点点不一样
XWPFDocument(最大的模块).getTables->XWPFTable(负责每一个表格).getRows->Row(代表一行).getTableCells->XWPFTableCell(每一格子)[由于cell无法更改具体看下图] 需要深入到 run
注意,我原来以为在cell就可以更改他的文本
但是看源码可以知道 他是在尾部追加并不是覆盖,所以还是只能追到run去覆盖。
表格代码:(直接看结尾的整合代码,写得更详细注释更全面)
import org.apache.poi.xwpf.usermodel.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestWord {
// 匹配表格的 结尾开头是数字 空格几个都行
static Pattern tablePattern = Pattern.compile("^(\\d+)(\\s*)$");
static Integer tableCount=0;
//用于存放段落开头有单个特殊符号的
static ArrayList<String> kaitou =new ArrayList<>();
public static void main(String[] args) {
String path="测试table.docx";
//初始化开头特殊字符
kaitou.add("★");
kaitou.add("*");
readWord(path);
}
public static void readWord(String filePath){
try {
// 1. 读取 .docx 文件
FileInputStream fis = new FileInputStream(filePath);
XWPFDocument document = new XWPFDocument(fis);
//直接一层一层找一下找到 run 试过在cell进行修改 但是cell的修改是 原来的基础上进行了新的增加 不能进行替换 直接找到run进行替换
for (XWPFTable table : document.getTables()) {
//一个table 清除一次计数器
tableCount=0;
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
for (XWPFParagraph paragraph : cell.getParagraphs()) {
List<XWPFRun> runs = paragraph.getRuns();
//直接匹配第一行
if (runs.size()==1){
tableup(runs.get(0));
}else if (runs.size()>1){
//这边就是 考虑到前面有个特殊符号的情况 就需要判断了
XWPFRun run1 = runs.get(0);
//第一个不是特殊那就直接走第一通道
boolean flag=false;
for (int i = 0; i < kaitou.size(); i++) {
if (kaitou.get(i).equals(run1.text())){
flag=true;
break;
}
}
if (flag){
tableup(runs.get(1));
}else {
tableup(run1);
}
}
}
}
}
}
System.out.println("测试完成");
FileOutputStream out = new FileOutputStream("生成的table.docx");
document.write(out);
// 4. 关闭流
document.close();
fis.close();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void tableup(XWPFRun run){
Matcher matcher = tablePattern.matcher(run.text());
if (matcher.find()){
//匹配成功 开始更换层级
String content = matcher.group(2);
String originalParts = matcher.group(1);
//开始覆盖掉序号 并拼接后面的内容
tableCount++;
StringBuffer result=new StringBuffer();
result.append(tableCount+content);
//写入
run.setText(result.toString(),0);
}
}
}
总结
整合代码:
package codeByZyc;
import org.apache.poi.xwpf.usermodel.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Upword {
// 匹配数字开头,小数点隔开 空格后接内容
static Pattern headerPattern = Pattern.compile("^(\\D?)(\\d+(?:\\.\\d+)*)(\\s+)");
//用于段落计算层级
static ArrayList<Integer> headerlist= new ArrayList();
// 匹配表格的 结尾开头是数字 空格几个都行
static Pattern tablePattern = Pattern.compile("^(\\D?)(\\d+)(\\s*)$");
//匹配表格的层级
static Integer tableCount=0;
public static void main(String[] args) {
String path="C:\\Users\\测试专用删除部分标题.docx";
//初始化段落层级计算器
for (int i = 0; i < 10; i++) {
headerlist.add(0);
}
//执行代码
updateWord(path);
}
public static void updateWord(String path){
try {
// 1. 读取 .docx 文件
FileInputStream fis = new FileInputStream(path);
XWPFDocument document = new XWPFDocument(fis);
//获取word每一个段落元素(不包含表格的)
List<XWPFParagraph> paragraphs = document.getParagraphs();
// //一个段落一个段落的处理
for (int i = 0; i < paragraphs.size(); i++) {
wordDuanluo(paragraphs.get(i));
}
//获取word的表格
// 直接一层一层找一下找到 run 试过在cell进行修改 但是cell的修改是 原来的基础上进行了新的增加 不能进行替换 直接找到run进行替换
for (XWPFTable table : document.getTables()) {
wordtable(table);
}
//开始村存回去
FileOutputStream out = new FileOutputStream("C:\\Users\\代码生成.docx");
document.write(out);
// 4. 关闭流
document.close();
fis.close();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//处理段落
private static void wordDuanluo(XWPFParagraph paragraph) {
//每个段落下面还有更小的元素,叫run 所以处理run
List<XWPFRun> runs = paragraph.getRuns();
/* 这里需要注意一下
因为我的测试文档的需求标题是这样的 *1.2.1 背景展望 像这个 要是格式不一样
会被划分成 三个部分 * 1.2.1 还有一个空格 。 但是要是格式一样 就直接化成一个了
主要是我的测试文档是有些一样 有些不一样所以需要考虑的比较多。
*/
if (runs.size()==1){
//针对只划分一个的 那没得说 直接走第一个
wordRun(runs.get(0));
}else if (runs.size()==2){
//这就是两个 那可能是两种情况: *1.2.1 空格 或者 * 1.2.1空格 就是两种了
wordRun(runs.get(0),runs.get(1));
}else if (runs.size()>2){
//数量在三个及以上
//这种 就是我上面说的 三个格式都不一样
wordRun(runs.get(0),runs.get(1),runs.get(2));
}
}
private static void wordRun(XWPFRun run) {
//run是作为操作word的非常小的元素了,他会把每个段落换分成几个run组成 具体划分规则我也不知道 (我看案列 是按格式进行 格式一样的情况大概率是在一起)
//采用正则表达式进行匹配
Matcher matcher = headerPattern.matcher(run.text());
if (matcher.find()) {
//匹配成功
String oldxuaho=matcher.group(2);
//按照.进行切割
String[] originalParts =oldxuaho.split("\\.");
//根据长度判断层级 一个就一级
int length = originalParts.length;
//文档按照顺序1 1.1 1.1.1
//将子层级覆盖掉
for (int i = length; i < headerlist.size(); i++) {
headerlist.set(i,0);
}
headerlist.set(length - 1, (headerlist.get(length - 1) + 1));
StringBuffer result = new StringBuffer();
//拼接正确的序号
//把序号前面的放进来
for (int i = 0; i < length; i++) {
result.append(headerlist.get(i));
result.append(".");
}
//多出一个. 进行删除
result.deleteCharAt(result.length()-1);
//替换
if(run.text().contains(oldxuaho)){
//进行替换
String s = run.text();
String replace = s.replace(oldxuaho, result.toString());
run.setText(replace,0);
}
}
}
// 处理非常特殊的 标题后面跟的空格字体和标题不一样分开了 进行拼接
private static void wordRun(XWPFRun run1, XWPFRun run2) {
//run是作为操作word的非常小的元素了,他会把每个段落换分成几个run组成 具体划分规则我也不知道 (我看案列 是按格式进行 格式一样的情况大概率是在一起)
//采用正则表达式进行匹配
Matcher matcher = headerPattern.matcher(run1.text()+run2.text());
if (matcher.find()) {
//匹配成功
String oldxuaho=matcher.group(2);
//按照.进行切割
String[] originalParts =oldxuaho.split("\\.");
//根据长度判断层级 一个就一级
int length = originalParts.length;
//文档按照顺序1 1.1 1.1.1
//将子层级覆盖掉
for (int i = length; i < headerlist.size(); i++) {
headerlist.set(i,0);
}
headerlist.set(length - 1, (headerlist.get(length - 1) + 1));
StringBuffer result = new StringBuffer();
//把前面的放进来
//拼接正确的序号
for (int i = 0; i < length; i++) {
result.append(headerlist.get(i));
result.append(".");
}
//多出一个. 进行删除
result.deleteCharAt(result.length()-1);
//查找替换 具体思路看一看下面的
if(run1.text().contains(oldxuaho)){
//进行替换
String s = run1.text();
String replace = s.replace(oldxuaho, result.toString());
run1.setText(replace,0);
}else if (run2.text().contains(oldxuaho)){
String s = run2.text();
String replace = s.replace(oldxuaho, result.toString());
run2.setText(replace,0);
}
}
}
private static void wordRun(XWPFRun run1, XWPFRun run2,XWPFRun run3) {
//run是作为操作word的非常小的元素了,他会把每个段落换分成几个run组成 具体划分规则我也不知道 (我看案列 是按格式进行 格式一样的情况大概率是在一起)
//采用正则表达式进行匹配
Matcher matcher = headerPattern.matcher(run1.text()+run2.text()+run3.text());
if (matcher.find()) {
//匹配成功
String oldxuaho=matcher.group(2);
//按照.进行切割
String[] originalParts = oldxuaho.split("\\.");
//根据长度判断层级 一个就一级
int length = originalParts.length;
//文档按照顺序1 1.1 1.1.1
//将子层级覆盖掉
for (int i = length; i < headerlist.size(); i++) {
headerlist.set(i,0);
}
headerlist.set(length - 1, (headerlist.get(length - 1) + 1));
StringBuffer result = new StringBuffer();
//拼接正确的序号
for (int i = 0; i < length; i++) {
result.append(headerlist.get(i));
result.append(".");
}
//多出一个. 进行删除
result.deleteCharAt(result.length()-1);
/*下面就需要考虑 run1 2 3 如何划分了
因为我的matcher.group 把他们三个合并的 拆分成了 三部分 (数字标题前面的部分) (数字标题) 空格内容(内容可能有可能无)
* 情况1: 最开始说到的 * 1.2.1 空格内容(内容可能有可能无)
* 情况2: 也有可能是: *1.2.1 空格内容(内容可能有可能无) 内容
情况3: 也有可能是: * 1.2.1空格内容(内容可能有可能无) 内容
情况4: *1.2.1空格内容(内容可能有可能无) 内容 内容
解决思路: group(2)匹配的序号 只需要 挨个便利 看看 哪个run包含 进行替换就行其他的保持不变。
* */
//查找
if(run1.text().contains(oldxuaho)){
//进行替换
String s = run1.text();
String replace = s.replace(oldxuaho, result.toString());
run1.setText(replace,0);
}else if (run2.text().contains(oldxuaho)){
String s = run2.text();
String replace = s.replace(oldxuaho, result.toString());
run2.setText(replace,0);
}else {
String s = run3.text();
String replace = s.replace(oldxuaho, result.toString());
run3.setText(replace,0);
}
}
}
//处理表格
private static void wordtable(XWPFTable table){
//一个table 清除一次计数器
tableCount=0;
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
for (XWPFParagraph paragraph : cell.getParagraphs()) {
List<XWPFRun> runs = paragraph.getRuns();
//直接匹配第一行
if (runs.size()==1){
wordTableRun(runs.get(0));
}else if (runs.size()==2){
wordTableRun(runs.get(0),runs.get(1));
}
}
}
}
}
//处理表格的单元格
private static void wordTableRun(XWPFRun run){
Matcher matcher = tablePattern.matcher(run.text());
if (matcher.find()){
//匹配成功 开始更换层级
String oldxuhao = matcher.group(2);
//开始覆盖掉序号 并拼接后面的内容
tableCount++;
String s = run.text();
String replace = s.replace(oldxuhao, tableCount.toString());
//写入
run.setText(replace,0);
}
}
private static void wordTableRun(XWPFRun run1,XWPFRun run2){
Matcher matcher = tablePattern.matcher(run1.text()+run2.text());
if (matcher.find()){
String oldxuhao=matcher.group(2);
//开始覆盖掉序号 并拼接后面的内容
tableCount++;
String s;
if (run1.text().contains(oldxuhao)){
s=run1.text();
String replace = s.replace(oldxuhao, tableCount.toString());
run1.setText(replace,0);
}else if (run2.text().contains(oldxuhao)){
s=run2.text();
String replace = s.replace(oldxuhao, tableCount.toString());
run2.setText(replace,0);
}
}
}
}
难点:
我感觉最大的难点就是对Api的熟悉,需要看看源码或者文档。以及利用正则表达式对标题进行匹配
3.0代码适用针对word的规则
共同规则:
1: 序号前面的特殊符号 * # 五角星等 必须紧贴序号
2: 分割符号和序号必须紧贴
针对段落:
1:段落的标题必须空格才能接内容
2:一次职能出现一个分割符号 不能 又是 . 又是 -
例子: *1.2.1 (一个或多个空格) 内容
针对表格:
1:一个表格的分割符号不能 又是 . 又是 - 。 注意是一个 你可以两个表格不一样的分割符号
说到底主要是能匹配上,下面的正则表达式
段落的正则表达式: ^(\\D?)(\\d+(?:(\\D)\\d+)*)\\s+.
表格的正则表达式: ^(\\D?)(\\d+(?:(\\D)\\d+)*)
整合代码3.0 优化大部分
package codeByZyc;
import org.apache.poi.xwpf.usermodel.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
version:3.0
* 2.0:优化了处理段落的问题 针对段落将冗杂的并且可能出错的代码进行了
* 3.0:优化了处理表格的问题。
* */
public class Upword3 {
//针对段落的参数
// 匹配数字开头,特殊符号隔开 空格后接内容
/**
* group(1) 匹配标题前面的特殊符号 如: * 五角星 #
* 3 匹配 分隔符号 . - 、 (改代码只支持一篇文章只支持段落出现一钟分隔符 不能 又出现 . 又出现 - )
* 4 匹配 多个空格 必须用空格 把标题和内容 隔开
* 5 匹配 剩下的内容
* **/
static Pattern headerPattern = Pattern.compile("^(\\D?)(\\d+(?:(\\D)\\d+)*)\\s+.");
//用于段落计算层级
static ArrayList<Integer> headerlist= new ArrayList();
//针对表格的参数
// 匹配表格的 开头任意1个或0个 非特殊符号
static Pattern tablePattern = Pattern.compile("^(\\D?)(\\d+(?:(\\D)\\d+)*)");
//匹配表格 对应的tablePattern 的层级
static ArrayList<Integer> tablelist= new ArrayList();
public static void main(String[] args) {
String path="C:\\Users\\33485\\Desktop\\zyc\\testWord\\测试专用删除部分标题.docx";
//初始化段落层级计算器
for (int i = 0; i < 10; i++) {
headerlist.add(0);
}
for (int i = 0; i < 10; i++) {
tablelist.add(0);
}
//执行代码
updateWord(path);
}
public static void updateWord(String path){
try {
// 1. 读取 .docx 文件
FileInputStream fis = new FileInputStream(path);
XWPFDocument document = new XWPFDocument(fis);
//获取word每一个段落元素(不包含表格的)
List<XWPFParagraph> paragraphs = document.getParagraphs();
// //一个段落一个段落的处理
for (int i = 0; i < paragraphs.size(); i++) {
wordDuanluo(paragraphs.get(i));
}
//获取word的表格
// 直接一层一层找一下找到 run 试过在cell进行修改 但是cell的修改是 原来的基础上进行了新的增加 不能进行替换 直接找到run进行替换
for (XWPFTable table : document.getTables()) {
wordtable(table);
}
//开始村存回去
FileOutputStream out = new FileOutputStream("C:\\Users\\33485\\Desktop\\zyc\\testWord\\代码生成的.docx");
document.write(out);
// 4. 关闭流
document.close();
fis.close();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//处理段落
private static void wordDuanluo(XWPFParagraph paragraph) {
//每个段落下面还有更小的元素,叫run 所以处理run
List<XWPFRun> runs = paragraph.getRuns();
//把内容拼起来
StringBuffer text=new StringBuffer();
for (XWPFRun run : runs) {
text.append(run.text());
}
//进行匹配
Matcher matcher = headerPattern.matcher(text);
if (matcher.find()){
//先把正确的序号拿到
String oldxuaho=matcher.group(2);
//获取数字中间的特殊符号
String split=matcher.group(3);
String newxuhao= upDuanluoXuhao(oldxuaho,split);
/*匹配成功表示 是标题字段 进行操作
* 思路是 按照空格判断 序号和内容的分界线
* runs起始位置0(坐标为0) 空格位置(坐标为a) runs结尾位置(坐标为b)
* 1 0到a 中间的run 全部放到 runs(0) 里面 都一个格式 其他的清空
* 2 a到b 中间的run 保持不变
* 3 空格的run 有三种情况 分别处理即可
* 空格开头
* 空格在中间
* 空格结尾
*
* */
//找出 空格在哪个run里面 记录坐标
int kg=-1;
int size= runs.size();
for (int i = 0; i < size; i++) {
if (runs.get(i).text().contains(" ")){
kg=i;
break;
}
}
if (kg==0){
//证明第一个run 里面就包含了 不用往下走 那就非常简单 替换掉old序号就行 其他不变
XWPFRun run = runs.get(0);
String replace = run.text().replace(oldxuaho, newxuhao);
run.setText(replace,0);
}else {
//执行第一步 0到a 中间的run 全部放到 runs(0) 里面 都一个格式 其他的清空
//把正确序号+前面特殊符号 放到1
runs.get(0).setText(matcher.group(1)+newxuhao,0);
for (int i=1; i<kg;i++){
//清空中间的run
paragraph.removeRun(1);
}
//执行第三步 空格的run 有三种情况 分别处理即可
//上面进行了删除 所以 空格的坐标 变成了1 自己调试看就知道了
kg=1;
XWPFRun run = runs.get(kg);
String runtext = run.text();
//找出空格 第一次的位置
int kgstart = runtext.indexOf(' ');
//找出空格 最后一次的位置
if (kgstart==0){
//空格开头 那就不用管了
}else {
/*空格在结尾 和空格在中间 可以合并 情况如下
* 空格在结尾:
* 1.1空格
* .1空格
*
* 空格在中间:
* .1空格内容
* 1.1空格内容
* 由于正确的序号已经被全部放到了runs(0) 这个run直接放空格加内容即可就行了
* */
Pattern kgendPattern = Pattern.compile("(\\s+.)");
Matcher matcher1 = kgendPattern.matcher(runtext);
if (matcher1.find()){
//按道理 这个肯定是百分百匹配成功的
run.setText(matcher1.group(1),0);
}else {
//以防万一 严谨一点
System.out.println("这个地方需要进行调式 code:001");
}
}
}
}
}
//处理段落的序号问题
public static String upDuanluoXuhao(String oldxuhao,String spilt ){
StringBuffer sb=new StringBuffer();
sb.append("\\").append(spilt);
StringBuffer result = new StringBuffer();
int length=0;
if (spilt==null){
//证明 中间没有特殊符号进行分割
length=1;
}else {
//根据长度判断层级 一个就一级
length = oldxuhao.split(sb.toString()).length;
}
//文档按照顺序1 1.1 1.1.1
//将子层级覆盖掉
for (int i = length; i < headerlist.size(); i++) {
headerlist.set(i,0);
}
headerlist.set(length - 1, (headerlist.get(length - 1) + 1));
//拼接正确的序号
//把序号前面的放进来
if (spilt==null){
for (int i = 0; i < length; i++) {
result.append(headerlist.get(i));
}
}else {
for (int i = 0; i < length; i++) {
result.append(headerlist.get(i));
result.append(spilt);
}
//多出一个. 进行删除
result.deleteCharAt(result.length()-1);
}
return result.toString();
}
//处理表格
private static void wordtable(XWPFTable table){
//一个table 清除一次计数器
for (int i = 0; i < 10; i++) {
tablelist.set(i,0);
}
for (XWPFTableRow row : table.getRows()) {
//第一列才有序号 直接匹配第一列
XWPFTableCell cell = row.getTableCells().get(0);
if (cell!=null){
for (XWPFParagraph paragraph : cell.getParagraphs()) {
upWordTable(paragraph);
}
}
}
}
/*
* 针对表格的序号进行处理 由于直接选取的 第一列进行匹配 不想段落一样需要考虑很多
*
* */
private static void upWordTable(XWPFParagraph paragraph) {
// 这里面 不需要考虑到 run里面包含了 内容等问题
Matcher matcher = tablePattern.matcher(paragraph.getText());
if (matcher.find()){
//匹配成功 开始更换层级
String oldxuhao = matcher.group(2);
//开始覆盖掉序号 并拼接后面的内容
//这是 中间的特殊符号
String spilt=matcher.group(3);
//进行序号处理
String newxuhao = upTableXuhao(oldxuhao, spilt);
/*
* 开始进行序号处理了 由于没有内容 我这边采用的是 全部删除 然后放在 runs(0)统一格式
*
* */
List<XWPFRun> runs = paragraph.getRuns();
XWPFRun run = runs.get(0);
//把正确的序号以及特殊标识放到runs(0)
//写入 前面的特殊符号(* 五角星 # 等)+正确的序号(1.2.3) 后面的空格这些我就没去读取了 需要的可以自己用正则进行匹配
run.setText(matcher.group(1)+newxuhao,0);
//开始清空后面的run
for (int i = 1; i < runs.size(); i++) {
//移除其余的run
paragraph.removeRun(1);
}
}
}
//进行表格的序号重置
public static String upTableXuhao(String oldxuhao,String spilt ){
StringBuffer sb=new StringBuffer();
sb.append("\\").append(spilt);
StringBuffer result = new StringBuffer();
int length=0;
if (spilt==null){
//证明 中间没有特殊符号进行分割
length=1;
}else {
//根据长度判断层级 一个就一级
length = oldxuhao.split(sb.toString()).length;
}
//文档按照顺序1 1.1 1.1.1
//将子层级覆盖掉
for (int i = length; i < tablelist.size(); i++) {
tablelist.set(i,0);
}
tablelist.set(length - 1, (tablelist.get(length - 1) + 1));
//拼接正确的序号
//把序号前面的放进来
if (spilt==null){
for (int i = 0; i < length; i++) {
result.append(tablelist.get(i));
}
}else {
for (int i = 0; i < length; i++) {
result.append(tablelist.get(i));
result.append(spilt);
}
//多出一个. 进行删除
result.deleteCharAt(result.length()-1);
}
return result.toString();
}
}