AWK语言第二版 1.5 用Awk计算

1.5 用Awk来计算

在Awk里,一个“动作”是一系列由换行或分号分隔开的语句。前面的例子给出了单个 print printf语句构成的动作。本节会给出一些例子,包含简单的数值或字符串计算的语句。在这些语句中你不仅能使用内置变量如NF,还能创建你自己的变量,用来执行计算、储存数据,以及类似操作。在Awk里,用户创建的变量不需要事先声明;当你使用它们的时候,它们就应运而生了。

计数

下面这个程序使用变量 emp 计算工作超过15小时的员工个数

$3 > 15 { emp = emp + 1 }
END     { print emp, "employees worked more than 15 hours" }

每读到一行时,若该行的第三个域大于15,则会将变量 emp 递增 1。用emp.data作为输入,输出是

3 employees worked more than 15 hours

Awk里的数值变量初始值是0,因此我们不需要对emp进行初始化。下面这种语句

emp = emp + 1

在程序中出现太频繁了,因此C语言,以及很多受其启发的语言,专门为之提供了一个等价的操作符 ++ ,用来简化代码:

emp++

对应地还有个递减操作符 -- ,后面很快会提到。

因此上面的计数程序可以用 ++ 来重写:

$3 > 15 { emp++ }
END     { print emp, "employees worked more than 15 hours" }

计算总和与平均值

为了计算员工的个数,我们不用自己造变量,而是使用内置变量 NR,它保存着到目前为止读入的行数;当所有输入都结束之后,它的值就是读入的总行数。

END { print NR, "employees" }

的输出为

6 employees

下面这个程序使用 NR 来计算平均值

    { pay = pay + $2 * $3 }
END { print NR, "employees"
      print "total pay is", pay
      print "average pay is", pay/NR
    }

其中第一个动作对所有员工的薪水进行累加计算。而 END 动作会输出

6 employees
total pay is 1456
average pay is 242.667

很明显这个程序不完美,可以使用 printf 来打印更整洁的输出,比如将小数点后数字控制在两位。而且还有个潜在的错误,虽然出现的情况很少:如果没读到数据,NR为0 ,程序会出现除0错误。

另外,操作符 += 也可以简化变量递增的写法,它会将表达式左边的变量加上右边的值,因此上面程序的第一行可以简写为:

    { pay += $2 * $3 }

处理文本

Awk变量既能表示数字也能表示字符串。下面这个程序找出哪个员工的时薪最高:

$2 > maxrate { maxrate = $2; maxemp = $1 }
END { print "highest hourly rate:", maxrate, "for", maxemp }

输出结果是

highest hourly rate: 25 for Mark

这个程序里,变量 maxrate 存的是数值,而变量 maxemp 存的是字符串。如果多个员工有相同的时薪,这个程序只会输出第一个。(想想怎么做到全部输出?)

字符串连接

可以把老字符串拼在一起形成新字符串;这个操作称为“连接”。在Awk程序里做字符串连接操作,就是要将字符值一个接一个地写出来;没有单独的字符串连接操作符。(现在看来,Awk的这个设计不够理想,因为有时会导致难以定位的错误)

这个程序演示了字符串连接操作:

    { names = names $1 " " }
END { print names }

通过将每个名字和一个空格拼接到变量 names 之前保存的值后面,就得到所有员工的名字。names的值在END的动作中打印:

Beth Dan Kathy Mark Mary Susie

在每个输入行,程序的第一条语句会拼接三个值:names之前的值,第一个域,还有一个空格;然后它将这个结果赋给names。这样,当读入所有的输入行,names就包含了单个字符串,其中是所有员工的姓名,每个姓名后面带个空格(注意最后一个姓名后面也有个看不见的空格)。用于保存字符串的变量在创建时包含了空字符串(即不包含字符),因此这个程序里names也不需要明确初始化。

打印最后一行

前面的第二个例子中,内置变量如 NR END 动作时保留了它的值,类似的, $0 也一样保留了。程序

END { print $0 }

可以用来打印最后一行。输出为

Susie   17      18

内置函数

我们已经看到Awk使用内置变量来维护频繁使用的值,如域的个数和输入行数。类似的,Awk提供了内置函数来计算其他有用的值。

除了平方根、对数、随机数等数学函数,还有用来操作文本的函数。比如 length,计算字符串中的字符。下面这个程序计算每个人的名字长度

{ print $1, length($1) }

结果是

Beth 4
Dan 3
Kathy 5
Mark 4
Mary 4
Susie 5

计算行数、单词数和字符数

下面这个程序用了 lengthNFNR 来计算输入的行数、单词数和字符数,功能类似Unix程序 wc。为了方便,我们将每个域看做一个单词(当然这样把问题简化了)

    { nc += length($0) + 1
      nw += NF
    }
END { print NR, "lines,", nw, "words,", nc, "characters" }

用emp.data文件做输入,结果是

6 lines, 18 words, 77 characters

注意到我们在每个输入行的 nc 变量里面加了1,这是因为 $0 不包括换行符,我们要把它算上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值