Подтвердить что ты не робот

Как инициализировать массив массивов в awk?

Можно ли инициализировать такой массив в AWK?

Colors[1] = ("Red", "Green", "Blue")
Colors[2] = ("Yellow", "Cyan", "Purple")

И затем иметь двумерный массив, где Цвета [2,3] = "Фиолетовый".


Из другого потока Я понимаю, что это невозможно ( "к сожалению, не существует способа установить массив сразу без злоупотребления split()" ). В любом случае, я хочу быть на 100% уверенным, и я уверен, что есть другие с тем же вопросом.

Я ищу самый простой способ для инициализации массивов, подобных приведенному выше, будет хорошо, если бы он был хорошо написан.

4b9b3361

Ответ 1

Спасибо за ответы. В любом случае, для тех, кто хочет инициализировать одномерные массивы, вот пример:

SColors = "Red_Green_Blue"
split(SColors, Colors, "_")
print Colors[1] " - " Colors[2] " - " Colors[3]

Ответ 2

Вы можете создать двумерный массив достаточно легко. То, что вы не можете сделать, AFAIK, инициализирует его за одну операцию. Поскольку dmckee указывает на комментарий, одной из причин невозможности инициализации массива является отсутствие ограничений на типы индексов и, следовательно, нет требования, чтобы они были чистыми числами. Вы можете выполнять несколько назначений, как в script ниже. Нижние индексы формально разделяются неясным символом, обозначенным переменной SUBSEP, со значением по умолчанию 034 (U + 001C, FILE SEPARATOR). Ясно, что если один из индексов содержит этот символ, будет происходить путаница (но когда вы последний раз использовали этот символ в строке?).

BEGIN {
    Colours[1,1] = "Red"
    Colours[1,2] = "Green"
    Colours[1,3] = "Blue"
    Colours[2,1] = "Yellow"
    Colours[2,2] = "Cyan"
    Colours[2,3] = "Purple"
}
END {
    for (i = 1; i <= 2; i++)
        for (j = 1; j <= 3; j++)
            printf "Colours[%d,%d] = %s\n", i, j, Colours[i,j];
}

Пример выполнения:

$ awk -f so14063783.awk /dev/null
Colours[1,1] = Red
Colours[1,2] = Green
Colours[1,3] = Blue
Colours[2,1] = Yellow
Colours[2,2] = Cyan
Colours[2,3] = Purple
$

Ответ 3

Если у вас GNU awk, вы можете использовать истинный многомерный массив. Хотя этот ответ использует функцию split(), он, безусловно, не злоупотребляет им. Выполнить как:

awk -f script.awk

Содержание script.awk:

BEGIN {

    x=SUBSEP

    a="Red" x "Green" x "Blue"
    b="Yellow" x "Cyan" x "Purple"

    Colors[1][0] = ""
    Colors[2][0] = ""

    split(a, Colors[1], x)
    split(b, Colors[2], x)

    print Colors[2][3]
}

Результаты:

Purple

Ответ 4

Аналогичное решение. SUBSEP=":" на самом деле не нужен, просто установите для любого видимого char для демонстрации:

awk 'BEGIN{SUBSEP=":"
split("Red Green Blue",a); for(i in a) Colors[1,i]=a[i];
split("Yellow Cyan Purple",a); for(i in a) Colors[2,i]=a[i];
for(i in Colors) print i" => "Colors[i];}'

Или немного более загадочная версия:

awk 'BEGIN{SUBSEP=":"
split("Red Green Blue Yellow Cyan Purple",a); 
for(i in a) Colors[int((i-1)/3)+1,(i-1)%3+1]=a[i];
for(i in Colors) print i" => "Colors[i];}'

Вывод:

1:1 => Red
1:2 => Green
1:3 => Blue
2:1 => Yellow
2:2 => Cyan
2:3 => Purple

Ответ 5

Существующие ответы полезны и вместе охватывают все аспекты, но я думал, что дам более целенаправленное резюме.

Вопрос состоит из двух аспектов:

  • инициализация массивов в Awk вообще
  • делая это, чтобы заполнить двумерный массив, в частности

Инициализация массива:

Awk имеет синтаксис без литерала (инициализатора).

Простейшим обходным путем является:

  • представляют элементы массива как одну строку и
  • используйте функцию split() для разделения этой строки на элементы массива.
$ awk 'BEGIN { n=split("Red Green Blue", arr); for (i=1;i<=n;++i) print arr[i] }'
Red
Green
Blue

