Java 怎样解析和生成 xls?
POI提供了全面的功能用以解析和生成xls,但它提供的API过于底层,即使一些简单的读写动作,也要编写大量代码从头写起。全面就意味着要照顾的细节多,各种任务都会显得非常繁琐,硬写代码的量很大。解析和生成的重点通常是数据,但POI在字体、颜色、粗细、对齐等大量的外观属性上投入了大量精力,相对地,数据的进一步处理能力反而并不专业,也只能在外围自己硬编码实现。
一个想法是将poi封装一下,提供更为简洁并擅长数据处理的接口,集算器SPL就是这样的开源库。
对于格式规则的行式xls,SPL的T函数可以用最简洁的代码读取。比如,Excel文件的每行是一条订单记录,首行是列名,读取该文件只需一行代码:
=T("D:/Orders.xls")
对于格式较不规则的行式xls,SPL的xlsimport函数提供了丰富简洁的读取功能。下面举一些常见的例子:
#没有列名,首行直接是数据:
=file("D:/Orders.xlsx").xlsimport()
#跳过前2行的标题区:
=file("D:/Orders.xlsx").xlsimport@t(;,3)
#从第3行读到第10行:
=file("D:/Orders.xlsx").xlsimport@t(;,3:10)
#只读取其中3个列:
=file("D:/Orders.xlsx").xlsimport@t(OrderID,Amount,OrderDate)
#读取名为"sheet3"的特定sheet:
=file("D:/Orders.xlsx").xlsimport@t(;"sheet3")
函数xlsimport还具有读取倒数N行、密码打开文件、读大文件等功能,这里不再详述。
SPL提供了JDBC接口,可以在JAVA里轻松调用。比如解析记录并进行计算:
…
Class.forName("com.esproc.jdbc.InternalDriver");
Connection connection =DriverManager.getConnection("jdbc:esproc:local://");
Statement statement = connection.createStatement();
String str="=T(\"D:/Orders.xls\").select(Amount>1000 && Amount<=3000 && like(Client,\"*S*\"))";
ResultSet result = statement.executeQuery(str);
…
SPL也擅长对xls进行查询、分组、关联等计算,但那是另一个话题,感兴趣可参考《SPL Excel 计算示例》
对于格式很不规则的xls,SPL的xlscell函数提供了灵活而简洁的读取功能。xlscell的基本功能是读写指定sheet里指定片区的数据,比如读取第1个sheet里的A2格:
=file("d:/orders.xlsx").xlsopen().xlscell("C2")
配合SPL灵活的语法,就可以解析自由格式的xls,比如将下面的文件读为规范的二维表(序表):
这个文件格式很不规则,对POI来说是个浩大的工程,而SPL代码就简短得多:
A |
B |
C |
|
1 |
=create(ID,Name,Sex,Position,Birthday,Phone,Address,PostCode) |
||
2 |
=file(“e:/excel/employe.xlsx").xlsopen() |
||
3 |
[C,C,F,C,C,D,C,C] |
[1,2,2,3,4,5,7,8] |
|
4 |
For |
=A3.(~/B3(#)).(A2.xlscell(~)) |
|
5 |
if len(B4(1))==0 |
Break |
|
6 |
>A1.record(B4) |
||
7 |
>B3=B3.(~+9) |
上面的SPL代码以脚本文件的形式外置于JAVA代码,修改时无需编译,可大幅降低耦合性,适合较复杂或频繁修改的解析或计算过程。JAVA代码可以用存储过程的方式调用上面的脚本文件:
…
Class.forName("com.esproc.jdbc.InternalDriver");
Connection connection =DriverManager.getConnection("jdbc:esproc:local://");
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery("call getEmps()");
...
SPL提供了专业的IDE,适合设计逻辑复杂的解析过程,不仅具备完整的调试功能,还可以观察每一步的中间结果。
说完解析,再讲讲Excel的生成。
SPL可用简洁的代码生成xls,掌握起来非常容易。比如上面例子的解析结果是个序表,存在SPL的A1格中,下面将A1写入xls的第一个sheet,首行为列名,只需一句代码:
A |
B |
C |
|
… |
|||
8 |
=file("e:/result.xlsx").xlsexport@t(A1) |
SPL提供了丰富的生成功能,可以将序表写入指定sheet,或只写入序表的部分行,下面是个具体例子,将序表中指定的列写入Excel。:
=file("e:/scores.xlsx").xlsexport@t(A1,No,Name,Class,Maths)
SPL可以方便地追加数据,比如对于已经存在且有数据的xls,将序表A1追加到该文件末尾,风格与末行保持一致:
=file("e:/scores.xlsx").xlsexport@a(A1)
前面提到的xlscell函数不仅有读取功能,也具有同样灵活简洁的写入功能,可以在不规则不连续的指定单元格中填入数据。比如,上级发来的xls中蓝色单元格是不规则的表头,下级需要在相应的白色单元格中填入数据,填好之后应当如下:
要实现上面不规则的数据填入,POI要大段冗长的代码,而SPL代码就简短许多(其中A1: F5格是待填的数据):
A |
B |
C |
D |
E |
F |
|
1 |
Mengniu Funds |
2017 |
3 |
58.2 |
364 |
300 |
2 |
8.5 |
50 |
200 |
100 |
400 |
200 |
3 |
182.6 |
76.3 |
43.7 |
28.5 |
16.4 |
|
4 |
120 |
1.07 |
30 |
0.27 |
90 |
0.8 |
5 |
154 |
6 |
4 |
|||
6 |
=file("e:/result.xlsx") |
=A6.xlsopen() |
||||
7 |
=C6.xlscell("B2",1;A1) |
=C6.xlscell("J2",1;B1) |
=C6.xlscell("L2",1;C1) |
|||
8 |
=C6.xlscell("B3",1;D1) |
=C6.xlscell("G3",1;E1) |
=C6.xlscell("K3",1;F1) |
|||
9 |
=C6.xlscell("B6",1;[A2:F2].concat("\t")) |
=C6.xlscell("H6",1;[A3:E3].concat("\t")) |
||||
10 |
=C6.xlscell("B9",1;[A4:F4].concat("\t")) |
=C6.xlscell("B11",1;[A5:C5].concat("\t")) |
||||
11 |
=A6.xlswrite(B6) |
注意,第6、9、11行有连续单元格,SPL可以简化代码一起填入,POI只能依次填入。
总之,在解析和生成xls方面,经过精心封装的SPL要比POI更方便更简洁,在读写不规则文件时,这种优势尤其明显。