如何在awk中初始化一个数组的数组?

32

在AWK中是否可以像这样初始化一个数组?

Colors[1] = ("Red", "Green", "Blue")
Colors[2] = ("Yellow", "Cyan", "Purple")

然后创建一个二维数组,其中Colors[2,3]="Purple"。


根据另一个线程的说法,很遗憾地,没有办法一次性设置数组("sadly, there is no way to set an array all at once without abusing split()")。无论如何,我想要100%确定,并且我相信还有其他人有同样的问题。

我正在寻找最简单的方法来初始化像上面那样的数组,希望它能写得很好。


1
"awk"中的“数组”是关联映射,而不是具有大小和索引的索引数组,并且没有多维数组的概念(尽管您可以通过使用带有“_”作为索引分隔符的字符串来模拟它),可能有更好的方法... - dmckee --- ex-moderator kitten
2
awk(至少gawk)确实具有多维数组的外观。当您使用逗号构造键时,逗号将被SUBSEP变量的内容替换。例如:gawk 'BEGIN {x[1,1]=1; for (i in x) printf "%s\n", i}' | xxd -g1 - glenn jackman
5个回答

19

如果你有GNU awk,你可以使用真正的多维数组。虽然这个答案使用了split()函数,但它肯定没有滥用它。运行方式如下:

awk -f script.awk

script.awk的内容:

BEGIN {

    x=SUBSEP

    a="Red" x "Green" x "Blue"
    b="Yellow" x "Cyan" x "Purple"

    Colors[1][0] = ""
    Colors[2][0] = ""

    split(a, Colors[1], x)
    split(b, Colors[2], x)

    print Colors[2][3]
}

结果:

Purple

1
这是正确的答案,但你把它弄得比必要的复杂了,这在我看来掩盖了一个事实,即你只需要{ Colors[1][0]; split("Red Green Blue",Colors[1]) } - Ed Morton

14

你可以很容易地创建一个二维数组。据我所知,你无法在单个操作中对其进行初始化。正如dmckee在评论中暗示的那样,不能初始化数组的原因之一是子脚本的类型没有限制,因此没有要求它们必须是纯数字的。您可以像以下脚本中一样进行多个赋值。子脚本由变量SUBSEP指定的一个不常见的字符正式分隔,默认值为034(U+001C,文件分隔符)。显然,如果其中一个索引包含此字符,则会导致混淆(但是您上次在字符串中使用该字符是什么时候?)。

BEGIN {
    Colours[1,1] = "Red"
    Colours[1,2] = "Green"
    Colours[1,3] = "Blue"
    Colours[2,1] = "Yellow"
    Colours[2,2] = "Cyan"
    Colours[2,3] = "Purple"
}
END {
    for (i = 1; i <= 2; i++)
        for (j = 1; j <= 3; j++)
            printf "Colours[%d,%d] = %s\n", i, j, Colours[i,j];
}

示例运行:

$ awk -f so14063783.awk /dev/null
Colours[1,1] = Red
Colours[1,2] = Green
Colours[1,3] = Blue
Colours[2,1] = Yellow
Colours[2,2] = Cyan
Colours[2,3] = Purple
$

12

谢谢回答。

无论如何,对于那些想要初始化一维数组的人,这里有一个例子:

SColors = "Red_Green_Blue"
split(SColors, Colors, "_")
print Colors[1] " - " Colors[2] " - " Colors[3]

23
你曾经提出了一个有关多维数组初始化的问题,得到了不少好的回答,但是你却在另一个问题下发表了自己的回答并接受了它。还有其他人认为这样做有问题吗?无论如何,正确的答案是@Steve's - Ed Morton

8

现有的答案都很有帮助,并且涵盖了所有方面,但我想给出更加专注的总结。

这个问题混淆了两个方面:

  • 在Awk中一般初始化数组
  • 特别是用来填充二维数组。

数组初始化:

在Awk中没有数组字面值(初始化器)语法。

最简单的解决方法是:

  • 将数组元素表示为单个字符串,并且
  • 使用split()函数将该字符串分割成数组的元素。
$ awk 'BEGIN { n=split("Red Green Blue", arr); for (i=1;i<=n;++i) print arr[i] }'
Red
Green
Blue

这是楼主在 他们自己的有用回答 中所做的。

如果元素本身包含空格,请使用不属于数据的自定义分隔符,例如此示例中的 |

$ awk 'BEGIN { n=split("Red (1)|Green (2)", arr, "|"); for (i=1;i<=n;++i) print arr[i] }'
Red (1)
Green (2)

