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 от v4 поддържа масиви като елементи, т.е. вложени масиви, по-гъвкави от многомерните масиви, 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]]++ работи, защото всяка двойка категория/customer_id е уникална като индекс към асоциативния масив, използван от 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-bit 4.4.0-21-generic #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-bit 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 ще бъде допуснат в стандарта. Самият Brian Kerningham изпрати имейл с поздравления, защото успях да убедя групата да приеме nawk(1) като стандарт. По това време бях служител на IEEE 1003.2 и председател на подкомисията, пряко отговорен за всички команди A-M. - person Bob Makowski; 08.07.2019
comment
Въпросът е маркиран awk, а не gawk. Този отговор не обсъжда приближението на многоизмерен масив POSIX, което използва запетаи, за да посочи SUBSEP разделители за второ измерение. Този формат е малко тромав, тъй като е толкова трудно да се разгадае и не може да улесни трето измерение. По-важното е, че както отбелязах, mawk няма да приеме a[2]["c"], тъй като не е в нито една спецификация (отвъд страниците gawk man/info). Много системи използват напълно съвместимите с POSIX mawk или nawk (вместо gawk) като /usr/bin/awk. Отговор само gawk няма да работи за потребители на такива системи. - person Adam Katz; 08.07.2019
comment
А, би помогнало, ако разбрахте, че на Linux, когато правите man awk, получавате manpage за gawk. Второ, вие молите за целия проблем, че стандартът POSIX, който всъщност цитирахте, има точния синтаксис, който твърдите, че не съществува в стандарта. 3-то, вие задавате въпроса, че моето решение всъщност работи. Губите времето на хората с това. - person Bob Makowski; 08.07.2019
comment
man awk е свързан с ръководството за всичко, което сте инсталирали като ваш awk двоичен файл; gawk за теб и mawk за мен. Има два екземпляра на ][ в най-новата спецификация на awk и нито един не препраща към истински многоизмерни масиви като a[2]["c"]. Всичко, което виждам, са редове като „Тъй като awk масивите са наистина едномерни, такъв разделен със запетая списък трябва да бъде преобразуван в единичен низ чрез конкатениране на низовите стойности на отделните изрази, всеки отделен от другия със стойността на променливата SUBSEP.“ - person Adam Katz; 09.07.2019