Это то, что OP сделал в свой собственный полезный ответ.

Если сами элементы содержат пробелы, используйте специальный разделитель, который не является частью данных, | в этом примере:

$ awk 'BEGIN { n=split("Red (1)|Green (2)", arr, "|"); for (i=1;i<=n;++i) print arr[i] }'
Red (1)
Green (2)

Инициализация двумерного массива:

  • В POSIX Awk имеет нет истинных многомерных массивов, только эмуляцию, используя одномерный массив, индексы которого неявно объединены с значение встроенной переменной SUBSEP для формирования одного ключа (индекс, обратите внимание, что все массивы Awk являются ассоциативными).

    • arr[1, 2] фактически совпадает с arr[1 SUBSEP 2], где 1 SUBSEP 2 представляет собой конкатенацию строк, которая строит значение ключа.
    • Поскольку не существует по-настоящему нескольких измерений - только плоский массив составных ключей, вы не можете перечислять (псевдо) измерения индивидуально с помощью for (i in ...), например, чтобы получить все подиндексы для первичного (псевдо) измерения 1.
    • Значением SUBSEP по умолчанию является символ "ИНФОРМАЦИОННЫЙ СЕПАРАТОР ОДИН" , редко используемый контрольный символ, который вряд ли появится в дате; в ASCII и UTF-8 он представлен как один байт 0x1f; если необходимо, вы изменяете значение.
  • В отличие от GNU Awk, как нестандартного расширения, есть поддержка истинных многомерных массивов.

    • Важно. Вы должны всегда указывать индексы отдельно; например, вместо arr[1,2] вы должны использовать arr[1][2].

POSIX-совместимый пример (аналогичный TrueY полезный ответ):

awk 'BEGIN {
  n=split("Red Green Blue", arrAux); for (i in arrAux) Colors[1,i] = arrAux[i]
  n=split("Yellow Cyan Purple", arrAux); for (i in arrAux) Colors[2,i] = arrAux[i]
  print Colors[1,2]
  print "---"
  # Enumerate all [2,*] values - see comments below.
  for (i in Colors) { if (index(i, 2 SUBSEP)==1) print Colors[i] }
}'
Green
---
Yellow
Cyan
Purple

Обратите внимание, что эмуляция многомерных массивов с одномерным массивом с использованием составных ключей имеет следующие <сильные > неудобные последствия:

  • Необходим вспомогательный массив auxArr, потому что вы не можете напрямую заполнить заданное (псевдо) измерение массива.

  • Вы не можете перечислить только одно (псевдо) измерение с помощью for (i in ...), вы можете только перечислять все индексы, через (псевдо) измерения.

    • for (i in Colors) { if (index(i, 2 SUBSEP)==1) print Colors[i] } выше показывает, как обойти это, перечисляя все ключи, а затем сопоставляя только те, чей первый составляющий индекс 2, что означает, что значение ключа должно начинаться с 2, а затем SUBSEP.

Пример GNU Awk (аналогичный Стив полезный ответ, улучшенный с Эд Мортон комментарий):

Поддержка GNU Awk (нестандартная) для истинных многомерных массивов делает неудобства для POSIX-совместимого решения (в основном) уходить
(Однако, GNU Awk также не имеет инициализаторов массива):

gawk 'BEGIN {
  Colors[1][""]; split("Red Green Blue", Colors[1])
  Colors[2][""]; split("Yellow Cyan Purple", Colors[2])
  # NOTE: Always use *separate* indices: [1][2] instead of [1,2]
  print Colors[1][2]
  print "---"
  # Enumerate all [2][*] values
  for (i in Colors[2]) print Colors[2][i]
}'

Примечание:

  • Важно. Как указано, для обращения к определенному элементу в многомерном массиве всегда используйте отдельные индексы; например, [1][2], а не [1,2].

    • Если вы используете [1,2], вы получите стандартное поведение, заданное POSIX, и вы ошибочно создадите новый единичный индекс (ключ) с (строковым конкатенированным) значением 1 SUBSEP 2.
  • split() удобно использовать для непосредственного заполнения подматрицы.

  • В качестве предпосылки, однако, должны быть инициализированы двумерные целевые массивы:

    • Colors[1][""] и Colors[2][""] делают именно это.
    • Dummy index [""] - это просто для создания двумерного массива; он отбрасывается, когда split() заполняет это измерение позже.
  • Поддерживается перечисление определенного размера с помощью for (i in ...):

    • for (i in Colors[2]) ... удобно перечисляет только субиндексы Colors[2].