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

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

e.g.

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

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

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

person cobp    schedule 17.06.2010    source источник
comment
gawk начиная с версии 4 поддерживает массивы как элементы, то есть вложенные массивы, более гибкие, чем многомерные массивы, for (i in arr2) for (j in arr2[i]) print arr2[i][j], см. ответ JJoao   -  person jthill    schedule 07.03.2017


Ответы (5)


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

Gawk 4 добавляет массивы массивов. Из этой ссылки:

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

Также см. Обход массивов массивов для получения информации о следующая функция, которая обходит массив массивов произвольного размера, в том числе зубчатые:

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])
    }
} 
person Dennis Williamson    schedule 17.06.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.

person psmears    schedule 17.06.2010

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

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

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

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

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

person Merlin    schedule 28.12.2016

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-универсальная #37-Ubuntu SMP

person Bob Makowski    schedule 29.12.2017
comment
Вы используете нестандартные расширения GNU в своих тестах. Они не работают в mawk, который явно соответствует определению Posix 1003.2 (проект 11.3) для Язык AWK (относится ко второй части POSIX до 1997 г. и устарел до путаницы IEEE Std 1003.1-2017, также известный как POSIX.1-2017). В текущей спецификации POSIX для awk по-прежнему отсутствуют ссылки на ваш синтаксис. - person Adam Katz; 30.01.2019
comment
учитывая, что я помогал писать стандарт awk(1) для POSIX IEEE 1003.2, я рад указать на эту работу и положиться на нее. согласно приведенной выше документации, он работает на awk, на котором установлена ​​Linux Mint 18 Cinnamon, 64-разрядная версия 4.4.0-21-generic #37-Ubuntu SMP. - person Bob Makowski; 04.03.2019
comment
Спасибо за работу над спецификацией! Не могли бы вы дать ссылку на написанную вами спецификацию и указать, где она предписывает поддержку многомерных массивов? Я не мог найти его. (Кроме того, почему он отсутствует в POSIX.1-2017?) Какая реализация awk поставляется с Mint 18 (ls -l /etc/alternatives/awk) и в какой версии? Для меня gawk 'BEGIN { a[2]["c"] = 4 }' работает нормально (gawk 4.2.1), но mawk 'BEGIN { a[2]["c"] = 4 }' выдает синтаксическую ошибку (mawk 1.3.3). - person Adam Katz; 04.03.2019
comment
Во-первых, первоначальный вопрос был о том, как решить проблему, а не о том, что говорит стандарт. нет никаких сомнений в том, что приведенный выше код действительно работает на платформе, которую я указал в своей документации. - person Bob Makowski; 08.07.2019
comment
Во-вторых, интересно, что предоставленная вами ссылка на самом деле противоречит вашему утверждению о том, что многомерные массивы есть в этой версии стандарта. Поскольку он специально показывает следующий синтаксис: var[expr1, expr2, ... exprn] Я нашел это верным еще в совместной спецификации Open Group IEEE Single UNIX Specification версии 3, POSIX: 2001, которая включает выпуск 6 их спецификации. . - person Bob Makowski; 08.07.2019
comment
Интересно отметить, что я ничего не нашел в документации gawk, например. их справочные страницы, где GNU указывает, какой версии POSIX они соответствуют. Что касается шашек, то у меня их в гараже с десяток. Потому что вначале возник спор о том, будет ли включен в стандарт синтаксис nawk(1) из UNIX System V Release 3.1. Брайан Кернингем сам прислал поздравления по электронной почте, потому что мне удалось убедить группу принять nawk(1) в качестве стандарта. В то время я был сотрудником IEEE 1003.2 и председателем подкомитета, непосредственно ответственным за все команды AM. - person Bob Makowski; 08.07.2019
comment
Вопрос помечен awk, а не gawk. В этом ответе не обсуждается приближение многомерного массива POSIX, в котором используются запятые для обозначения разделителей SUBSEP для второго измерения. Этот формат немного громоздкий, так как его очень трудно выделить, и он не может способствовать третьему измерению. Что еще более важно, как я уже заметил, mawk не примет a[2]["c"], поскольку его нет ни в одной спецификации (кроме gawk справочных/информационных страниц). Многие системы используют полностью совместимые с POSIX mawk или nawk (а не gawk) как /usr/bin/awk. Ответ только gawk не подойдет для пользователей таких систем. - person Adam Katz; 08.07.2019
comment
Ах, было бы полезно, если бы вы поняли, что в Linux, когда вы выполняете man awk, вы получаете справочную страницу для gawk. Во-вторых, вы спрашиваете всю проблему о том, что стандарт POSIX, который вы фактически цитировали, имеет точный синтаксис, который, как вы утверждали, не существовал в стандарте. В-третьих, вы задаетесь вопросом, что мое решение действительно работает. Вы тратите время людей на это. - person Bob Makowski; 08.07.2019
comment
man awk привязан к руководству по тому, что вы установили как awk двоичный файл; gawk для тебя и mawk для меня. В последней спецификации awk есть два экземпляра ][, и ни один из них не ссылается в настоящие многомерные массивы, такие как a[2]["c"]. Все, что я вижу, это строки вроде «Поскольку массивы awk действительно одномерны, такой список, разделенный запятыми, должен быть преобразован в одну строку путем объединения строковых значений отдельных выражений, каждое из которых отделено от другого значением переменная SUBSEP». - person Adam Katz; 09.07.2019