【问题标题】:Awk array iteration for multi-dimensional arrays多维数组的awk数组迭代
【发布时间】:2011-03-04 21:17:48
【问题描述】:

Awk 为数组处理提供关联索引。一维数组的元素可以迭代:

例如

for(index in arr1)
  print "arr1[" index "]=" arr1[index]

但是这种二维数组是怎么做的呢?下面给出的语法是否有效?

for(index1 in arr2)
for(index2 in arr2)
   arr2[index1,index2]     

【问题讨论】:

  • gawk 从 v4 开始支持数组作为元素,即嵌套数组,比多维数组更灵活,for (i in arr2) for (j in arr2[i]) print arr2[i][j],参见JJoao's answer

标签: arrays awk associative-array


【解决方案1】:

不,语法

for(index1 in arr2) for(index2 in arr2) {
    print arr2[index1][index2];
}

行不通。 awk 并不真正支持多维数组。如果你做类似的事情,它会做什么

x[1,2] = 5;

是将两个索引(1 和 2)连接成一个字符串,由 SUBSEP 变量的值分隔。如果这等于“*”,那么您将具有与

相同的效果
x["1*2"] = 5;

SUBSEP的默认值为非打印字符,对应Ctrl+\。您可以通过以下脚本看到这一点:

BEGIN {
    x[1,2]=5;
    x[2,4]=7;
    for (ix in x) {
        print ix;
    }
}

运行此命令:

% awk -f scriptfile | cat -v
1^\2
2^\4

所以,回答您的问题 - 如何迭代多维数组 - 只需使用单个 for(a in b) 循环,但您可能需要一些额外的工作才能将 a 拆分为 x 和 @987654331 @零件。

