1、Java操作word基本介绍

Java对Word文档的基本操作可以通过Apache POI库实现,主要包括以下几个方面:

  1. 创建Word文档:可以通过创建XWPFDocument对象来创建一个空白的Word文档
  2. 读取Word文档:可以通过XWPFDocument对象的构造函数读取已有的Word文档
  3. 插入段落:可以通过XWPFDocument对象的createParagraph()方法创建一个段落,然后设置段落的样式和内容。
  4. 插入表格:可以通过XWPFDocument对象的createTable()方法创建一个表格,然后设置表格的样式和内容。
  5. 插入图片:可以通过XWPFDocument对象的createParagraph()方法创建一个段落,然后使用XWPFRun对象的addPicture()方法插入图片。
  6. 保存Word文档:可以通过XWPFDocument对象的write()方法将文档保存到指定的文件路径。

2、添加maven依赖

<!-- 引入poi-ooxml依赖,2007+使用 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>

3、示例一(创建新文档)

下面是一个简单的java操作word表格的示例,演示了如何创建一个Word文档并创建表格,以及水平合并单元格(合并列)、垂直合并单元格(合并行)、单元格赋值等操作。

3.1 创建新Word文档

// 创建一个新的Word文档
XWPFDocument document = new XWPFDocument();

3.2 创建新表格

// 创建一个新的表格,11行7列
XWPFTable table = document.createTable(11, 7);

3.3 设置表格样式

在Word文档中,表格样式是通过样式名称来定义的,例如"Table Grid"就是一种内置的表格样式。

// 设置表格样式
table.getCTTbl().addNewTblPr().addNewTblStyle().setVal("Table Grid");

3.4 设置表格宽度

// 设置表格宽度为100%
table.setWidth("100%");

3.5 循环填充单元格内容

// 填充表格内容
XWPFTableCell cell = null;
for (int row = 0; row < 11; row++) { // 循环遍历表格的行
  for (int col = 0; col < 7; col++) { // 循环遍历表格的列
    // 获取当前单元格
    cell = table.getRow(row).getCell(col);
    // 设置单元格内容垂直居中
    cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
    // 设置单元格内容水平居中(获取单元格中的第一个段落对象)
    cell.getParagraphArray(0).setAlignment(ParagraphAlignment.CENTER); 
    // 设置单元格内容
    cell.setText("行 " + (row + 1) + ", 列 " + (col + 1));
  }
}

3.6 合并单元格

3.6.1 合并行

// 合并第1行的第2列到第4列
table.getRow(0).getCell(1).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
table.getRow(0).getCell(2).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
table.getRow(0).getCell(3).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
效果:

Java操作word表格基本使用_Java

3.6.2 合并列

// 合并第1列的第3行到第4行
table.getRow(2).getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
table.getRow(3).getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);

效果:

Java操作word表格基本使用_Word_02

3.6.3 跨列跨行合并

跨行跨列合并是合并行、合并列的组合,因此需要按照一定的合并顺序来进行。下面是一个合并第4行第4列、第5列、第5行第4列、第5列为一个单元格的步骤。

// 合并第4行第4列、第5列、第5行第4列、第5列为一个单元格
// 1. 合并第4行的第4列到第5列
table.getRow(3).getCell(3).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
table.getRow(3).getCell(4).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
// 2. 合并第5行的第4列到第5列
table.getRow(4).getCell(3).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
table.getRow(4).getCell(4).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
// 3. 合并第4列的第4行到第5行
table.getRow(3).getCell(3).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
table.getRow(4).getCell(3).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);

效果:

Java操作word表格基本使用_Word_03

3.7 保存文档

// 输出流,用于将文档写入磁盘
FileOutputStream fos = new FileOutputStream("f:/test.docx");
// 将文档写入输出流
document.write(fos);

3.8 关闭输出流与文档

// 关闭输出流
fos.close();
// 关闭文档
document.close();

3.9 完整代码

3.9.1 源码

合并列和合并行的代码,这里进行了封装,优化了代码结构,具体内容可看以下代码:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;

public class ToolWord {
	public static void main(String[] args) {
		test();
	}

