跳转到内容

Awk 入门/数组

来自维基教科书,开放的书籍,为开放的世界

Awk 也允许使用数组。那些以前编写过程序的人已经熟悉数组。对于那些没有接触过数组的人来说,数组只是一个保存多个值的单个变量,类似于列表。命名约定与变量相同,并且,与变量一样,数组不需要声明。

Awk 数组只能有一维;第一个索引为 1。数组元素通过索引来识别,索引包含在方括号中。例如

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

括号内的数字是索引。它选择数组内的特定条目,或元素,然后可以像普通变量一样创建、访问或修改它。

关联数组

[编辑 | 编辑源代码]

这是熟悉 C 风格数组的人学习新事物的地方。Awk 数组很有趣,因为它们实际上是关联数组。索引实际上是字符串,因此关联数组更像字典而不是编号列表。例如,数组可以用来统计一组债务人所欠的钱,如下所示

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

C 风格数组(表现得像编号列表)和关联数组(表现得像字典)之间存在一些差异

C 风格数组 关联数组
  • 固定长度
  • 整数索引
  • 索引从 0 开始
  • 多维
  • 可变长度
  • 字符串索引
  • 稀疏且无序
  • 单维

Awk 只有关联数组,没有 C 风格数组。这种比较只针对那些从其他编程语言学习数组的人。

数组细节

[编辑 | 编辑源代码]

让我们回顾一下 Awk 数组的一些更具体的特征

可变长度

[编辑 | 编辑源代码]

Awk 中的数组可以在程序执行过程中增长和缩小。每当访问 Awk 之前从未见过的索引时,新条目会自动创建。无需让 Awk 知道您打算使用多少个元素。

message[1]="Have a nice"
message[2]="day."
print message[1], message[2], message[3]

在这个例子中,数组 message 中的元素 1 和 2 在我们为它们分配值的那一刻就被创建。在最后一行,访问 message[3],尽管它还没有创建。Awk 将创建元素 message[3] 并将其初始化为空字符串(因此什么都不会出现)。

此外,可以从数组中删除元素。以下是上述示例的扩展

delete message[2]
message[3]="night."
print message[1], message[2], message[3]

现在,message[2] 不再存在。就好像您从未在第一次为它赋值一样,因此 Awk 将对它的提及视为空字符串。如果您一起运行这两个示例,结果将是

Have a nice day.
Have a nice night.

(注意 print 语句中的逗号如何在数组元素之间添加空格。这可以通过设置内置变量 OFS 来更改。)

一些实现,比如 gawk 或 mawk,也允许程序员删除整个数组而不是单个元素。在

delete message

之后,数组 message 不再存在。

字符串索引

[编辑 | 编辑源代码]

您已经看到 Awk 数组使用字符串来选择数组中的每个元素,就像字典一样。

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

但是,数字是完全可以接受的。像往常一样,Awk 只会在需要时将数字转换为字符串。

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

然而,当用小数访问数组时,事情可能会变得棘手。

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

您可以使用 problems[0.333] 访问此元素吗?不。这取决于内置变量 OFMT 的内容,它告诉 Awk 如何将数字转换为字符串。将转换一定数量的小数位数,其余的将被丢弃。一般来说,尽量避免使用带小数的索引,除非您非常小心地使用正确的格式(可以更改)。

稀疏性和无序性

[编辑 | 编辑源代码]

Awk 数组是稀疏的,这意味着您可以有元素 1 和元素 3 而没有元素 2。这很明显——Awk 使用字符串索引,因此它不区分编号元素。

更重要的是,关联数组中的元素不会按任何特定顺序存储。

有两个有用的命令允许您检查数组中的元素。我们将在接下来的章节中详细了解它们,但现在让我们看一些示例。

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

回到介绍(其中创建了一个 debts 数组将人们的姓名与金额相关联),我们可以看到 Awk 提供了一些有用的命令来访问数组。第一个命令,if in,让我们检查特定元素是否已定义,然后根据该结果执行代码。第二个命令,for in,让我们创建一个临时变量(在本例中称为 person),并在数组中的每个元素上重复一条语句。

玩弄这些例子,看看 Awk 如何不一定会在其数组中保持特定的顺序。幸运的是,这从来都不是真正的问题。

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"

这只是多维数组的快速演示。如您所见,这些实际上不是多维的;而是单维的,但使用特殊的分隔符。此处不会进一步探讨多维数组,因为必须理解几个技术细节。您仍然可以在没有它们的情况下编写有用的 Awk 程序,但如果您对多维数组感兴趣,请随时查阅您的 Awk 手册(或者只是玩弄一下看看什么有效)。

标准 Awk 中没有用于数组的函数。但是,gawk 提供了三个函数(A 和 B 应该都是数组)

  • length(A) 返回 A 的长度。
  • asort(A[,B]) - 如果未给出 B,则对 A 进行排序。A 的索引将被替换为从 1 开始的连续整数。如果给出 B,则将 A 复制到 B,然后按上述方式对 B 进行排序,而 A 保持不变。返回 A 的长度。
  • asorti(A[,B]) - 如果未给出 B,则丢弃 A 的值并对它的索引进行排序。排序后的索引将成为新的值,从 1 开始的连续整数将成为新的索引。与前一种情况类似,如果给出 B,则将 A 复制到 B,然后按上述方式对 B 的索引进行排序,而 A 保持不变。返回 A 的长度。
  1. 更新我们在本书开头编写的“coins”程序。使用数组来统计按国家分类的硬币数量。显示结果以及摘要。
  2. 编写债务人程序。它应该扫描一个日志文件,该文件列出了像“Jim owes 50”和“Kim paid 30”这样的交易。使用关联数组,对人们借入和偿还的所有钱进行汇总。确保一个人可以在文件中多次出现,并且他们的债务会得到相应的更新。在 END 处,列出每个人及其总额。
  3. 改进#2中的程序,如果一个人偿还了所有欠款,则从数组中删除他们的姓名。这样,结果就不会被欠零元的人弄得乱七八糟。

下一页快速回顾了 Awk 提供的所有运算符

华夏公益教科书