Excel 文件的解析和生成
Excel文件是非常常见的数据文件,在数据处理业务中,经常要把Excel文件数据解析成结构化数据,或者把处理好的结构化数据保存到Excel文件中。在Java中可以使用Apache Poi开源包读写Excel单元格数据。POI虽然强大,但有一定的学习曲线。许多Excel文件的格式并不规整,而且文件结构也多种多样,导致编程读写的工作量会比较大,而且很难通用,每次都要针对文件格式进行分析后再进行开发。
Open esProc开源包提供了读写Excel文件数据的功能,比POI更简便易用,它的SPL脚本语法简洁,只需一两句代码就可完成。
1. 解析
SPL用file函数打开Excel文件,用xlsimport函数将数据读成序表或游标。当文件数据不大,能够全部装入内存时,将数据读成序表;当文件数据很大,可以将数据读成游标以减少内存占用。SPL还可以只读入指定单元格数据,或某片区单元格数据。
下面是一些Excel文件读取的例子。
1.1 简单行式文件
行式Excel文件每行对应一条记录,各行有相同的列,相当于数据库中的一张数据表。
=file("e:/scores.xlsx").xlsimport@t()
file函数的参数可以是文件的绝对路径,也可以是相对路径(相对于SPL配置文件中的主目录mainPath),xlsimport函数默认读取文件中第1个Sheet所有行列数据生成序表,选项@t表示第1行是标题行,此行的列值为对应序表列的名称;若无选项@t,表示无标题行,此时用#1、#2等序号方式来引用列。
1.2 读取部分数据
=file("e:/scores.xlsx").xlsimport@t(No,Name,Class,Maths)
指定了列名参数,表示只读取No,Name,Class,Maths列的数据。
=file("e:/scores.xlsx").xlsimport@t(;,1:100)
读取文件中第1个Sheet中第1行到第100行的所有列数据。
=file("e:/scores.xlsx").xlsimport@t(;,1:-10)
读取文件中第1个Sheet中第1行到倒数第10行的所有列数据。负数表示倒数的行序号。
1.3 读取指定 Sheet
=file("e:/scores.xlsx").xlsimport@t(;"School2")
表示读取名称为School2的Sheet中所有列数据。
1.4 读取 Sheet 信息
Excel文件中有多个Sheet,SPL提供xlsopen函数将文件打开生成一个Excel对象,它是一个由各Sheet信息组成的序表。
=file("e:/scores.xlsx").xlsopen()
打开后的Excel对象如下图:
每行表示一个Sheet,stname、nrows、ncols分别表示Sheet的名称、数据行数、数据列数。
1.5 自由格式
一条数据记录分散在多行,字段列值单元格紧跟在列名单元格后面或下面,可能有跨列或跨行的合并单元格。但每条记录所占的行数以及对应行结构是相同的。循环读取时要以每条记录所占行数为单位组成一条记录。
读取Excel指定单元格数据时,使用SPL中的xlsopen函数将文件读成Excel对象,再调用此对象的xlscell函数。
示例:自由格式的员工信息文件employee.xlsx中,部分数据如下图:
读取数据的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) |
A1 创建列名为“ID、Name、Sex、Position、Birthday、Phone、Address、PostCode”的空序表
A2 打开Excel数据文件,生成一个Excel对象
A3 定义雇员信息所在单元格列号序列
B3 定义雇员信息所在单元格行号序列
A4 用for循环读取每个雇员信息
B4 A3.(~/B3(#))先算出当前雇员单元格编号序列,再读出这些单元格值组成雇员信息序列。第一次循环时为[C1,C2,F2,C3,C4,D5,C7,C8],第二次循环时为[C10,C11,F11,C12,C13,D14,C16,C17]……每次行号加9。
B5 判断雇员ID值是否为空,为空则退出循环,结束读数
B6 将一条雇员信息存入A1序表尾
B7 让雇员信息的行号序列都加上9,读取下一条雇员信息
读取出来的A1单元格数据如下图所示:
更多Excel文件解析介绍请参考《SPL:Excel文件读写》
2. 生成
SPL用file函数打开Excel文件,然后用xlsexport函数将序表或游标数据写入到此文件中。用xlsopen将文件打开成Excel对象,再调用此对象的xlscell函数,可以将数据写入指定的单元格。
下面是一些写入Excel文件的例子。
2.1 简单行式文件
假设SPL脚本文件中的A1单元格中是要写入Excel文件的序表数据。
=file("e:/scores.xlsx").xlsexport@t(A1)
表示将序表A1中的数据全部写入scores.xlsx文件的第1个Sheet中,选项@t表示在第1行写入列名称,即第1行为标题行。没有此选项则不写列标题。
=file("e:/scores.xlsx").xlsexport@t(A1,No,Name,Class,Maths)
指定了列名参数,表示只将A1中的No,Name,Class,Maths列数据写入Excel文件。
2.2 写入指定 Sheet
=file("e:/scores.xlsx").xlsexport@t(A1;2)
表示将序表A1中的数据全部写入scores.xlsx文件的第2个Sheet中。
=file("e:/scores.xlsx").xlsexport@t(A1; "School2")
表示将序表A1中的数据全部写入scores.xlsx文件Sheet名为School2的工作表中。
2.3 追加数据及预定格式
=file("e:/scores.xlsx").xlsexport@a(A1)
选项a表示文件已经存在时,将序表A1中的数据全部追加写入scores.xlsx文件第1个Sheet末尾,同时沿用最后一行的格式。
当需要写出有预定格式的Excel数据文件时,可以事先建好文件,在文件的最后一行各列定义好本列的数据格式。在写入数据时,采用追加写入选项a,就会按照预定的格式写入数据了。
例如定义格式文件如下:
然后删除第2行数据,使之成为空行。追加写入时,会将最后一个非空行当做标题行,其下一行作为数据行的格式。写入数据后的文件如下:
2.4 固定行列填充
有一个行列数和单元格格式都固定的Excel文件,里面有些空单元格,需要把相关数据填写到这些格子里。这时要使用SPL中的xlsopen函数将文件读成Excel对象,再调用此对象的xlscell函数往指定格写入数据。
示例:某基金公司总公司向分公司下发了一张excel表格,要求分公司填入它的相关数据后回传给总公司,下发的excel文件如下:
填写数据的SPL脚本如下:
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:/excel/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(C6) |
假定要填的数据已计算好(在前5行)。样表中前6个要填的单元格都是独立的,所以只能每次填一个格,第6行有连续填写的单元格,此时把待填数据拼成以\t分隔的字符串,这样可以从指定单元格开始顺序填充。数据填完以后,再把C6的Excel对象写回到result.xlsx文件中。
填写完成以后的result.xlsx如下图所示:
更多Excel文件生成介绍请参考《SPL:Excel文件读写》
英文版