按组处理大文件(结构化文本)

有这样一类文本文件:文件太大无法全部读入内存计算;但数据已按某列排序,如果以该列为标准每次读取一组数据,则可以放入内存进行计算。电信通话记录、网站访问记录、商场会员信息等等都属于此类文件。

JAVA实现此算法需要编写大量代码,过程复杂难以维护。使用集算器来辅助Java编程,这类问题就轻松许多。下面我们通过例子来看一下具体作法。

文本文件sOrder.txt存储着大量的订单信息,以tab为分隔符,第一行是列名,数据已按SellerId排序。现在需要每次读入SellerId相同的一组数据,并对每组数据执行同样的数据处理过程。

 

文件sOrder.txt的部分数据如下:



 

集算器脚本如下:



 

 A1:函数cursor以游标方式打开文件,默认的分割符是tab,函数选项@t表示将第一行读为列名。如果只读取前四列,且分割符是逗号,则应当写作:[email protected](OrderID, Client, SellerId, Amount; ”,”)

A2for A1 ;SellerId。对游标A1进行循环读数,每次读入SellerId相同的一组数据,此时数据才会真正读入内存。

注意这里的for 语句。集算器有for cs,n这样的写法,这表示每次读入游标cs中的n条记录。而for cs;x表示每次读入游标cs中的一组记录,每组记录的x字段相同,数据需要按照x事先排序。本案例的数据已经排过序了,如果尚未排序,则可先用sortx函数排序。

语句for cs;x中的x不止是字段,也可以是表达式,即:每次读入多条数据,直到表达式x发生变化,比如:for A14 ;floor(SellerId/10)。这句代码会将SellerId09的数据归为一组,1019的归为下一组,函数floor表示取整数部分。如果每个SellerId对应的记录比较少,则上述语句可以一次性读入更多的数据,从而提高计算性能。

B3-C3:这是for语句的循环体,用来对每组数据进行同样的数据处理。处理过程并非本文重点,本案例设计为:计算每个销售员(SellerId)的销售额,如果销售额大于10000,则将该销售员的销售记录追加到文件result.txt

值得注意的是,for语句的作用范围用缩进就可以表示,而无需用括号或begin/end等标记。另外,循环变量用for所在单元格的格名就可以表示,即A2表示当前SellerId对应的记录,A2.sum(Amount)表示对这组记录的Amount字段使用求和函数。函数export用来将一组记录输出到文件中,函数选项@a表示以追加的方式输出。

文件result.txt存储着计算结果,部分数据如下:



 

上述脚本已经完成了所有的数据处理工作,接下来通过JDBC将集算器脚本集成在JAVA里,具体的JAVA代码如下:

         //建立esProc jdbc连接

         Class.forName("com.esproc.jdbc.InternalDriver");

         con= DriverManager.getConnection("jdbc:esproc:local://");

         //调用esProc,其中test是脚本文件名

         st =(com.esproc.jdbc.InternalCStatement)con.prepareCall("call test()");

         st.execute();//执行esProc存储过程

         说明:本案例不要求将计算结果返回JAVA,但有时我们会将计算结果追加在单元格中(假设是B2格),并将结果返回JAVA继续加工。这种情况下需要在集算器中增加一句脚本,比如在A4格书写:result B2,这表示将B2里的数据输出到JDBC接口。

         接下来的JAVA代码也需要增加一句代码,用来接收返回的结果,即在execute之后书写:

         ResultSet set = st.getResultSet(); 。