数组(Arrays)

Awk 允许使用数组。如果你有编程经验,应该已经熟悉数组的概念。简单来说,数组是一个变量,可以存储多个值,类似于列表(list)。在 Awk 中,数组不需要声明变量命名规则与普通变量相同。


1. Awk 数组的基本用法

Awk 的数组是单维(one-dimensional)的,索引从 1 开始。数组的元素使用方括号 [] 访问

some_array[1] = "Hello"
some_array[2] = "Everybody"
some_array[3] = "!"
print some_array[1], some_array[2], some_array[3]

输出

Hello Everybody !

索引不需要连续,未使用的索引不会被自动填充。


2. 关联数组(Associative Arrays)

不同于 C 语言的数组,Awk 的数组是“关联数组”(Associative Arrays),索引可以是字符串,类似于 Python 的字典(dictionary)。

debts["Kim"] = 50
debts["Roberto"] += 70
debts["Vic"] -= 30
print "Vic paid 30 dollars, but still owes", debts["Vic"]

C 风格数组 vs 关联数组

C 风格数组 Awk 关联数组
固定长度 动态增长
整数索引(从 0 开始) 字符串索引
多维数组 只有一维(但可以模拟多维)
顺序存储 无固定顺序(无序)

注意:Awk 只有关联数组,没有传统的 C 风格数组。


3. 数组的特性

3.1 动态增长

Awk 数组的大小是动态的,无需预定义长度。当访问未初始化的索引时,Awk 会自动创建该元素:

message[1] = "Have a nice"
message[2] = "day."
print message[1], message[2], message[3]  # message[3] 未初始化

输出

Have a nice day.

由于 message[3] 没有赋值,Awk 将其初始化为空字符串(""),因此输出不会包含额外内容。

3.2 删除数组元素

使用 delete 关键字可以删除数组元素

delete message[2]  # 删除第二个元素
message[3] = "night."
print message[1], message[2], message[3]

输出

Have a nice night.

message[2] 已被删除,所以 Awk 认为它是空字符串。

3.3 删除整个数组

某些 Awk 版本(如 gawkmawk)支持删除整个数组:

delete message  # 删除整个数组

删除后,message 数组不再存在。


4. 字符串索引

Awk 数组的索引可以是字符串

translate["table"] = "mesa"
translate["chair"] = "silla"
translate["good"] = "bueno"

但数值索引也完全可行

translate[1] = "uno"
translate[5] = "cinco"

4.1 注意:使用小数索引

如果使用小数索引,Awk 可能无法正确匹配

problems[ (1/3) ] = "one third"

不能使用 problems[0.333] 访问这个元素,因为 Awk 会将数值转换为字符串,格式取决于 OFMT 变量

BEGIN { print (1/3) }

默认情况下,输出可能是 0.333333,因此 problems[0.333] 可能不会找到正确的键。

建议

  • 避免使用小数作为索引,或确保格式化一致

5. 数组的无序性

Awk 关联数组是无序的

debts["Alice"] = 100
debts["Bob"] = 200
debts["Charlie"] = 50

元素的存储顺序不固定,但可以使用 for in 遍历:

for (person in debts)
    print person, "owes", debts[person]

可能的输出:

Charlie owes 50
Alice owes 100
Bob owes 200

(顺序可能不同)

5.1 检查元素是否存在

if ("Kane" in debts)
    print "Kane owes", debts["Kane"]

in 关键字用于检查某个索引是否存在


6. 模拟多维数组

Awk 不支持真正的多维数组,但可以使用 SUBSEP 模拟多维数组

array["NY", "capital"] = "Albany"
array["NY", "big city"] = "New York City"
array["OR", "capital"] = "Salem"
array["OR", "big city"] = "Portland"

等价于:

array["NY@capital"] = "Albany"
array["NY@big city"] = "New York City"
array["OR@capital"] = "Salem"
array["OR@big city"] = "Portland"

默认情况下,SUBSEP = "@",但可以修改:

BEGIN { SUBSEP = ":" }

这样 array["NY", "capital"] 就等价于 array["NY:capital"]


7. gawk 提供的数组函数

标准 Awk 没有内置数组函数,但 gawk 提供了排序等功能

函数 作用
length(A) 返回数组 A 的长度
asort(A[,B]) A 进行排序,索引变为 1,2,3...
asorti(A[,B]) A 的索引排序,索引变为 1,2,3...,值变为原来的索引

示例:排序数组

BEGIN {
    arr["Bob"] = 30
    arr["Alice"] = 20
    arr["Charlie"] = 40

    asorti(arr, sorted)

    for (i in sorted)
        print sorted[i], arr[sorted[i]]
}

8. 练习

练习 1:统计硬币数量

修改之前的 "coins" 程序,使用数组按国家统计硬币数量

/gold|silver/ { count[$4]++ }
END {
    for (country in count)
        print country, "has", count[country], "coins"
}

练习 2:债务追踪

读取日志,记录借款和还款:

Jim owes 50
Kim paid 30
Jim owes 20
Kim owes 10

代码:

{
    if ($2 == "owes") debts[$1] += $3
    else if ($2 == "paid") debts[$1] -= $3
}
END {
    for (person in debts)
        print person, "owes", debts[person]
}

练习 3:删除已还清的债务

END {
    for (person in debts)
        if (debts[person] != 0)
            print person, "owes", debts[person]
}

这样,欠款为 0 的人不会出现在结果中


总结

  • Awk 只有关联数组,索引可以是字符串数字
  • 数组是动态的,大小可变。
  • 支持删除单个元素或整个数组
  • 无序存储,使用 for in 遍历。
  • 可用 SUBSEP 模拟多维数组
  • gawk 提供排序函数

在下一章,我们将学习 Awk 的运算符(Operators) 🚀

最后修改: 2025年01月30日 星期四 01:11