ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

政府项目里Word文档的表格怎么读进系统——POI双格式解析

政府项目里Word文档的表格怎么读进系统——POI双格式解析 政府项目里Word文档的表格怎么读进系统——Apache POI双格式解析项目申报书、评审表、统计数据——政府项目里大量数据以Word表格的形式存在。手工录入费时还容易错。这篇文章记录了一个同时支持 .doc 和 .docx 的Word表格提取器三行调用把所有表格读进List直接对接数据库入库。文章目录政府项目里Word文档的表格怎么读进系统——Apache POI双格式解析一、为什么需要解析Word表格二、解析docx——新格式三、解析doc——旧格式多了一个坑四、统一入口——自动判断格式五、从表格到数据库六、结语一、为什么需要解析Word表格政务项目里外部数据常常长这样项目申报书申报单位信息、预算明细、人员名单——全在Word表格里评审意见表十几位专家的打分表每位专家一个Word文档统计数据上级部门下发的统计报表Word格式几百行数据这些数据如果手工录进系统一个申报书几十个字段录错了还得返工。最省事的办法——写一个工具类把Word里的表格直接解析成结构化的数据再批量入库。二、解析docx——新格式importorg.apache.poi.xwpf.usermodel.XWPFDocument;importorg.apache.poi.xwpf.usermodel.XWPFTable;importorg.apache.poi.xwpf.usermodel.XWPFTableCell;importorg.apache.poi.xwpf.usermodel.XWPFTableRow;importjava.io.InputStream;importjava.util.ArrayList;importjava.util.List;publicstaticListArrayListArrayListStringdocx(InputStreamfis){ListArrayListArrayListStringtablesnewArrayList();try{XWPFDocumentdocumentnewXWPFDocument(fis);ListXWPFTabletlistdocument.getTables();for(XWPFTableobj:tlist){ListXWPFTableRowrowsobj.getRows();ArrayListArrayListStringtablenewArrayList();for(XWPFTableRowrow:rows){ListXWPFTableCellcellsrow.getTableCells();ArrayListStringrnewArrayList();for(XWPFTableCellcell:cells){r.add(cell.getText());}table.add(r);}tables.add(table);}fis.close();}catch(IOExceptione){e.printStackTrace();}returntables;}三层循环表→行→单元格。每层把数据塞进一个ArrayList最终返回List表行单元格文本。这个嵌套结构调用方用起来很直观——取第一个表第3行第2列tables.get(0).get(2).get(1)。三、解析doc——旧格式多了一个坑importorg.apache.poi.hwpf.HWPFDocument;importorg.apache.poi.hwpf.usermodel.*;importorg.apache.poi.poifs.filesystem.POIFSFileSystem;importjava.io.InputStream;importjava.util.ArrayList;importjava.util.List;publicstaticListArrayListArrayListStringdoc(InputStreamfis){ListArrayListArrayListStringtablesnewArrayList();try{POIFSFileSystempfsnewPOIFSFileSystem(fis);HWPFDocumentdocumentnewHWPFDocument(pfs);Rangerangedocument.getRange();TableIteratoritnewTableIterator(range);while(it.hasNext()){Tabletb(Table)it.next();ArrayListArrayListStringtablenewArrayList();for(inti0;itb.numRows();i){TableRowtrtb.getRow(i);ArrayListStringrnewArrayList();for(intj0;jtr.numCells();j){TableCelltdtr.getCell(j);Stringtext;// 单元格内可能有多个段落for(intk0;ktd.numParagraphs();k){Paragraphparatd.getParagraph(k);Stringspara.text();// 去除段落末尾的控制字符if(null!s!.equals(s)){ss.substring(0,s.length()-1);}texts;}r.add(text);}table.add(r);}tables.add(table);}fis.close();}catch(IOExceptione){e.printStackTrace();}returntables;}.doc格式比.docx复杂两处第一表格遍历方式不同。.docx直接document.getTables()返回所有表格。.doc要先获取Range再用TableIterator遍历——API设计年代更早不够直观。第二也是最关键的——段落末尾的控制字符。.doc格式里每个段落的文本末尾有一个不可见控制字符。直接用para.text()取出来的字符串尾巴上带着这个字符——入库后字段值后面多一个乱码比对了半天不知道差在哪。所以第87行这一刀是必须的ss.substring(0,s.length()-1);这是靠打印每一个字符的Unicode码值才发现的。不加这行数据永远读不干净。四、统一入口——自动判断格式importjava.io.FileInputStream;importjava.io.FileNotFoundException;publicstaticvoidmain(String[]args)throwsFileNotFoundException{StringfilenameD:\\项目申报书.doc;FileInputStreamfisnewFileInputStream(filename);ListArrayListArrayListStringtables;if(filename.endsWith(.docx)){tablesdocx(fis);}else{tablesdoc(fis);}// 打印所有表格数据if(tables!null){for(inti0;itables.size();i){System.out.println( 表i );for(intm0;mtables.get(i).size();m){System.out.print(行m:);for(intn0;ntables.get(i).get(m).size();n){System.out.print(\ttables.get(i).get(m).get(n));}System.out.println();}}}}通过文件名后缀自动分派到docx()或doc()。调用方不用关心Word是什么版本——传入文件流返回解析好的表格数据。五、从表格到数据库解析出来的ListListListString直接对接入库// 假设第一个表是申报单位信息表头在第一行数据在后续行ListArrayListStringtabletables.get(0);for(inti1;itable.size();i){// 跳过表头行ArrayListStringrowtable.get(i);StringunitNamerow.get(0);// 单位名称StringunitCoderow.get(1);// 单位代码Stringcontactrow.get(2);// 联系人Stringphonerow.get(3);// 联系电话// 入库dao.setUnitName(unitName);dao.setUnitCode(unitCode);dao.insert();}表结构稳定时列号固定直接按索引取值入库。表结构不固定时第一行是表头可以用表头文本匹配列号——单位名称在第几列就取哪一列。六、结语这套工具类的核心价值不是代码多复杂——是省掉手工录Word表格的时间。一个项目申报书几十个字段手工录入要半小时还容易录错。用这套工具读到内存里再写一段入库的逻辑五分钟整个流程自动化。Apache POI对两种Word格式的API差异不小但核心思路都是套三层循环。唯一需要特别注意的就是.doc格式的那个段落末尾控制字符——不是文档里写的字符是格式存储方式产生的副作用。不加那一刀数据读出来永远有点脏。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进