Итерация массива Awk для многомерных массивов - PullRequest
19 голосов
/ 17 июня 2010

Awk предлагает ассоциативную индексацию для обработки массива.Элементы одномерного массива могут быть повторены:

например,

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

Но как этот тип делается для двумерного массива?Работает ли синтаксис, приведенный ниже?

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

Ответы [ 5 ]

35 голосов
/ 17 июня 2010

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 :

Чтобы проверить, существует ли конкретная индексная последовательность в многомерном массиве, используйте тот же оператор (in), который используется для одномерных массивов. Запишите всю последовательность индексов в скобках, разделенных запятыми, в качестве левого операнда:

     (subscript1, subscript2, ...) in array
5 голосов
/ 17 июня 2010

Нет, синтаксис

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 и y запчасти.

3 голосов
/ 09 марта 2016

Текущие версии gawk (gnu awk, по умолчанию в Linux, и может быть установлен везде, где вы хотите), имеет реальные многомерные массивы.

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

См. Также функцию isarray()

2 голосов
/ 28 декабря 2016

Я приведу пример того, как я использую это в своих данных обработки запросов.Предположим, у вас есть файл выписки, полный транзакций по категории продукта и идентификатору клиента:

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]]++ работает, потому что каждая пара категория / customer_id уникальна как индекс для используемого ассоциативного массивапо awk.

По правде говоря, я использую gnu awk, который работает быстрее и может выполнять array[i][j], как упоминал Д. Уильямсон.Но я хотел быть уверен, что смогу сделать это в стандартном awk.

1 голос
/ 29 декабря 2017

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-bit 4.4.0-21-generic # 37-Ubuntu SMP

...