集算器如何处理文本计算——无结构运算

文本可以说是除了数据库外几乎最常见的数据存储形式,针对文本的计算非常重要。然而文本本身没有计算能力,不象数据库有SQL语法,这样对文本的计算就需要借助程序设计语言编码,而大多数用于文本处理的程序语言都没有集合化的,编写批量运算时很繁琐。比如用Java写个很简单地求和运算就要很多行,如果涉及到过滤分组这种运算就需要几百行代码。近年来新出来的perl,python,R等脚本语言在这些方面有所改善,但对批量结构化计算的支持仍然不足,而且集成性也较差。

还有一种方案是将文本数据导入数据库再利用SQL计算,但文本经常缺乏数据库要求的强数据类型特征,导入过程常常要伴随繁琐的数据整理,而且多了一个步骤,事务的处理效率也会受到严重影响。

作为集合化的动态脚本语言,集算器一定程度地弥补这方面的缺失。这里将列举一些文本计算中常见的情况,说明集算器实施这类计算的优势。

无结构运算

文本解析

文本T.txt的行内数据项由不确定数量的空格分隔开:

20010-8-13 991003 3166.63 3332.57  3166.63  3295.11

2010-8-10 991003 3116.31 3182.66  3084.2   3140.2

……

现在要计算每行最后四项数据的平均值列表。用集算器只要一句:

        

A

1

=file("T.txt").read@n().(~.array@tp(“”).to(-4).avg())

read@n()将文本读入成字串集合,array@t(“”)将字串按不定数量的空白符拆成子串集合,@p将自动解析成合适的数据类型以便进一步计算(这里计算平均)。

 

将逗号分隔符文本T.csv中行内数据项数不少于8项的行的前8项写出成另一个文本R.txt,分隔符替换成|(某些银行系统采用的分隔符):

        

A

1

=file("T.csv").read@n().(~.array(“,”)).select(~.len()>=8)

2

>file(“R.txt”).write(A1.(~.to(8).string(“|”)))

string()函数可将集合按指定分隔符再拼成字串。

 

文本T.txt中都是形如下行的串,需要按字符US前的州名(LA)分组拆分成多个文件。

COOP:166657,'NEW IBERIA AIRPORT ACADIANA REGIONAL LA US',200001,177,553

……

        

A

1

=file("T.txt").read@n()

2

=A1.group(mid(~,pos(~," US'")-2,2):state;~:data)

3

>A2.run(file(state+".txt").export(data))

集算器也提供了对正则表达式的支持以应对复杂的拆解需求。不过由于正则表达式的使用难度较大且性能较差,一般建议仍然用常规方法实现。

 

结构化

日志S.log中每3行构成一段完整信息,需要将其解析成结构化数据后再到T.txt:

        

A

B

 C

1

=file(“S.log”).read@n()

 

 

2

=create(…)

 

建立目标结果集

3

for A1.group((#-1)\3)

按行号分组,每3行一个单位

 

从A3(这3行)中解析出字段值

 

>A2.insert(…)

插入到目标结果集

>file(“T.txt”).export(A2)

 

写出结果

有了按行号分组的机制,就可以用循环每次处理一组数据,简化难度。

显然,更简单的单行情况是其特例。

 

如果S.log大到不能读入内存,也可以使用游标逐步读入并写出:

        

A

B

1

=file(“S.log”).cursor@si()

创建游标用流式读入文件

2

=file(“T.txt”)

 

结果文件

3

for A1,3

每读入3行执行一轮循环

 

从A3(这3行)中解析出字段值

 

>A2.export@a(...)

追加写到文件中

熟悉的用户还可以优化代码,使得解析多条记录后一次写出,会有更好的性能。

 

日志S.log中每段完整信息均以”---start---“开头,包含行的数量不确定。这时只要将前面的A3格改成:

3

for A1.group@i(~==”---start---”)

出现---start---时会产生一个新分组

类似地,大文本时也可以用游标处理,也是将上面A3格改成:

3

for A1;~==”---start—“:0

出现---start---时另起一轮循环

 

不定行还有一种情况,同一段信息的每一行都有相同的前缀(比如该段日志所属的用户号等),当这个前缀发生变化时就表示开始另一段信息了,这时仍然只要简单地修改A3代码即可处理:

3

for A1.group@o(left(~,6))

前6个字符变化时产生一个新组

3

for A1;left(~,6)

前6个字符变化时另起一轮循环

前一小节的运算也可以改造成使用游标支持大文本。

 

查找统计

在目录下所有文本中找出含有指定单词的文件,并列出所在行内容及行号:

        

A

1

=directory@p(“*.txt”)

2

=A1.conj(file(~).read@n().(if(pos(~,"xxx"),[A1.~,#,~].string())).select(~))

grep是常用的unix命令,但有些操作系统下没有,且在程序中实现也不简单。集算器提供了文件系统的遍历功能,结合文本计算能力,只要两句代码就能完成。

 

列出文本T.txt中所有出现过的单词及次数,忽略大小写:

        

A

1

=lower(file(“T.txt”).read()).words().groups(~:word;count(1):count)

WordCount是著名的练习题,集算器提供了words()函数将串拆分成单词,只要一句就可以完成这个运算。

 

列出文本T.txt包括字母a,b,c的所有单词,忽略大小写:

        

A

1

=lower(file(“T.txt”).read()).words().select(~.array(“”).pos([“a”,”b”,”c”]))

由于次序问题,判断字母包含不能用子串查找,要用array(“”)将串拆成单字符集合,再用集合从属去判断。有集合运算支持的集算器也只要一句即可。

这些运算都可以用分段或游标的方式简单改造以支持大文本。

posted on 2015-10-23 14:32  hivehooker  阅读(369)  评论(0编辑  收藏  举报

导航