本文通过一个实例阐述了 AWK 关联数组的应用,读者可以通过这篇文章得到理论和实践两方面的知识,例子很简单,希望这篇文章能起到抛砖引玉的作用。
AWK、AWK 关联数组(associative arrays)
AWK是一种优秀的文本处理工具,它不仅是 Linux 中也是很多其它 Unix 环境中现有的功能最强大的数据处理工具之一。AWK 提供了极其强大的功能:可以进行样式装入、流控制、数学运算、进程控制语句以及内置的变量和函数。它具备了一个完整语言所具有的几乎所有特性。 在 awk 中数组叫做关联数组(associative arrays),因为下标记可以是数也可以是串。awk 中的数组不必提前声明,也不必声明大小。数组元素用 0 或空串来初始化,这根据上下文而定。例如:
1:可以用数值作数组索引(下标)
Myarray[1]=”xu sihua” Myarray[2]=”780927” |
2:可以用字符串作数组索引(下标)
Myarray[“first”]=”xu” Myarray[“last”]=”si hua” Myarray[“birth”]=”780927” |
使用中 print myarray[1] 将得到”xu sihua” 而 print myarray[2] 和 print[“birth”] 都将得到 ”780927” 。
问题的提出
在我们的生产系统中有一个应用每天都有大量的数据库插入操作,这些操作被记录在了日志文件中,而我们的目的就是从这些日志文件中提取所需要的数据。日志文件的数据格式如下:
--sert into table_a ( field1,field2,field3 ...) values ( 3,1,'xx',...) ; --sert into table_a ( field4,field1,field2 ...) valuse ( 'yyy',2,675 ,...); --sert into table_a ( field1,field8,fieldN ...) valuse ( 'zzz','ccc','eee' ,...); |
其中 field1、field2、fieldN 等是数据库表 table_a 中的字段,每条语句中的字段数不定,顺序也不定,但是所有的字段都是表 table_a 内的字段,values 中的值有字符串也有整数。我想通过上面的文件产生下面格式的数据:
field1_value|field2_value|field3_value|.....|fieldN_value| field1_value|field2_value|field3_value|.....|fieldN_value| field1_value|field2_value|field3_value|.....|fieldN_value| |
具体实现
实现思想很简单,我创建一个数组(AWK 中的关联数组),用字段名作为数组的下标(键),用每条记录 Values 中的值来填充这个数组,最后按照自己所需要的格式输出,下面的代码我省略了一些不必要的部分,仅仅是为了把问题讲清楚:
BEGIN { print "\n Extract some data from CDR Data Package. ","\n", "Processing begins at:",strftime("%Y%m%d%H%M%S", systime()) #为了记录程序的运行时间我在程序开始时输出当前的系统时间,用到了 AWK 的内置函数 strftime. } { #下面创建了数组 ta_record,数组的下标就是记录中的字段名 ta_record["field1"]="" ta_record["field2"]="" ta_record["field3"]="" ta_record["field4"]="" ………… ta_record["fieldn"]="" #下面把每条记录中的字段与对应的值存入关联数组中 for (i=5; i<=(NF-1)/2;i++) { if(i%2 > 0) { value=tolower($(i+(NF-3)/2 )) if( match(value,/^'/) ) #有些字段的值是用单引号引起来的这个需 #要去掉。 { #这里用了 AWK 的 tolower 和 substr 函数 ta_record[tolower($i)]=substr(value,2,length(value)-2) } else #字段的值如果不带单引号直接赋值即可。 ta_record[tolower($i)]=value } } #根据自己的需要输出数据,可以加多个过滤条件,例如: #只输出字段 fieldx 的值为 3 和字段 fieldy 的值为 12 的记录 if( (NR+1)%10000 == 0) {printf("%s",". ")} if(ta_record["fieldx"] == 3 || ta_record["fieldy"] == 12 ) { printf("%s%s%s%s%s%s%s%s%s%s%s\n", ta_record["field1"],"|", ta_record["field2"],"|", ta_record["field3"],"|", ta_record["field4"],"|" ,ta_record["field5"],"|", ta_record["fieldN"],"|") >> ta_record["fieldz"] #这里用ta_record["fieldz"]作为输出文件是有好处 #的,如果fieldz有多个可能的值,例如1,3,5 #那么输出的结果就会生成文件名是1,3,5的三 #个文件,分别记录了fieldz的值为1,3,5的结果,文 #件名是动态产生的,当然您也可以写一个固定的 #文件名把所有结果输出到#一个结果文件中。 } } #最后,在程序运行完成时输出当前的系统时间,这样就可以大概算出这个 #程序的总运行时间了。 END { print "\n","Processing ends at: ",strftime("%Y%m%d%H%M%S", systime()) } |
为什么使用 AWK 来解决这个问题呢?
为什么使用AWK处理这些数据呢,首先因为 AWK 是高效的文本处理工具,而且在几乎所有的 Unix/Linux 中都存在, 另外 AWK 十分高效,对文本数据的处理极具针对性先对于其它语言有很多方便的特性。
程序运行后得到了下面这样的结果:
11|991|1540|13579210134|00992927770589|00992927770589| 33|996|77|13565078660|95533|099195533| 3|991|30|9918814897|200|09063353941| |
现在你需要的数据提取出来了,数据怎么使用取决于你的需要,我用 Oracle 数据库的 SQL Loader 工具把这些数据导入到一个临时表中,然后通过 SQL 操作处理剩下的工作。附件中有真实的数据和和完整的代码清单。
(责任编辑:A6)