0.前言
上一篇文章介绍了创建表格和读取表格,在很多时候,我们需要动态的创建表格和修改表格内容。这篇文章介绍一些哪些创建行和列的方式。比较常见的创建行的方式有
createRow()
addRow()
insertNewTableRow()
addNewRowBetween()
官方文档:https://poi.apache.org/apidocs/4.0/org/apache/poi/xwpf/usermodel/XWPFTable.html
1.创建行
- addNewRowBetween(int start, int end)
在某个区间范围内插入行,但是官方显示此方法已经弃用,所以不推荐
- createRow()
创建一个新的XWPFTableRow对象,其单元格数与该时刻定义的列数一样多。
XWPFTableRow row = table.createRow();
参考代码:
@Test
void testXWPFTable1(){
System.out.println("开始执行");
try {
//定义word
XWPFDocument doc = new XWPFDocument();
//在word中创建一个表格(2行,3列)
XWPFTable table = doc.createTable(2,3);
//第一行数据
table.getRow(0).getCell(0).setText("1");
table.getRow(0).getCell(1).setText("2");
table.getRow(0).getCell(2).setText("3");
//插入新的一行
XWPFTableRow row = table.createRow();
table.getRow(1).getCell(0).setText("4");
table.getRow(1).getCell(1).setText("5");
table.getRow(1).getCell(2).setText("666666");
//导出
String path = "C:\\"; //文件路径
String name = "test1"; //文件名
path = path + "/" + name + ".docx";
File file = new File(path);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream out = new FileOutputStream(file);
doc.write(out);
}catch (IOException e){
e.printStackTrace();
}
}
效果:
可以看出,createRow方法是在表格底部创建一行。另外我们在单元格中写入数据“666666”然后可以看到,我们创建的表格会自适应宽度。
实际遇到的问题:
在另一个springMVC项目中,使用createRow方法创建的行是没有边框没有任何样式的。
- insertNewTableRow(int pos)
在pos位置插入一行数据,使用insertNewTableRow创建行,必须填充列,且列数必须统一,否则word会报错:“word在试图打开文件时遇到错误。请尝试下列法方法:检查文档或驱动器的文件权限。确保有足够的内存和磁盘空间。用文本恢复转换器打开文件。”
void testXWPFTable1(){
System.out.println("开始执行");
try {
//定义word
XWPFDocument doc = new XWPFDocument();
//在word中创建一个表格(2行,3列)
XWPFTable table = doc.createTable(2,3);
//第一行数据
table.getRow(0).getCell(0).setText("1");
table.getRow(0).getCell(1).setText("2");
table.getRow(0).getCell(2).setText("3");
//插入新的一行
XWPFTableRow row = table.insertNewTableRow(1);
//使用insertNewTableRow必须填充列,且列数必须统一
for (int j = 0; j < 3; j++) {
row.createCell(); //创建列
}
//同时,表格行和列的数量变了,所以这个地方从2开始
table.getRow(2).getCell(0).setText("4");
table.getRow(2).getCell(1).setText("5");
table.getRow(2).getCell(2).setText("666666");
//导出
String path = "C:\\"; //文件路径
String name = "test1"; //文件名
path = path + "/" + name + ".docx";
File file = new File(path);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream out = new FileOutputStream(file);
doc.write(out);
}catch (IOException e){
e.printStackTrace();
}
}
效果:
可以看出,可以在中间创建一行。
实际问题:在另一个项目中使用此方法,创建出来的行没有边框没有样式。
- addRow((XWPFTableRow copyRow, int pos)
按照copyRow的样式,在pos位置插入新的一行,新的一行具有copyRow的样式。
错误用法1:直接复制某一行数据
void testXWPFTable1(){
System.out.println("开始执行");
try {
//定义word
XWPFDocument doc = new XWPFDocument();
//在word中创建一个表格(2行,3列)
XWPFTable table = doc.createTable(2,3);
//第一行数据
table.getRow(0).getCell(0).setText("1");
table.getRow(0).getCell(1).setText("2");
table.getRow(0).getCell(2).setText("3");
//这样会完全克隆上 row=0 行数据,连数据都一样
table.addRow(table.getRow(0),1);
//开始修改新创建的行的数据
table.getRow(1).getCell(0).setText("99");
table.getRow(1).getCell(1).setText("88");
table.getRow(1).getCell(2).setText("77");
//同时,表格行和列的数量变了
table.getRow(2).getCell(0).setText("4");
table.getRow(2).getCell(1).setText("5");
table.getRow(2).getCell(2).setText("666666");
//导出
String path = "C:\\"; //文件路径
String name = "test1"; //文件名
path = path + "/" + name + ".docx";
File file = new File(path);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream out = new FileOutputStream(file);
doc.write(out);
}catch (IOException e){
e.printStackTrace();
}
}
如果我们直接通过getRow作为复制行,然后修改行数据,实际上修改的是被复制行的数据。
效果如下:
可以看出,原来表头是 1 2 3 那行数据,我们复制那行数据,然后修改,发现表头被修改了,然后新复制出来的那行没有被修改。
比较推荐的复制行方法是:
CTRow ctrow = CTRow.Factory.parse(workbook.getRow(12).getCtRow().newInputStream()); //复制一行
错误用法2:操作后再插入数据会导致修改内容失败
比如下面这样:
void testXWPFTable1(){
System.out.println("开始执行");
try {
//定义word
XWPFDocument doc = new XWPFDocument();
//在word中创建一个表格(2行,3列)
XWPFTable table = doc.createTable(2,3);
//第一行数据
table.getRow(0).getCell(0).setText("1");
table.getRow(0).getCell(1).setText("2");
table.getRow(0).getCell(2).setText("3");
CTRow ctrow = null;
try {
ctrow = CTRow.Factory.parse(table.getRow(0).getCtRow().newInputStream());
} catch (XmlException e) {
e.printStackTrace();
}
XWPFTableRow copyRow = new XWPFTableRow(ctrow, table);
table.addRow(copyRow,1);
//开始修改新创建的行的数据
table.getRow(1).getCell(0).setText("99");
table.getRow(1).getCell(1).setText("88");
table.getRow(1).getCell(2).setText("77");
//同时,表格行和列的数量变了
table.getRow(2).getCell(0).setText("4");
table.getRow(2).getCell(1).setText("5");
table.getRow(2).getCell(2).setText("666666");
//导出
String path = "C:\\"; //文件路径
String name = "test1"; //文件名
path = path + "/" + name + ".docx";
File file = new File(path);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream out = new FileOutputStream(file);
doc.write(out);
}catch (IOException e){
e.printStackTrace();
}
}
效果:
可以看出新插入的行的数据即使在代码中设置数据也没有生效。
正确用法:先复制再设置内容最后插入
void testXWPFTable1(){
System.out.println("开始执行");
try {
//定义word
XWPFDocument doc = new XWPFDocument();
//在word中创建一个表格(2行,3列)
XWPFTable table = doc.createTable(2,3);
//第一行数据
table.getRow(0).getCell(0).setText("1");
table.getRow(0).getCell(1).setText("2");
table.getRow(0).getCell(2).setText("3");
CTRow ctrow = null;
try {
ctrow = CTRow.Factory.parse(table.getRow(0).getCtRow().newInputStream());
} catch (XmlException e) {
e.printStackTrace();
}
XWPFTableRow copyRow = new XWPFTableRow(ctrow, table);
//设置内容
copyRow.getCell(0).setText("99");
copyRow.getCell(1).setText("88");
copyRow.getCell(2).setText("77");
//插入
table.addRow(copyRow,1);
//同时,表格行和列的数量变了
table.getRow(2).getCell(0).setText("4");
table.getRow(2).getCell(1).setText("5");
table.getRow(2).getCell(2).setText("666666");
//导出
String path = "C:\\"; //文件路径
String name = "test1"; //文件名
path = path + "/" + name + ".docx";
File file = new File(path);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream out = new FileOutputStream(file);
doc.write(out);
}catch (IOException e){
e.printStackTrace();
}
}
效果:
2.问题
除了上面说到的遇到的实际问题,还遇到了其他问题,比如我们复制行数据后,使用setText方法对新行设置数据,发现原来的数据没有被清除,而是在同一个单元格进行了追加。(实际操作中,发现在代码中创建的新表格没有这个问题,而在代码中读取word中预创建的表格会有这个问题)
这样的话上面的表格会变成
此时我们可以用下面的代码清除某个单元格的数据
copyRow.getTableCells().get(j).getCTTc().setPArray(new CTP[] {CTP.Factory.newInstance()}); //清除单元格信息
其中上面的代码中的j表示某个列。