awk针对单个记录优化了多分隔符

时间:2014-08-27 08:14:36

标签: awk delimiter

我的文件中包含超过2条bilion记录。

它包含多分隔符的记录,如OBO-OBI-CSP-和TICK

文件B2.txt中的我的行是:

917354000000,SUD=CAT-10&DBSG-1&BS3G-1&TS11-1&TS21-1&TS22-1&RSA-1&CSP-69&NAM-0&SCHAR-4&PWD-0000&OFA-0&OICK-134&HOLD-1&MPTY-1&CLIP-1&CFU-1&CFB-1&CFNRY-1&CFNRC-1&CAW-1&SOCFU-1&SOCFB-1&SOCFRY-1&SOCFRC-1&SODCF-0&SOSDCF-4&SOCB-0&SOCLIP-0&SOCLIR-0&SOCOLP-0;

917354000004,SUD=CAT-10&DBSG-1&OBO-2&OBR-2&BS3G-1&TS11-1&TS21-1&TS22-1&RSA-4&PRBT-1&NAM-0&SCHAR-8&PWD-0000&OFA-6&HOLD-1&CLIP-1&CFU-1&CFB-1&CFNRY-1&CFNRC-1&CAW-1&SOCFU-0&SOCFB-0&SOCFRY-0&SOCFRC-0&SODCF-0&SOSDCF-4&SOCB-0&SOCLIP-0&SOCLIR-0&SOCOLP-0;

我的代码运行时间差不多超过2天...... 以下是代码。

#!/usr/bin/sh

echo "MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value" > tt3.txt

while read i
do
MSISDN=`echo $i | awk -F"," '{ print $1}'`;
Complete_Info=`echo $i | awk -F"," '{ print $2}'`;
OBO_Value=`echo $Complete_Info | awk -F"OBO-" '{ print $2 }'| awk -F"&"
'{ print $1 }'`
OBI_Value=`echo $Complete_Info | awk -F"OBI-" '{ print $2 }'| awk -F"&"
'{ print $1 }'`
CSP_Value=`echo $Complete_Info | awk -F"CSP-" '{ print $2 }'| awk -F"&"
'{ print $1 }'`
TICK_Value=`echo $Complete_Info | awk -F"TICK-" '{ print $2 }'| awk -F"&"
'{ print $1 }'`

echo $MSISDN,$OBO_Value,$OBI_Value,$TICK_Value,$CSP_Value >> tt3.txt;

done < B2.txt

是否可以使用awk优化此代码,因此输出文件包含如下内容 9173.54亿,,69,

5 个答案:

答案 0 :(得分:4)

另一个awk:

awk '
  BEGIN {
    FS="[,&;]"
    OFS=","
    print "MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value"
  }

  NF { 
    split(x,A)                # a way of clearing array A, gawk can use delete A
    for(i=2; i<=NF; i++) {
      split ($i,F,/-/)
      A[F[1]]=F[2]
    }
    print $1,A["OBO"],A["OBI"],A["TICK"],A["CSP"]
  }
' B2.txt > tt3.txt

-------

在所有测试结果之后,我决定用1M记录进行测试,其中25%是空行。我在OSX 10.9上测试了以下awk版本:

  • awk(BSD awk)v.200700)
  • mawk v.1.3.4 20100625
  • gawk 4.0.2

我遗漏了3个结果,因为我从经验中知道他们通常是令人失望的,并且它通常会在最后结束...

使用BSD awk和GNU awk,Martin的解决方案更快,但mawk则相反,我的解决方案速度提高了25%。 Ed没有使用split()函数的改进进一步提高了约30%的速度,而mawk只花了7.225s,这是Martin解决方案所需时间的一半。

但真正的踢球者竟然是Jidders第一个使用match()并且没有使用函数的解决方案。凭借mawk,它以惊人的1.868s

完成了它

所以YMMV,但速度明智的最佳解决方案似乎是Jidder与mawk组合的第一个解决方案..

S上。

结果:

       Martin  Scrutinizer  Ed Morton  Jidder (non-function version)
BSDawk
real  0m25.008s  1m51.424s  0m38.566s  0m17.945s
user  0m24.545s  1m47.791s  0m37.662s  0m17.485s
sys   0m0.117s   0m0.824s   0m0.120s   0m0.117s

mawk
real  0m14.472s  0m11.618s  0m7.225s   0m1.868s
user  0m13.922s  0m11.091s  0m6.988s   0m1.759s
sys   0m0.117s   0m0.116s   0m0.093s   0m0.084s

gawk
real  0m33.486s  1m16.490s  0m30.642s  0m17.201s
user  0m32.816s  1m14.874s  0m30.041s  0m16.689s
sys   0m0.134s   0m0.219s   0m0.116s   0m0.131s

答案 1 :(得分:3)

另一种方式

awk 'BEGIN{OFS=FS=",";print "MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value"}
     NF{match($2,/CSP-[1-9]+/);a=substr($2,RSTART+4,RLENGTH-4)
     match($2,/TICK-[1-9]+/);b=substr($2,RSTART+5,RLENGTH-5)
     match($2,/OBI-[1-9]+/);c=substr($2,RSTART+4,RLENGTH-4)
     match($2,/OBO-[1-9]+/);d=substr($2,RSTART+4,RLENGTH-4)
     print $1,d,c,b,a}
