技巧:AWK 关联数组的一个应用

来源:developerWorks 中国 作者:许 四化
  
本文通过一个实例阐述了 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)


时间:2008-08-27 16:30 来源:developerWorks 中国 作者:许 四化 原文链接

好文,顶一下
(0)
0%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量