31

是否可以在 AWK 中初始化这样的数组?

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

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


另一个线程我了解到这是不可能的(“遗憾的是,没有办法在不滥用 split() 的情况下一次设置一个数组”)。无论如何,我想 100% 确定,并且我确信还有其他人有同样的问题。

我正在寻找最简单的方法来初始化像上面这样的数组,如果写得好的话会很好。

4

5 回答 5

17

如果有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
于 2012-12-28T04:41:34.667 回答
13

您可以很容易地创建一个二维数组。AFAIK,您不能做的是在单个操作中对其进行初始化。正如dmckee在评论中暗示的那样,无法初始化数组的原因之一是对下标的类型没有限制,因此不需要它们是纯数字。您可以按照下面的脚本进行多项分配。下标由变量 SUBSEP 指定的晦涩字符正式分隔,默认值为 034 (U+001C, FILE SEPARATOR)。显然,如果其中一个索引包含此字符,则会出现混乱(但您最后一次在字符串中使用该字符是什么时候?)。

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
$
于 2012-12-28T03:12:02.360 回答
12

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

SColors = "Red_Green_Blue"
split(SColors, Colors, "_")
print Colors[1] " - " Colors[2] " - " Colors[3]
于 2012-12-28T03:36:58.183 回答
5

现有的答案很有帮助,并且涵盖了所有方面,但我想我会给出一个更集中的总结。

这个问题把两个方面混为一谈:

  • 通常在 Awk 中初始化数组
  • 这样做是为了特别填充二维数组

数组初始化:

awk没有数组字面量(初始化器)语法。

最简单的解决方法是:

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

这就是 OP 在他们自己的有用答案中所做的。

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

$ 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“INFORMATION SEPARATOR ONE”字符,一个很少使用的控制字符,不太可能出现在日期中;在 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 ...),您只能枚举所有索引,跨越(伪)维度。

    • 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 强制行为,并且您将错误地创建一个带有 (string-concatenated) value 的新的单一索引 (key) 1 SUBSEP 2
  • split()可以方便地用于直接填充子数组。

  • 但是,作为先决条件,必须初始化二维目标数组:

    • Colors[1][""]Colors[2][""]做到这一点。
    • 虚拟索引[""]只是用来创建一个二维数组;split()稍后填充该维度时将其丢弃。
  • 支持枚举特定维度for (i in ...)

    • for (i in Colors[2]) ...方便地仅枚举 的子索引Colors[2]
于 2017-03-31T14:02:00.420 回答
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
于 2013-04-22T12:26:20.340 回答