【讨论】:

    【解决方案2】:

    AWK 通过将索引与 SUBSEP 变量 (0x1c) 中保存的字符连接起来来伪造多维数组。您可以像这样使用split 遍历二维数组(基于info gawk 文件中的示例):

    awk 'BEGIN { OFS=","; array[1,2]=3; array[2,3]=5; array[3,4]=8; 
      for (comb in array) {split(comb,sep,SUBSEP);
        print sep[1], sep[2], array[sep[1],sep[2]]}}'
    

    输出:

    2,3,5
    3,4,8
    1,2,3
    

    但是,您可以使用嵌套的 for 循环遍历数字索引数组:

    for (i = 1; i <= width; i++)
        for (j = 1; j < = height; j++)
            print array[i, j]
    

    来自GAWK manual 的另一个值得注意的信息:

    要测试特定索引序列是否存在于多维数组中,请使用与单维数组相同的运算符 (in)。将整个索引序列写在括号中,用逗号分隔,作为左操作数:

         (subscript1, subscript2, ...) in array
    

    Gawk 4 添加了arrays of arrays。从那个链接:

    for (i in array) {
        if (isarray(array[i])) {
            for (j in array[i]) {
                print array[i][j]
            }
        }
        else
            print array[i]
    }
    

    另请参阅Traversing Arrays of Arrays,了解有关以下函数的信息,该函数遍历任意维度的数组数组,包括锯齿状数组:

    function walk_array(arr, name,      i)
    {
        for (i in arr) {
            if (isarray(arr[i]))
                walk_array(arr[i], (name "[" i "]"))
            else
                printf("%s[%s] = %s\n", name, i, arr[i])
        }
    } 
    

    【讨论】:

      【解决方案3】:

      gawk 的当前版本(gnu awk,默认在 linux,并且可以随心所欲地安装),具有真正的多维数组。

      for(b in a)
         for(c in a[b])
            print a[b][c], c , b
      

      另见函数isarray()

      【讨论】:

        【解决方案4】:

        我将提供一个示例,说明我在处理查询数据的工作中如何使用它。假设您有一个包含按产品类别和客户 ID 进行交易的提取文件:

        customer_id  category  sales
        1111         parts     100.01
        1212         parts       5.20
        2211         screws      1.33
        ...etc...
        

        它易于使用 awk 计算购买的不同客户总数:

        awk 'NR>1 {a[$1]++} END {for (i in a) total++; print "customers: " total}' \ 
        datafile.txt
        

        但是,计算每个类别中购买的不同客户的数量建议使用二维数组:

        awk 'NR>1 {a[$2,$1]++} 
              END {for (i in a) {split(i,arr,SUBSEP); custs[arr[1]]++}
                   for (k in custs) printf "category: %s customers:%d\n", k, custs[k]}' \
        datafile.txt
        

        custs[arr[1]]++ 的增量有效,因为每个 category/customer_id 对作为 awk 使用的关联数组的索引是唯一的。

        事实上,我使用 gnu awk,它更快,并且可以像 D. Williamson 提到的那样执行array[i][j]。但我想确定我可以在标准 awk 中做到这一点。

        【讨论】:

          【解决方案5】:

          awk(1) 最初被设计为——部分地——作为 C 语言的教学工具,而多维数组几乎永远存在于 C 和 awk(1) 中。因此,POSIX IEEE 1003.2 对它们进行了标准化。

          要探索语法和语义,如果您创建以下名为“test.awk”的文件:

          BEGIN {
            KEY["a"]="a";
            KEY["b"]="b";
            KEY["c"]="c";
            MULTI["a"]["test_a"]="date a";
            MULTI["b"]["test_b"]="dbte b";
            MULTI["c"]["test_c"]="dcte c";
          }
          END {
            for(k in KEY) {
              kk="test_" k ;
              print MULTI[k][kk]
            }
            for(q in MULTI) {
              print q
            }
            for(p in MULTI) {
              for( pp in MULTI[p] ) {
                print MULTI[p][pp]
              }
            }
          }
          

          并使用以下命令运行它:

          awk -f test.awk /dev/null
          

          你会得到以下输出:

          date a
          dbte b
          dcte c
          a
          b
          c
          date a
          dbte b
          dcte c
          

          至少在 Linux Mint 18 Cinnamon 64 位 4.4.0-21-generic #37-Ubuntu SMP 上

          【讨论】:

          • 您在测试中使用了非标准的 GNU 扩展。这些在mawk 中不起作用,它明确“符合 AWK 语言的 Posix 1003.2(11.3 草案)定义”(这是指POSIX before 1997 的第二部分,并且被IEEE Std 1003.1-2017, aka POSIX.1-2017 混淆了)。 current POSIX spec for awk 仍然缺少对您的语法的引用。
          • 考虑到我帮助编写了 POSIX IEEE 1003.2 的 awk(1) 标准,我很高兴指出这项工作并依赖它。根据上述文档,它适用于安装 Linux Mint 18 Cinnamon 64-bit 4.4.0-21-generic #37-Ubuntu SMP 的 awk。
          • 感谢您在规范方面所做的工作!您能否链接到您编写的规范并指出它要求多维数组支持的位置?我找不到它。 (另外,为什么 POSIX.1-2017 中缺少它?) Mint 18 (ls -l /etc/alternatives/awk) 附带了哪个 awk 实现以及什么版本?对我来说,gawk 'BEGIN { a[2]["c"] = 4 }' 工作正常(gawk 4.2.1)但mawk 'BEGIN { a[2]["c"] = 4 }' 给我一个语法错误(mawk 1.3.3)。
          • 首先,最初的问题是“如何解决问题”而不是“标准怎么说”。毫无疑问,上述代码实际上可以在我的文档中引用的平台上运行。
          • 其次,有趣的是,您提供的链接实际上与您声称多维数组在该标准版本中的说法相矛盾。因为它特别显示了以下语法: var[expr1, expr2, ... exprn] 早在 Open Group IEEE 联合规范“Single UNIX Specification version 3, POSIX:2001”中我就发现了这一点,该规范包含了第 6 期他们的规格。 "
          猜你喜欢
          • 2023-03-08
          • 2014-05-17
          • 1970-01-01
          • 2021-11-02
          • 2011-11-20
          • 2016-01-28
          • 2020-02-17
          相关资源
          最近更新 更多