点击量:4129
近期应一个妹子的邀请做一些跟了一些和Word,excel相关的读写研究。需求很简单,就是从excel表格里读取一些白名单,然后在Word表格里找到这些匹配项再做相应的处理。因为每次手动操作很麻烦,所以呢妹子就找到了我,问下我能不能写个程序来做这件事。我一想这玩意儿应该挺简单的啊,肯定有开源的库来读写excel和Word,而且能提高妹子的工作效率,所以就欣然答应下来了。
之后就Google了一番看看有木有什么开源的库可以用,果不其然第一个就是Apache的POI项目,然后去官网看API文档。总的来说这个库使用起来还是蛮方便,下面介绍下读写Word和excel的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
String path = "whitelist.xlsx"; Workbook wb = null; FileInputStream fis = null; try { fis = new FileInputStream(path); wb = WorkbookFactory.create(fis); } catch (EncryptedDocumentException | InvalidFormatException | IOException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("System hault,can't open file:"+path); } //get first sheet Sheet sheet = wb.getSheetAt(0); int first = sheet.getFirstRowNum(); int last = sheet.getLastRowNum(); //traverse rows for(int i=first; i<=last;i++){ //read row data Row row = sheet.getRow(i); if(row == null){ continue; } //read first cell Cell cell = row.getCell(0); if(null == cell){ continue; } //only read STRING & INTEGER String content = null; if(cell.getCellType() == Cell.CELL_TYPE_STRING){ content = cell.getStringCellValue(); }else if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){ content = cell.getNumericCellValue() + ""; }else { continue; } //do something... } try { //close stream and workbook wb.close(); fis.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } |
这个只是读取excel的代码,值得一提是由WorkbookFactory创建出来的Workbook是支持xls和xlsx两种格式的文档,开发者不再需要区分是03还是07格式的excel文件,非常方便。然后读取Word文档却不像读取excel这么方便,他有两套API,一个是HWPFDocument只支持03格式的Word(后缀:doc)还有一个是XWPFDocument 只支持07格式的word(后缀:docx),这两个名字看起来差不多实则使用方法却不一样,下面介绍下读写doc中表格的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
String path = "test.doc"; //file input stream FileInputStream fis = null; try { fis = new FileInputStream(path); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("System hault,can't open file:"+path); } POIFSFileSystem pfs = null; try { pfs = new POIFSFileSystem(fis); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("System hault,invalid word file"); } HWPFDocument hwdf = null; try { hwdf = new HWPFDocument(pfs); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); throw new RuntimeException("System hault,HWPFDocument read exception"); } //readable ranges Range range = hwdf.getRange(); //find all tables TableIterator tableIterator = new TableIterator(range); while(tableIterator.hasNext()){ Table table = tableIterator.next(); //get first cell text of the first row String text = table.getRow(0).getCell(0).text(); //do something with title... //read all rows int end = table.numRows(); for(int i=0;i<end;i++){ TableRow row = table.getRow(i); if(null ==row){ continue; } TableCell cell = row.getCell(0); if(null == cell){ continue; } String content = cell.text(); if(null == content){ continue; } //do something... } } //output stream FileOutputStream fos = null; try { fos = new FileOutputStream(new File("output.doc")); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } //write to output stream try { hwdf.write(fos); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { pfs.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { fis.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } |
自己新建了一个excel和word文档,测试通过,everything goes right,简直so easy嘛。但是当妹子把要处理的文档发给我的时候却遇到了一问题,POI在读取的时候报了一个错:
1 2 3 4 5 |
org.apache.poi.poifs.filesystem.NotOLE2FileException: Invalid header signature; read 0x6576206C6D783F3C, expected 0xE11AB1A1E011CFD0 - Your file appears not to be a valid OLE2 document at org.apache.poi.poifs.storage.HeaderBlock.<init>(HeaderBlock.java:162) at org.apache.poi.poifs.storage.HeaderBlock.<init>(HeaderBlock.java:112) at org.apache.poi.poifs.filesystem.NPOIFSFileSystem.<init>(NPOIFSFileSystem.java:302) at org.apache.poi.poifs.filesystem.POIFSFileSystem.<init>(POIFSFileSystem.java:86) |
看报错应该是word文档的格式不对了,怀疑是给出的文档不规范,不是标准的doc文档,所以我又另存为doc文档,然后就可以读取了也成功的生成了output.doc文档,诡异的是使用office打开这个文件时报错了。既然可以读进来了为什么写进去的文档不能被打开呢?
刚开始的时候怀疑是API使用不正确,但是新建一个word文档却没有问题。所以问题就出在了妹子给出的这个文档上面了(这个文档也是程序生成的)。那这个文档跟普通的doc文档到底有什么不一样呢?因为output是由原始文件生成的,所以又把目光锁定到了之前那个报错上面。invalid header signature到底读到了什么?把0x6576206C6D783F3C翻译成文本字符串就是:ev lmx?<,这是啥感觉有点像xml啊。再进一步直接用vim打开这个word文档发现前两行是这样的:
1 2 |
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?mso-application progid="Word.Document"?> |
再往后看发现这就是一个xml文件啊,后来查阅资料才知道还有另一种office的格式叫OOXML,全称是Office Open XML,由微软公司为Office 2007产品开发的技术规范,现已成为国际文档格式标准,兼容前国际标准开放文档格式和中国文档标准“标文通”(外语简称:UOF)。说白了就是基于XML的word,excel和PowerPoint。据此可以推测妹子的文档很可能是使用freemarker导出XML到word的。但是xml文件怎么了呢?为什么POI就不能打开呢?又去官网看了一圈,发现现在的POI并不支持OOXML格式的文档,他们这样说道:
There is a different format for the XML-based file formats of the Microsoft Office suite which Apache POI does not support yet.
虽然office的产品能兼容这种格式的文档,但是这种标准的文档读写方式是不同于普通的word文档,api层面需要重新写一套来用,遗憾的是POI并不支持,他们项目组还在招募人员开发这个项目。
原因到此就清楚了,但怎么解决这个问题呢?要么就自己解析XML文件,但是这个XML文档的标签实在太多了,解析起来相当麻烦。还有就是让妹子多费点事把表格copy到一个新的.doc文档中也能解决问题,但是并不完美。