初始化二维数组:

  • 根据 POSIX 标准,Awk 没有真正的多维数组,只是使用一个一维数组进行模拟,其索引会隐式地与内置变量 SUBSEP 的值连接起来形成一个单一键(索引;注意所有 Awk 数组都是关联数组)。

    • arr[1,2] 实际上等同于 arr[1 SUBSEP 2],其中 1 SUBSEP 2 是一个字符串连接,用于构建键值。
    • 由于没有真正的多个维度 - 只有一个复合键的平面数组 - 你不能使用 for (i in ...) 枚举(伪)维度,例如仅获取主要(伪)维度 1 的所有子索引。
    • SUBSEP 的默认值是“信息分隔符1”字符,它是一个很少使用的控制字符,在日期中不太可能出现;在 ASCII 和 UTF-8 中,它表示为单字节 0x1f;如果需要,可以更改该值。
  • 相比之下,GNU Awk 作为非标准扩展,确实支持真正的多维数组

    • 重要提示:你必须始终单独指定索引;例如,你必须使用 arr[1][2] 而不是 arr[1,2]

符合 POSIX 标准的示例(类似于 TrueY 给出的有用答案):

awk 'BEGIN {
  n=split("Red Green Blue", arrAux); for (i in arrAux) Colors[1,i] = arrAux[i]
  n=split("Yellow Cyan Purple", arrAux); for (i in arrAux) Colors[2,i] = arrAux[i]
  print Colors[1,2]
  print "---"
  # Enumerate all [2,*] values - see comments below.
  for (i in Colors) { if (index(i, 2 SUBSEP)==1) print Colors[i] }
}'
Green
---
Yellow
Cyan
Purple

注意,使用复合键将多维数组模拟成一维数组具有以下不便之处:
  • 需要辅助数组 auxArr,因为您不能直接填充数组的给定(伪)维度。

  • 您不能仅枚举一个(伪)维度,只能枚举跨(伪)维度的所有索引。

    • 上面的代码 for (i in Colors) { if (index(i, 2 SUBSEP)==1) print Colors[i] } 显示了如何通过枚举所有键,然后仅匹配第一个组成部分索引为 2 的键来解决这个问题,这意味着键值必须以 2 开头,后面跟着 SUBSEP

GNU Awk 示例(类似于 Steve 给出的有用答案,但改进了 Ed Morton 的评论):

GNU Awk 对真正的多维数组的(非标准)支持使 POSIX 兼容解决方案的不便(大多数情况下)消失了(但 GNU Awk 也没有数组初始化器)。

gawk 'BEGIN {
  Colors[1][""]; split("Red Green Blue", Colors[1])
  Colors[2][""]; split("Yellow Cyan Purple", Colors[2])
  # NOTE: Always use *separate* indices: [1][2] instead of [1,2]
  print Colors[1][2]
  print "---"
  # Enumerate all [2][*] values
  for (i in Colors[2]) print Colors[2][i]
}'

注意:
重要提示:如上所述,为了访问多维数组中的特定元素,请始终使用单独的索引;例如,使用 [1][2] 而不是 [1,2] 。
如果使用 [1,2] ,您将得到标准的 POSIX 规定行为,并且您将错误地创建一个新的、带有(字符串拼接)值的单个索引(键),即 1 SUBSEP 2 。
split() 可以方便地用于直接填充子数组。
但前提是,必须初始化二维目标数组:
Colors[1][""] 和 Colors[2][""] 就是这样做的。
虚拟索引 [""] 只是用来创建二维数组的;当 split() 填充该维度时,它被丢弃。
支持使用 for (i in ...) 枚举特定的维度:
for (i in Colors[2]) ... 方便地枚举 Colors[2] 的子索引。

3
一个类似的解决方案。不一定需要SUBSEP=":",只需将其设置为任何可见字符以进行演示:
awk 'BEGIN{SUBSEP=":"
split("Red Green Blue",a); for(i in a) Colors[1,i]=a[i];
split("Yellow Cyan Purple",a); for(i in a) Colors[2,i]=a[i];
for(i in Colors) print i" => "Colors[i];}'

或者稍微难懂一点的版本:

awk 'BEGIN{SUBSEP=":"
split("Red Green Blue Yellow Cyan Purple",a); 
for(i in a) Colors[int((i-1)/3)+1,(i-1)%3+1]=a[i];
for(i in Colors) print i" => "Colors[i];}'

输出:

1:1 => Red
1:2 => Green
1:3 => Blue
2:1 => Yellow
2:2 => Cyan
2:3 => Purple

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接