为什么awk似乎随机化了数组?

时间:2013-12-19 13:52:11

标签: arrays awk

如果您查看此awk测试的输出,您会发现array中的awk似乎是以某种随机模式打印的。对于相同数量的输入,它似乎是相同的顺序。为什么会这样做?

echo "one two three four five six" | awk '{for (i=1;i<=NF;i++) a[i]=$i} END {for (j in a) print j,a[j]}'
4 four
5 five
6 six
1 one
2 two
3 three

echo "P04637 1A1U 1AIE 1C26 1DT7 1GZH 1H26 1HS5 1JSP 1KZY 1MA3 1OLG 1OLH 1PES 1PET 1SAE 1SAF 1SAK 1SAL 1TSR 1TUP 1UOL 1XQH 1YC5 1YCQ" | awk '{for (i=1;i<=NF;i++) a[i]=$i} END {for (j in a) print j,a[j]}'
17 1SAF
4 1C26
18 1SAK
5 1DT7
19 1SAL
6 1GZH
7 1H26
8 1HS5
9 1JSP
10 1KZY
20 1TSR
11 1MA3
21 1TUP
12 1OLG
22 1UOL
13 1OLH
23 1XQH
14 1PES
1 P04637
24 1YC5
15 1PET
2 1A1U
25 1YCQ
16 1SAE
3 1AIE

为什么会这样做,这是否有规则?

4 个答案:

答案 0 :(得分:9)

在引用for (value in array)语法时,从GNU Awk用户指南中的8. Arrays in awk --> 8.5 Scanning All Elements of an Array开始:

  

此类访问数组元素的顺序   语句由数组的内部排列决定   awk中的元素,无法控制或更改。这可能导致   如果新元素被添加到数组中的语句中的问题   循环体;不能预测for循环是否会   到达他们。类似地,改变循环内部的var可能会产生   奇怪的结果。最好避免这样的事情。


因此,如果您想按照存储它的顺序打印数组,那么您必须使用经典的for循环:

for (j=1; j<=NF; j++) print j,a[j]

实施例

$ awk '{for (i=1;i<=NF;i++) a[i]=$i} END {for (j=1; j<=NF; j++) print j,a[j]}' <<< "P04637 1A1U 1AIE 1C26 1DT7 1GZH 1H26 1HS5 1JSP 1KZY 1MA3 1OLG 1OLH 1PES 1PET 1SAE 1SAF 1SAK 1SAL 1TSR 1TUP 1UOL 1XQH 1YC5 1YCQ"
1 P04637
2 1A1U
3 1AIE
4 1C26
5 1DT7
6 1GZH
7 1H26
8 1HS5
9 1JSP
10 1KZY
11 1MA3
12 1OLG
13 1OLH
14 1PES
15 1PET
16 1SAE
17 1SAF
18 1SAK
19 1SAL
20 1TSR
21 1TUP
22 1UOL
23 1XQH
24 1YC5
25 1YCQ

答案 1 :(得分:5)

Awk使用hash tables来实现关联数组。这只是这种特定数据结构的固有属性。特定元素存储到数组中的位置取决于值的哈希值。要考虑的其他因素是哈希表的实现。如果它具有内存效率,它将使用模数函数或其他方法限制每个键存储的范围。您也可能会为不同的密钥冲突哈希值,因此会发生链接,再次影响顺序,具体取决于首先插入的密钥。

构造(key in array)在适当地用于循环每个键时非常精细但你不能指望顺序,你不应该在循环中更新array,因为你可能最终会处理array[key] 1}}错误多次。

书中Think Complexity对哈希表有很好的描述。

答案 2 :(得分:4)

问题是您用来获取数组索引的运算符,而不是数组存储在哈希表中的事实。

in运算符以随机(查看)顺序提供数组索引(默认情况下,IS与哈希表相关,但这是一个实现选择,可以修改)。

以数字递增顺序显式提供数组索引的for循环也在in运算符所在的相同哈希表上运行,但无论如何都以特定顺序生成输出。

这只是获取数组索引的两种不同方式,两者都可以在哈希表上工作。

man awk并查找in运算符。

如果要使用in运算符控制输出顺序,可以通过填充PROCINFO["sorted_in"]使用GNU awk(从4.0版开始)来完成。有关详细信息,请参阅http://www.gnu.org/software/gawk/manual/gawk.html#Controlling-Array-Traversal

访问数组索引的一些常用方法:

按照您不关心的顺序打印数组元素:

{a[$1]=$0} END{for (i in a) print i, a[i]}

如果索引是数字且从1开始是连续的,则按索引的数字顺序打印数组元素:

{a[++i]=$0} END{for (i=1;i in a;i++) print i, a[i]}

如果索引是数字但不连续,则以索引的数字顺序打印数组元素:

{a[$1]=$0; min=($1<min?$1:min); max=($1>max?$1:max)} END{for (i=min;i<=max;i++) if (i in a) print i, a[i]}

按照输入中显示的顺序打印数组元素:

{a[$1]=$0; b[++max]=$1} END{for (i=1;i <= max;i++) print b[i], a[b[i]]}

使用gawk 4.0 +:

以特定的索引顺序打印数组元素
BEGIN{PROCINFO["sorted_in"]=whatever} {a[$1]=$0} END{for (i in a) print i, a[i]}

除此之外,请编写自己的代码和/或查看gawk asort()asorti()

答案 3 :(得分:0)

如果您使用的是gawk或mawk,还可以设置环境变量WHINY_USERS,该变量将在迭代前对索引进行排序。

示例:

echo "one two three four five six" | WHINY_USERS=true awk '{for (i=1;i<=NF;i++) a[i]=$i} END {for (j in a) print j,a[j]}'
1 one
2 two
3 three
4 four
5 five
6 six

摘自mawk的手册:

WHINY_USERS

这是一个未记录的gawk功能。它告诉mawk在开始遍历数组元素之前对数组索引进行排序。