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表示某个列。