跳轉到內容

Awk 入門/陣列

來自華夏公益教科書,開放的書籍,為開放的世界

Awk 也允許使用陣列。那些以前編寫過程式的人已經熟悉陣列。對於那些沒有接觸過陣列的人來說,陣列只是一個儲存多個值的單個變數,類似於列表。命名約定與變數相同,並且,與變數一樣,陣列不需要宣告。

Awk 陣列只能有一維;第一個索引為 1。陣列元素透過索引來識別,索引包含在方括號中。例如

some_array[1] = "Hello"
some_array[2] = "Everybody"
some_array[3] = "!"
print some_array[1], some_array[2], some_array[3]

括號內的數字是索引。它選擇陣列內的特定條目,或元素,然後可以像普通變數一樣建立、訪問或修改它。

關聯陣列

[編輯 | 編輯原始碼]

這是熟悉 C 風格陣列的人學習新事物的地方。Awk 陣列很有趣,因為它們實際上是關聯陣列。索引實際上是字串,因此關聯陣列更像字典而不是編號列表。例如,陣列可以用來統計一組債務人所欠的錢,如下所示

debts["Kim"] = 50
debts["Roberto"] += 70
debts["Vic"] -= 30
print "Vic paid 30 dollars, but still owes", debts["Vic"]

C 風格陣列(表現得像編號列表)和關聯陣列(表現得像字典)之間存在一些差異

C 風格陣列 關聯陣列
  • 固定長度
  • 整數索引
  • 索引從 0 開始
  • 多維
  • 可變長度
  • 字串索引
  • 稀疏且無序
  • 單維

Awk 只有關聯陣列,沒有 C 風格陣列。這種比較只針對那些從其他程式語言學習陣列的人。

陣列細節

[編輯 | 編輯原始碼]

讓我們回顧一下 Awk 陣列的一些更具體的特徵

可變長度

[編輯 | 編輯原始碼]

Awk 中的陣列可以在程式執行過程中增長和縮小。每當訪問 Awk 之前從未見過的索引時,新條目會自動建立。無需讓 Awk 知道您打算使用多少個元素。

message[1]="Have a nice"
message[2]="day."
print message[1], message[2], message[3]

在這個例子中,陣列 message 中的元素 1 和 2 在我們為它們分配值的那一刻就被建立。在最後一行,訪問 message[3],儘管它還沒有建立。Awk 將建立元素 message[3] 並將其初始化為空字串(因此什麼都不會出現)。

此外,可以從陣列中刪除元素。以下是上述示例的擴充套件

delete message[2]
message[3]="night."
print message[1], message[2], message[3]

現在,message[2] 不再存在。就好像您從未在第一次為它賦值一樣,因此 Awk 將對它的提及視為空字串。如果您一起執行這兩個示例,結果將是

Have a nice day.
Have a nice night.

(注意 print 語句中的逗號如何在陣列元素之間新增空格。這可以透過設定內建變數 OFS 來更改。)

一些實現,比如 gawk 或 mawk,也允許程式設計師刪除整個陣列而不是單個元素。在

delete message

之後,陣列 message 不再存在。

字串索引

[編輯 | 編輯原始碼]

您已經看到 Awk 陣列使用字串來選擇陣列中的每個元素,就像字典一樣。

translate["table"] = "mesa"
translate["chair"] = "silla"
translate["good"] = "bueno"

但是,數字是完全可以接受的。像往常一樣,Awk 只會在需要時將數字轉換為字串。

translate[1] = "uno"
translate[5] = "cinco"

然而,當用小數訪問陣列時,事情可能會變得棘手。

problems[ (1/3) ] = "one third"

您可以使用 problems[0.333] 訪問此元素嗎?不。這取決於內建變數 OFMT 的內容,它告訴 Awk 如何將數字轉換為字串。將轉換一定數量的小數位數,其餘的將被丟棄。一般來說,儘量避免使用帶小數的索引,除非您非常小心地使用正確的格式(可以更改)。

稀疏性和無序性

[編輯 | 編輯原始碼]

Awk 陣列是稀疏的,這意味著您可以有元素 1 和元素 3 而沒有元素 2。這很明顯——Awk 使用字串索引,因此它不區分編號元素。

更重要的是,關聯陣列中的元素不會按任何特定順序儲存。

有兩個有用的命令允許您檢查陣列中的元素。我們將在接下來的章節中詳細瞭解它們,但現在讓我們看一些示例。

if( "Kane" in debts )
    print "Kane owes", debts["Kane"]
for( person in debts )
    print person, "owes", debts[person]

回到介紹(其中建立了一個 debts 陣列將人們的姓名與金額相關聯),我們可以看到 Awk 提供了一些有用的命令來訪問陣列。第一個命令,if in,讓我們檢查特定元素是否已定義,然後根據該結果執行程式碼。第二個命令,for in,讓我們建立一個臨時變數(在本例中稱為 person),並在陣列中的每個元素上重複一條語句。

玩弄這些例子,看看 Awk 如何不一定會在其陣列中保持特定的順序。幸運的是,這從來都不是真正的問題。

Awk 陣列僅為單維。這意味著只有一個索引。但是,有一個名為 SUBSEP 的內建變數,除非您更改它,否則它等於 "@"。如果您希望建立一個多維陣列,其中每個元素有兩個或多個索引,您可以用逗號隔開它們。

array["NY", "capital"] = "Albany"
array["NY", "big city"] = "New York City"
array["OR", "capital"] = "Salem"
array["OR", "big city"] = "Portland"

這些程式碼行與以下程式碼完全相同

array["NY@capital"] = "Albany"
array["NY@big city"] = "New York City"
array["OR@capital"] = "Salem"
array["OR@big city"] = "Portland"

這只是多維陣列的快速演示。如您所見,這些實際上不是多維的;而是單維的,但使用特殊的分隔符。此處不會進一步探討多維陣列,因為必須理解幾個技術細節。您仍然可以在沒有它們的情況下編寫有用的 Awk 程式,但如果您對多維陣列感興趣,請隨時查閱您的 Awk 手冊(或者只是玩弄一下看看什麼有效)。

標準 Awk 中沒有用於陣列的函式。但是,gawk 提供了三個函式(A 和 B 應該都是陣列)

  • length(A) 返回 A 的長度。
  • asort(A[,B]) - 如果未給出 B,則對 A 進行排序。A 的索引將被替換為從 1 開始的連續整數。如果給出 B,則將 A 複製到 B,然後按上述方式對 B 進行排序,而 A 保持不變。返回 A 的長度。
  • asorti(A[,B]) - 如果未給出 B,則丟棄 A 的值並對它的索引進行排序。排序後的索引將成為新的值,從 1 開始的連續整數將成為新的索引。與前一種情況類似,如果給出 B,則將 A 複製到 B,然後按上述方式對 B 的索引進行排序,而 A 保持不變。返回 A 的長度。
  1. 更新我們在本書開頭編寫的“coins”程式。使用陣列來統計按國家分類的硬幣數量。顯示結果以及摘要。
  2. 編寫債務人程式。它應該掃描一個日誌檔案,該檔案列出了像“Jim owes 50”和“Kim paid 30”這樣的交易。使用關聯陣列,對人們借入和償還的所有錢進行彙總。確保一個人可以在檔案中多次出現,並且他們的債務會得到相應的更新。在 END 處,列出每個人及其總額。
  3. 改進#2中的程式,如果一個人償還了所有欠款,則從陣列中刪除他們的姓名。這樣,結果就不會被欠零元的人弄得亂七八糟。

下一頁快速回顧了 Awk 提供的所有運算子

華夏公益教科書