300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 关于JAVA POI解析WPS docx文档中的table(复杂表格包含单元格横向 纵向的合并)

关于JAVA POI解析WPS docx文档中的table(复杂表格包含单元格横向 纵向的合并)

时间:2022-12-07 00:00:31

相关推荐

关于JAVA POI解析WPS docx文档中的table(复杂表格包含单元格横向 纵向的合并)

关于JAVA POI解析WPS docx文档中的table(复杂表格包含单元格横向,纵向的合并)

首先,关于poi解析表格先阅读一篇他人的博客

使用poi读取word(.docx)中的复杂表格.

这篇博客提到了,如何用poi将单元格合并。

static void mergeCellHorizontally(XWPFTable table, int row, int fromCol, int toCol) {for(int colIndex = fromCol; colIndex <= toCol; colIndex++){CTHMerge hmerge = CTHMerge.Factory.newInstance();if(colIndex == fromCol){// The first merged cell is set with RESTART merge valuehmerge.setVal(STMerge.RESTART);} else {// Cells which join (merge) the first one, are set with CONTINUEhmerge.setVal(STMerge.CONTINUE);}XWPFTableCell cell = table.getRow(row).getCell(colIndex);// Try getting the TcPr. Not simply setting an new one every time.CTTcPr tcPr = cell.getCTTc().getTcPr();if (tcPr != null) {tcPr.setHMerge(hmerge);} else {// only set an new TcPr if there is not one alreadytcPr = CTTcPr.Factory.newInstance();tcPr.setHMerge(hmerge);cell.getCTTc().setTcPr(tcPr);}}}

记住CTTcPr 这个类,他包装了单元格的格式信息

CTTcPr tcPr = cell.getCTTc().getTcPr();

但是,当我开始解析wps的docx后发现横向合并的单元格信息根本拿不到。就是HMerge这个对象。一直为空。VMerge倒是没问题。

打开docx文件的xml文件一看发现了问题

<w:tc><w:tcPr><w:tcW w:w="1000" w:type="dxa"/><w:hMerge w:val="restart"/></w:tcPr><w:p><w:r><w:t>row 1, col 2</w:t></w:r></w:p></w:tc>

这是我们期望的格式

<w:tc><w:tcPr><w:tcW w:w="1000" w:type="dxa"/><w:gridSpan w:val="2"/></w:tcPr><w:p><w:r><w:t>row 1, col 2</w:t></w:r></w:p></w:tc>

wps docx打开是这样的。区别在于hMerge 和gridSpan

hMerge不释放td对象。比如一行5列。1-2合并

那么在poi里得到5个cell对象。1的hMerge枚举为started。2为continue。

gridSpan反之。1-2合并,只能拿到4个cell。

虽然费了一般波折,总算可以解析出来了。废话不多

说。先看效果,再上代码

这是docx文档中的表格

这是解析,并用html生成的table

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.junit.Test;import org.openxmlformats.schemas.wordprocessingml.x.main.CTTcPr;import org.openxmlformats.schemas.wordprocessingml.x.main.STMerge;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;/*** poi 解析wps docx中的table*/public class poiTest {@Testpublic void poiTest() throws IOException {File file = new File("C:\\Users\\xxx\\xxx\\xxx.docx");FileInputStream fis = new FileInputStream(file);XWPFDocument document = new XWPFDocument(fis);List<XWPFTable> tables = document.getTables();XWPFTable table = tables.get(0);List<XWPFTableRow> rows = table.getRows();/*** 声明一个集合数组。* 数组代表行,集合代表列* map装解析出来的单元格对象*/List<Map<String, Object>>[] tableParse = new ArrayList[rows.size()];/*** 解析横向单元格合并*/hMerger(tableParse,rows);/*** 解析纵向单元格合并*/vMerger(tableParse,rows);System.out.println(excelToHtml(tableParse));}/*** tcPr.getVMerge().getVal() == STMerge.CONTINUE* 当某个单元格的vMerge属性为STMerge.CONTINUE时* 代表是被合并的纵向单元格** @param tableParse* @param c 列下标* @param r 行下标*/private void vMergeAdd(List<Map<String, Object>>[] tableParse,int c, int r) {for (int a = r-1; a >= 0; a--) {Map<String, Object> cell = tableParse[a].get(c);if(cell.get("type").equals(1)){cell.put("row",(int)cell.get("row")+1);return;}}}/*** 拼装成html字符串* @param tableParse* @return*/private String excelToHtml(List<Map<String, Object>>[] tableParse){StringBuilder sb = new StringBuilder("<table border='1px' border-color='grey' style='width:100%'>");for(int r = 0;r<tableParse.length;r++){sb.append("<tr>");List<Map<String,Object>> cells = tableParse[r];for(int c = 0; c< cells.size();c++){tdMaker(sb,cells.get(c));}sb.append("</tr>");}sb.append("</table>");return sb.toString();}/*** 生成td字符串* @param sb* @param cell*/private void tdMaker(StringBuilder sb,Map<String,Object> cell){if((int)cell.get("type")==1){sb.append("<td");if(cell.get("col")!=null){sb.append(" colspan='"+cell.get("col")+"'");}if(cell.get("row")!=null){sb.append(" rowspan='"+cell.get("row")+"'");}sb.append(">");sb.append(cell.get("text"));sb.append("</td>");}}/*** 如果有单元格横向的合并。会释放td位置* 比如一行五列,1-2合并。poi只能解析到1,3,4,5四个cell* 为了方便纵向单元格合并的解析。补上被合并的td对象** @param tableParse* @param rows*/private void hMerger(List<Map<String, Object>>[] tableParse,List<XWPFTableRow> rows){for (int r = 0; r < rows.size(); r++) {tableParse[r] = new ArrayList<>();XWPFTableRow row = rows.get(r);List<XWPFTableCell> tableCells = row.getTableCells();for (int c = 0; c < tableCells.size(); c++) {Map<String, Object> cellMap = new HashMap<>();tableParse[r].add(cellMap);XWPFTableCell cell = tableCells.get(c);cellMap.put("text", cell.getText());cellMap.put("type",1);CTTcPr tcPr = cell.getCTTc().getTcPr();if (tcPr.getGridSpan() != null) {cellMap.put("col", tcPr.getGridSpan().getVal());for(int g = 0;g<tcPr.getGridSpan().getVal().intValue()-1;g++){Map<String,Object> visCell = new HashMap<>();visCell.put("type",0);tableParse[r].add(visCell);}}}}}/*** 纵向单元格合并的解析* 纵向单元格合并的解析比较简单。* 如果存在vMerge的val为STMerge。CONTINUE* 表明是前面行的合并单元格** @param tableParse* @param rows*/private void vMerger(List<Map<String, Object>>[] tableParse,List<XWPFTableRow> rows){for (int r = 0; r < rows.size(); r++) {XWPFTableRow row = rows.get(r);List<XWPFTableCell> tableCells = row.getTableCells();for (int c = 0; c < tableCells.size(); c++) {XWPFTableCell cell = tableCells.get(c);CTTcPr tcPr = cell.getCTTc().getTcPr();if (tcPr.getVMerge() != null) {if (tcPr.getVMerge().getVal() == STMerge.RESTART) {tableParse[r].get(c).put("row", 1);} else if(tcPr.getVMerge().getVal() == STMerge.CONTINUE){tableParse[r].get(c).put("type", 0);vMergeAdd(tableParse, c, r);}}}}}}

代码注释有两个关键点可能没说明白。第一,为什么要补虚拟单元格。

因为gridSpan合并的单元格只有一个cell。假设3*5的表格,如果第一行1-2-3合并。那么第一行的第二个单元会对应第二行的第四个单元格。如果恰好第一行第二个单元格和第二行第四个单元格合并。那么在做纵向单元格合并解析的时候不好处理。

第二,纵向单元格合并解析的思路。当遍历单元格的时候,如果找到vMerge的值为STMerge.STARTED说明这个单元有纵向合并,row值set1。当vMerge的值为STMerge.CONTINUE时说明这个单元被纵向合并。找上一行同列的单元格row值。如果不为空则row值加一。本单元格type标记为0(表示不生成td)。因为做横向单元格解析时已经补过虚拟单元格。所以在纵向解析时,纵向合并的单元格一定是列下标相同的。另,windows office和wps office还是有少许区别的。

注 : 这是我的第二篇blog。大佬总说要写blog。我觉的如果能百度到别人的答案就没必要写下来。但是这次的问题百度了很久,最后靠自己解决的,所以写下来,希望能帮到大家

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。