Итерация массива 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