	public static void test() {
		// 1.创建一个新的Word文档
		XWPFDocument document = new XWPFDocument();
		FileOutputStream fos = null;
		try {
			// 2.创建一个新的表格,11行7列
			XWPFTable table = document.createTable(11, 7);

			// 3.设置表格样式
			table.getCTTbl().addNewTblPr().addNewTblStyle().setVal("Table Grid");
			table.setWidth("100%"); // 设置表格宽度为100%

			// 4.填充表格内容
			XWPFTableCell cell = null;
			for (int row = 0; row < 11; row++) { // 循环遍历表格的行
				for (int col = 0; col < 7; col++) { // 循环遍历表格的列
					// 获取当前单元格
					cell = table.getRow(row).getCell(col);
					// 设置单元格内容垂直居中
					cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
					// 设置单元格内容水平居中(获取单元格中的第一个段落对象)
					cell.getParagraphArray(0).setAlignment(ParagraphAlignment.CENTER);
					// 设置单元格内容
					cell.setText("行 " + (row + 1) + ", 列 " + (col + 1));
				}
			}

			/**
			
			// 5.合并单元格方式一 // 5.1 合并第1行的第2列到第4列
			table.getRow(0).getCell(1).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
			table.getRow(0).getCell(2).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
			table.getRow(0).getCell(3).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
			
			// 5.2 合并第1列的第3行到第4行
			table.getRow(2).getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
			table.getRow(3).getCell(0).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
			
			// 5.3 合并第4行第4列、第5列、第5行第4列、第5列为一个单元格 // 5.3.1 合并第4行的第4列到第5列
			table.getRow(3).getCell(3).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
			table.getRow(3).getCell(4).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
			// 5.3.2 合并第5行的第4列到第5列
			table.getRow(4).getCell(3).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
			table.getRow(4).getCell(4).getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
			// 5.3.3 合并第4列的第4行到第5行
			table.getRow(3).getCell(3).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
			table.getRow(4).getCell(3).getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
			
			**/

			// 6.合并单元格方式二(推荐)
			// 6.1 水平合并第1行的第2列到第4列
			mergeCellsByHorizontal(table, 0, 1, 3);
			// 6.2 垂直合并第1列的第3行到第4行
			mergeCellsByVertically(table, 0, 2, 3);
			// 6.3 合并第4行第4列、第5列、第5行第4列、第5列为一个单元格
			// 6.3.1 合并第4行的第4列到第5列
			mergeCellsByHorizontal(table, 3, 3, 4);
			// 6.3.2 合并第5行的第4列到第5列
			mergeCellsByHorizontal(table, 4, 3, 4);
			// 6.3.3 合并第4列的第4行到第5行
			mergeCellsByVertically(table, 3, 3, 4);

			// 保存文档
			// 输出流,用于将文档写入磁盘
			fos = new FileOutputStream("f:/test.docx");
			// 将文档写入输出流
			document.write(fos);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				try {
					// 关闭输出流
					fos.close();
					if (document != null) {
						// 关闭Word文档
						document.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		System.out.println("Word文档生成成功!");
	}

	// ==================== private method ====================
	/**
	 * <h5>描述:水平方向合并单元格<h5>
	 * @param table 表格
	 * @param rowIndex 合并列所在的行下标(从0开始)
	 * @param startCellIndex 开始合并的列下标(从0开始)
	 * @param endCellIndex 结束合并的列下标(从0开始)
	 */
	private static void mergeCellsByHorizontal(XWPFTable table, int rowIndex, int startCellIndex, int endCellIndex) {
		String str = "";
		for (int i = startCellIndex; i <= endCellIndex; i++) {
			XWPFTableCell cell = table.getRow(rowIndex).getCell(i);
			str = "table.getRow("+rowIndex+").getCell("+i+")";
			if (i == startCellIndex) {
				// The first merged cell is set with RESTART merge value  
				cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
				System.out.println(str + ".getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);");
			} else {
				// Cells which join (merge) the first one, are set with CONTINUE  
				cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
				System.out.println(str + ".getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);");
			}
		}
	}
 
	/**
	 * <h5>描述:垂直方向合并单元格<h5>
	 * @param table 表格
	 * @param columnIndex 合并行所在列下标(从0开始)
	 * @param startCell 开始合并的行下标(从0开始)
	 * @param endCell 结束合并的行下标(从0开始)
	 */
	private static void mergeCellsByVertically(XWPFTable table, int columnIndex, int startRowIndex, int endRowIndex) {
		String str = "";
		for (int i = startRowIndex; i <= endRowIndex; i++) {
			XWPFTableCell cell = table.getRow(i).getCell(columnIndex);
			str = "table.getRow("+i+").getCell("+columnIndex+")";
			if (i == startRowIndex) {
				// The first merged cell is set with RESTART merge value  
				cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
				System.out.println(str + ".getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);");
			} else {
				// Cells which join (merge) the first one, are set with CONTINUE  
				cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
				System.out.println(str + ".getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);");
			}
		}
	}
}

3.9.2 执行信息

Java操作word表格基本使用_Java_04

3.9.3 效果

Java操作word表格基本使用_Word_05

4、示例二(模板调用)

下面也是一个简单的java操作word表格的示例,演示了如何获取一个已存在的Word模板,并对表格进行插入新行、合并行、合并列、单元格赋值等操作。

4.1 word模板

Java操作word表格基本使用_Word_06

4.2 读取Word模板文件

 "f:/Word模板.docx"是word模板全路径

// 读取Word模板文件
XWPFDocument document = new XWPFDocument(new FileInputStream("f:/Word模板.docx"));

4.3 获取指定表格

表格下标都是从0开始,这里获取的是第1个表格

// 获取第一个表格(表格下标从0开始)
XWPFTable table = document.getTables().get(0);

4.4 指定位置添加行

添加行之后,至少要新建一个单元格,否则不会添加成功

int rowIndex = 3;
// 在指定行下面添加一行(下标从0开始),这里添加到第4行
XWPFTableRow newRow = table.insertNewTableRow(rowIndex);
// 新行创建单元格
for (int i = 0; i < 6; i++) {
  if (i % 2 == 1) {
    newRow.createCell(); // 仅创建新单元格
    continue;
  }
  newRow.createCell().setText("新单元格" + (rowIndex + 1) + (i + 1)); // 创建新单元格并填充内容
}

4.5 填充表格内容

4.5.1 先新增再填充

插入了新行之后再填充表格内容,表格内容是连续的

// --------------------在指定行下面添加一行,并创建指定数量的单元格start--------------------
int rowIndex = 3;
// 在指定行下面添加一行(下标从0开始),这里添加到第4行
XWPFTableRow newRow = table.insertNewTableRow(rowIndex);
// 新行创建单元格
for (int i = 0; i < 6; i++) {
  if (i % 2 == 1) {
    newRow.createCell(); // 仅创建新单元格
    continue;
  }
  newRow.createCell().setText("新单元格" + (rowIndex + 1) + (i + 1)); // 创建新单元格并填充内容
}
// --------------------在指定行下面添加一行,并创建指定数量的单元格end--------------------

// 填充表格内容
XWPFTableRow tableRow = null;
List<XWPFTableCell> cellList = null;

List<XWPFTableRow> rowList = table.getRows();
for (int i = 0; i < rowList.size(); i++) {
  // 如果是刚才新添加的行,则不填充表格内容
  if (i == rowIndex) {
    continue;
  }
  tableRow = rowList.get(i);
  cellList = tableRow.getTableCells();
  for (int j = 0; j < cellList.size(); j++) {
    cellList.get(j).setText("info " + (i + 1) + (j + 1));
  }
}

效果:

Java操作word表格基本使用_Java_07

4.5.2 先填充再新增

填充表格内容之后再插入新行,表格内容会从中间断开

// 填充表格内容
XWPFTableRow tableRow = null;
List<XWPFTableCell> cellList = null;

List<XWPFTableRow> rowList = table.getRows();
for (int i = 0; i < rowList.size(); i++) {
  tableRow = rowList.get(i);
  cellList = tableRow.getTableCells();
  for (int j = 0; j < cellList.size(); j++) {
    cellList.get(j).setText("info " + (i + 1) + (j + 1));
  }
}

// --------------------在指定行下面添加一行,并创建指定数量的单元格start--------------------
int rowIndex = 5;
// 在指定行下面添加一行(下标从0开始),这里添加到第6行
XWPFTableRow newRow2 = table.insertNewTableRow(rowIndex);
// 新行创建单元格
for (int i = 0; i < 6; i++) {
  if (i % 2 == 0) {
    newRow2.createCell(); // 仅创建新单元格
    continue;
  }
  newRow2.createCell().setText("新单元格" + (rowIndex + 1) + (i + 1)); // 创建新单元格并填充内容
}
// --------------------在指定行下面添加一行,并创建指定数量的单元格end--------------------

效果:

Java操作word表格基本使用_Java_08

4.6 表格末尾添加行

添加行之后,至少要新建一个单元格,否则不会添加成功

// 表格末尾添加一行
XWPFTableRow newRow3 = table.createRow();

// 设置新行的单元格内容
newRow3.getCell(0).setText("新单元格 1");
newRow3.getCell(1).setText("新单元格 2");
newRow3.getCell(2).setText("新单元格 3");

效果:

Java操作word表格基本使用_Java_09

4.7 保存文档

// 输出流,用于将文档写入磁盘
FileOutputStream fos = new FileOutputStream("f:/test.docx");
// 将文档写入输出流
document.write(fos);

4.8 关闭输出流与文档

// 关闭输出流
fos.close();
// 关闭文档
document.close();

4.9 完整代码

4.9.1 源码

代码结构进行了整合优化,与以上代码稍有不同,但不影响阅读

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;

public class ToolWord {
	public static void main(String[] args) {
		readWord();
	}

	public static void readWord() {
    XWPFDocument document = null;
    FileOutputStream out = null;
    try {
      // 读取Word模板文件
      document = new XWPFDocument(new FileInputStream("f:/Word模板.docx"));

      // 获取第一个表格(表格下标从0开始)
      XWPFTable table = document.getTables().get(0);

      // --------------------在指定行下面添加一行,并创建指定数量的单元格start--------------------
      int rowIndex = 3;
      // 在指定行下面添加一行(下标从0开始),这里添加到第4行
      XWPFTableRow newRow = table.insertNewTableRow(rowIndex);
      // 新行创建单元格
      for (int i = 0; i < 6; i++) {
        if (i % 2 == 1) {
          newRow.createCell(); // 仅创建新单元格
          continue;
        }
        newRow.createCell().setText("新单元格" + (rowIndex + 1) + (i + 1)); // 创建新单元格并填充内容
      }
      // --------------------在指定行下面添加一行,并创建指定数量的单元格end--------------------

      // 填充表格内容
      XWPFTableRow tableRow = null;
      List<XWPFTableCell> cellList = null;

      List<XWPFTableRow> rowList = table.getRows();
      for (int i = 0; i < rowList.size(); i++) {
        // 如果是刚才新添加的行,则不填充表格内容
        if (i == rowIndex) {
          continue;
        }
        tableRow = rowList.get(i);
        cellList = tableRow.getTableCells();
        for (int j = 0; j < cellList.size(); j++) {
          cellList.get(j).setText("info " + (i + 1) + (j + 1));
        }
      }

      // --------------------在指定行下面添加一行,并创建指定数量的单元格start--------------------
      rowIndex = 5;
      // 在指定行下面添加一行(下标从0开始),这里添加到第6行
      XWPFTableRow newRow2 = table.insertNewTableRow(rowIndex);
      // 新行创建单元格
      for (int i = 0; i < 6; i++) {
        if (i % 2 == 0) {
          newRow2.createCell(); // 仅创建新单元格
          continue;
        }
        newRow2.createCell().setText("新单元格" + (rowIndex + 1) + (i + 1)); // 创建新单元格并填充内容
      }
      // --------------------在指定行下面添加一行,并创建指定数量的单元格end--------------------

      // 表格末尾添加一行
      XWPFTableRow newRow3 = table.createRow();

      // 设置新行的单元格内容
      newRow3.getCell(0).setText("新单元格 1");
      newRow3.getCell(1).setText("新单元格 2");
      newRow3.getCell(2).setText("新单元格 3");

      // 保存修改后的Word文件
      out = new FileOutputStream("f:/新word.docx");
      document.write(out);
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if (out != null) {
        try {
          // 关闭输出流
          out.close();
          if (document != null) {
            // 关闭Word文档
            document.close();
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
    System.out.println("新Word文档生成成功!");
  }
}

4.9.2 执行信息

Java操作word表格基本使用_Java_10

4.9.3 效果

可以很明显的看到,先新增行再填充数据的单元格数据是连续的先填充数据再新增的行,单元格数据被从中间隔开了

Java操作word表格基本使用_Java_11