Итерация массива Awk для многомерных массивов

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

например.

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

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

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

Ответы

Ответ 1

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 (i = 1; i <= width; i++)
    for (j = 1; j < = height; j++)
        print array[i, j]

Еще один примечательный бит информации из руководства GAWK:

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

     (subscript1, subscript2, ...) in array

Ответ 2

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

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 + \. Вы можете увидеть это со следующим script:

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

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

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

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

Ответ 4

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

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.

Ответ 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-bit 4.4.0-21-общий # 37-Ubuntu SMP