' file

制作

MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value
917354000000,,,,69
917354000004,2,,,

我认为它非常自我解释,但如果你需要解释任何问题,那就不用了。

编辑:

这是使用函数

awk 'BEGIN{OFS=FS=",";print "MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value"}
     function f(g){match($2,g"-[1-9]*");return (substr($2,RSTART+4,RLENGTH-4))}
     NF{a=f("OBO");b=f("OBI");c=f("TICK");d=f("CSP");print $1,a,b,c,d} ' file

Bit neater

awk 'BEGIN{
         OFS=FS=","
         print "MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value"
     }

     function GetVal(Str){
         match($2,Str"-[1-9]*")
         return (substr($2,RSTART+4,RLENGTH-4))
     }

     NF{
         a=GetVal("OBO")
         b=GetVal("OBI")
         c=GetVal("TICK")
         d=GetVal("CSP")
         print $1,a,b,c,d} ' file

决定检查这里的每个脚本的速度为10 000行

 Mine(functions) -        real 0m0.773s user 0m0.755s sys 0m0.016s 
 Mine(non-funct) -        real 0m0.306s user 0m0.295s sys 0m0.009s
 Scrutinizer -            real 0m0.400s user 0m0.392s sys 0m0.008s 
 Martin -                 real 0m0.298s user 0m0.291s sys 0m0.006s

第一个功能明显变慢。

答案 2 :(得分:2)

这是一个适合您输入的awk脚本:

BEGIN { 
   OFS=","
   FS=",|&|;|-"
   print "MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value"
}

{ 
   obi=""
   tick=""
   csp=""
   obo=""

   for (i=4; i<=NF; i+=2) {
     if( $i == "OBO" ) { 
       obo=$(i+1) 
     } else if ($i == "OBI") {
       obi=$(i+1)
     } else if ($i == "CSP") { 
       csp=$(i+1)
     } else if ($i == "TICK") { 
       tick=$(i+1)
     }
   } 

   print $1, obo, obi, tick, csp
}

给了

MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value
917354000000,,,,69
917354000004,2,,,

我利用了这样一个事实,即您的数据和输入似乎每两步都交替进行。


为了完整性,让我在1000万行的基准测试中提及更新所有解决方案:

mawk                  gawk
Ed Morton
real    1m19.259s     real  3m36.107s
user    1m17.987s     user  3m35.163s
sys      0m0.706s     sys   0m0.936s 

Martin  
real    2m13.875s     real  4m37.112s
user    2m12.680s     user  4m36.032s
sys     0m0.848s      sys   0m0.954s 

Scrutinizer
real    1m48.755s     real  6m40.202s
user    1m47.513s     user  6m39.148s
sys      0m0.894s     sys   0m1.041s 

Jidder (non-function version)
real    0m16.403s     real  3m18.342s
user    0m15.626s     user  3m17.004s
sys     0m0.632s      sys   0m0.968s 

显然:使用带有mawk的Jidders解决方案,这可以节省批次时间。

答案 3 :(得分:2)

我使用与Scrutinizer相同的变量,因此很容易看到这种类似方法的差异,不需要额外的数组,也不会在每个字段上调用split():

$ cat tst.awk
BEGIN{
    FS="[-&,]"
    OFS=","
    print "MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value"
}
{
    delete A            # or split("",A) in non-gawk
    for (i=2; i<NF;i+=2)
        A[$i] = $(i+1)
    print $1,A["OBO"],A["OBI"],A["TICK"],A["CSP"]
}

$ awk -f tst.awk file
MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value
917354000000,,,,69
917354000004,2,,,

由于带有mawk的@Jidders解决方案似乎速度最快,我想知道这将如何比较:

BEGIN{OFS=FS=","; print "MSISDN,OBO_Value,OBI_Value,TICK_Value,CSP_Value"}
NF {
    a=b=c=d=$2
    sub(/.*CSP-/,"",a);  sub(/[^0-9].*/,"",a)
    sub(/.*TICK-/,"",b); sub(/[^0-9].*/,"",b)
    sub(/.*OBI-/,"",c);  sub(/[^0-9].*/,"",c)
    sub(/.*OBO-/,"",d);  sub(/[^0-9].*/,"",d)
    print $1,d,c,b,a
}

类似的方法,但只使用2个subs()而不是match()+ substr()。结果是,这比我原来的尝试和Jidders慢得多:

$ time awk -f ed.awk file10k > ed.out
real    0m0.468s
user    0m0.405s
sys     0m0.030s

$ time awk -f ed2.awk file10k > ed2.out
real    0m1.092s
user    0m1.045s
sys     0m0.046s

$ time awk -f jidder.awk file10k > jidder.out
real    0m0.218s
user    0m0.124s
sys     0m0.061s

我猜mawk必须对match()+ substr()进行一些严肃的优化!

啊,我刚刚意识到区别是什么 - 字符串操作在awk中比较慢(比I / O慢)并且上面的2-sub()s解决方案修改每个字符串变量两次。

答案 4 :(得分:0)

您可以像这样简化它:

OBO_Value=$(awk -F"OBO-" '/OBO-/ {split($2,a,"&");print a[1];exit}' <<< $Complete_Info)

这一切都在一起,所以应该更快一些。此外,如果找到带有值的行,exit会使awk停止。

PS使用括号而不是旧的和过时的背景