在进行文本处理时,经常会遇到对大文件进行条件查询的情况。用命令行的grep\cat可以处理一些简单情况,写法很简单,但效率太低,用高级语言处理此类问题虽然可以获得较高的运行效率,但代码编写复杂度却相当高。如果遇到条件比较复杂或条件经常变化的情况,还需要额外实现一套类SQL的底层库函数,其难度会更大。
集算器支持大文件条件查询和多线程并行计算,代码简洁性能优异,下面通过例子来看一下具体作法。
文本文件employee.txt中保存了员工数据。现在要读取员工信息,从中找出1981年1月1日(含)之后出生的女员工,最后将查询结果输出到result.txt中。
文本文件empolyee.txt的格式如下:
EID NAME SURNAME GENDER STATE BIRTHDAY HIREDATE DEPT SALARY
1 Rebecca Moore F California 1974-11-20 2005-03-11 R&D 7000
2 Ashley Wilson F New York 1980-07-19 2008-03-16 Finance 11000
3 Rachel Johnson F New Mexico 1970-12-17 2010-12-01 Sales 9000
4 Emily Smith F Texas 1985-03-07 2006-08-15 HR 7000
5 Ashley Smith F Texas 1975-05-13 2004-07-30 R&D 16000
集算器代码:
A1:以游标的形式打开文件。函数cursor并不会将数据全部读入内存,而是以游标(流)的方式打开文件,因此不会占据内存空间。函数cursor使用了默认参数,即:以tab为列分割符读入全部的字段。选项@t表示文件的第一行是列名,之后可以使用直观的列名来书写表达式,如果没有这个选项,则会自动命名为_1、_2、_3…_n。
A2=A1.select(${where})
按照条件过滤。这里使用宏来实现动态解析表达式,其中的where就是动态传入的参数,需要提前定义。定义参数的界面如下:
集算器会先计算${…}里的表达式,将计算结果作为宏字符串值替换${…}之后解释执行。比如按照案例中的条件给where赋值为BIRTHDAY>=date(1981,1,1) && GENDER=="F",则A2的表达式会被替换为=A1.select(BIRTHDAY>=date(1981,1,1) && GENDER=="F")。参数可以在IDE中输入,也可以来自JAVA代码和命令行。
A3=file("D:/result.txt").export@t(A2)。将计算结果输出到文件中。如果计算结果总是很小,也可以用=A2.fetch()将计算结果放入内存并直接观察,或者用result A2.fetch()将计算结果返回给JAVA程序。
本案例的最终结果如下:
本案例实现的是动态查询,即过滤条件发生变化时不用改变代码,只需改变where参数即可。例如,条件变为:查询1981年1月1日(含)之后出生的女员工,或者NAME+SURNAME等于”RebeccaMoore”的员工。Where的参数值可以写为:BIRTHDAY>=date(1981,1,1) && GENDER=="F" || NAME+SURNAME=="RebeccaMoore"。执行之后,A2中的结果集如下图:
前面的算法是串行,改成并行可以进一步提高性能,具体做法是用多个线程并行读取文件,每个线程都用游标访问文件的一部分,并同时进行条件查询,最后再将每个游标的结果合并。
集算器并行计算的代码如下:
A1=4,A1是分段数量,即将文件分成4段。分段数量,也就是实际执行时的并行数,一般不要超过CPU的核数,否则会形成排队等待,并不能提高性能。实际使用最大并行数量可以在选项中配置。
A2=A1.(file("d:/employee.txt").cursor@z(;, ~:A1))
上面的代码按照分段数量生成4个游标。其中A1.(express)表示按照括号内的表达式依次计算A1的成员,括号内可用“~”来表示当前成员。A1一般是集合,比如["file1", " file2"]或[2,3],A1如果是从1开始的连续数字,比如[1,2,3,4],则可以简写成4.( express),案例中的代码就是这种情况。
括号内的表达式是file("d:/employee.txt ").cursor@z(;, ~:A1),其中函数cursor使用了选项@z,这表示将文件分段,用游标取其中的某一段。~:A1表示文件会被大致分为4段(A1=4),当前取第~段。“~”是A1的当前成员,因此每个游标依次对应第1、第2、第3、第4段文件。
另外,之所以是“大致分”,是因为精确分会出现半行数据的情况,而集算器会去头补位,自动取出整行数据。
A3=A2.(~.select(${where})),这句代码针对A2中的每个游标(即~)进行计算,求查询出游标中符合条件的行数据。这里的计算结果仍然是四个游标。
A4=A3.conj@xm(),这句代码将A3中的多个游标进行并行合并。
A5=file("d:/result.txt”).export(A4),将最终计算结果输出到文件中。