awk線上例項入門教程 第3部分
awk線上例項入門教程 第三部分
——字串函式和……支票簿?
格式化輸出
雖然大多數情況下 awk 的 print 語句可以完成任務,但有時我們還需要更多。在那些情況下,awk 提供了兩個我們熟知的老朋友 printf() 和 sprintf()。是的,如同其它許多 awk 部件一樣,這些函式等同於相應的 C 語言函式。printf() 會將格式化字串列印到 stdout,而 sprintf() 則返回可以賦值給變數的格式化字串。如果不熟悉 printf() 和 sprintf(),介紹 C 語言的文章可以讓您迅速瞭解這兩個基本列印函式。在 Linux 系統上,可以輸入 "man 3 printf" 來檢視 printf() 幫助頁面。
以下是一些 awk sprintf() 和 printf() 的樣本程式碼。可以看到,它們幾乎與 C 語言完全相同。 此程式碼將列印:
字串函式
awk 有許多字串函式,這是件好事。在 awk 中,確實需要字串函式,因為不能象在其它語言(如 C、C++ 和 Python)中那樣將字串看作是字元陣列。例如,如果執行以下程式碼: 將會接收到一個錯誤,如下所示:
噢,好吧。雖然不象 Python 的序列型別那樣方便,但 awk 的字串函式還是可以完成任務。讓我們來看一下。
首先,有一個基本 length() 函式,它返回字串的長度。以下是它的使用方法: 此程式碼將列印值:
好,繼續。下一個字串函式叫作 index,它將返回子字串在另一個字串中出現的位置,如果沒有找到該字串則返回 0。使用 mystring,可以按以下方法呼叫它: awk 會列印:
讓我們繼續討論另外兩個簡單的函式,tolower() 和 toupper()。與您猜想的一樣,這兩個函式將返回字串並且將所有字元分別轉換成小寫或大寫。請注意,tolower() 和 toupper() 返回新的字串,不會修改原來的字串。這段程式碼: ……將產生以下輸出:
到現在為止一切不錯,但我們究竟如何從字串中選擇子串,甚至單個字元?那就是使用 substr() 的原因。以下是 substr() 的呼叫方法: mystring 應該是要從中抽取子串的字串變數或文字字串。startpos 應該設定成起始字元位置,maxlen 應該包含要抽取的字串的最大長度。請注意,我說的是 最大長度 ;如果 length(mystring) 比 startpos+maxlen 短,那麼得到的結果就會被截斷。substr() 不會修改原始字串,而是返回子串。以下是一個示例: awk 將列印:
如果您通常用於程式設計的語言使用陣列下標訪問部分字串(以及不使用這種語言的人),請記住 substr() 是 awk 代替方法。需要使用它來抽取單個字元和子串;因為 awk 是基於字串的語言,所以會經常用到它。
現在,我們討論一些更耐人尋味的函式,首先是 match()。match() 與 index() 非常相似,它與 index() 的區別在於它並不搜尋子串,它搜尋的是規則表示式。match() 函式將返回匹配的起始位置,如果沒有找到匹配,則返回 0。此外,match() 還將設定兩個變數,叫作 RSTART 和 RLENGTH。RSTART 包含返回值(第一個匹配的位置),RLENGTH 指定它佔據的字元跨度(如果沒有找到匹配,則返回 -1)。透過使用 RSTART、RLENGTH、substr() 和一個小迴圈,可以輕鬆地迭代字串中的每個匹配。以下是一個 match() 呼叫示例: awk 將列印:
字串替換
現在,我們將研究兩個字串替換函式,sub() 和 gsub()。這些函式與目前已經討論過的函式略有不同,因為它們 確實修改原始字串 。以下是一個模板,顯示瞭如何呼叫 sub(): 呼叫 sub() 時,它將在 mystring 中匹配 regexp 的第一個字元序列,並且用 replstring 替換該序列。sub() 和 gsub() 用相同的自變數;唯一的區別是 sub() 將替換第一個 regexp 匹配(如果有的話),gsub() 將執行全域性替換,換出字串中的所有匹配。以下是一個 sub() 和 gsub() 呼叫示例: 必須將 mystring 復位成其初始值,因為第一個 sub() 呼叫直接修改了 mystring。在執行時,此程式碼將使 awk 輸出:
當然,也可以是更復雜的規則表示式。我把測試一些複雜規則表示式的任務留給您來完成。
透過介紹函式 split(),我們來彙總一下已討論過的函式。split() 的任務是“切開”字串,並將各部分放到使用整數下標的陣列中。以下是一個 split() 呼叫示例: 呼叫 split() 時,第一個自變數包含要切開文字字串或字串變數。在第二個自變數中,應該指定 split() 將填入片段部分的陣列名稱。在第三個元素中,指定用於切開字串的分隔符。split() 返回時,它將返回分割的字串元素的數量。split() 將每一個片段賦值給下標從 1 開始的陣列,因此以下程式碼: ……將列印:
特殊字串形式
簡短註釋 -- 呼叫 length()、sub() 或 gsub() 時,可以去掉最後一個自變數,這樣 awk 將對 $0(整個當前行)應用函式呼叫。要列印檔案中每一行的長度,使用以下 awk 指令碼: 財務上的趣事
幾星期前,我決定用 awk 編寫自己的支票簿結算程式。我決定使用簡單的 tab 定界文字檔案,以便於輸入最近的存款和提款記錄。其思路是將這個資料交給 awk 指令碼,該指令碼會自動合計所有金額,並告訴我餘額。以下是我決定如何將所有交易記錄到 "ASCII checkbook" 中:
此檔案中的每個欄位都由一個或多個 tab 分隔。在日期(欄位 1,$1)之後,有兩個欄位叫做“費用分類帳”和“收入分類帳”。以上面這行為例,輸入費用時,我在費用欄位中放入四個字母的別名,在收入欄位中放入 "-"(空白項)。這表示這一特定項是“食品費用”。:) 以下是存款的示例:
在這個例項中,我在費用分類帳中放入 "-"(空白),在收入分類帳中放入 "inco"。"inco" 是一般(薪水之類)收入的別名。使用分類帳別名讓我可以按類別生成收入和費用的明細分類帳。至於記錄的其餘部分,其它所有欄位都是不需加以說明的。“是否付清?”欄位("Y" 或 "N")記錄了交易是否已過帳到我的帳戶;除此之外,還有一個交易描述,和一個正的美元金額。
用於計算當前餘額的演算法不太難。awk 只需要依次讀取每一行。如果列出了費用分類帳,但沒有收入分類帳(為 "-"),那麼這一項就是借方。如果列出了收入分類帳,但沒有費用分類帳(為 "-"),那麼這一項就是貸方。而且,如果同時列出了費用和收入分類帳,那麼這個金額就是“分類帳轉帳”;即,從費用分類帳減去美元金額,並將此金額新增到收入分類帳。此外,所有這些分類帳都是虛擬的,但對於跟蹤收入和支出以及預算卻非常有用。
程式碼
現在該研究程式碼了。我們將從第一行(BEGIN 塊和函式定義)開始:
balance,第 1 部分 首先執行 "chmod +x myscript" 命令,那麼將第一行 "#!..." 新增到任何 awk 指令碼將使它可以直接從 shell 中執行。其餘行定義了 BEGIN 塊,在 awk 開始處理支票簿檔案之前將執行這個程式碼塊。我們將 FS(欄位分隔符)設定成 "t+",它會告訴 awk 欄位由一個或多個 tab 分隔。另外,我們定義了字串 months,下面將出現的 monthdigit() 函式將使用它。
最後三行顯示瞭如何定義自己的 awk 。格式很簡單 -- 輸入 "function",再輸入名稱,然後在括號中輸入由逗號分隔的引數。在此之後,"{ }" 程式碼塊包含了您希望這個函式執行的程式碼。所有函式都可以訪問全域性變數(如 months 變數)。另外,awk 提供了 "return" 語句,它允許函式返回一個值,並執行類似於 C 和其它語言中 "return" 的操作。這個特定函式將以 3 個字母字串格式表示的月份名稱轉換成等價的數值。例如,以下程式碼: ……將列印:
現在,讓我們討論其它一些函式。
財務函式
以下是其它三個執行簿記的函式。我們即將見到的主程式碼塊將呼叫這些函式之一,按順序處理支票簿檔案的每一行,從而將相應交易記錄到 awk 陣列中。有三種基本交易,貸方 (doincome)、借方 (doexpense) 和轉帳 (dotransfer)。您會發現這三個函式全都接受一個自變數,叫作 mybalance。mybalance 是二維陣列的一個佔位符,我們將它作為自變數進行傳遞。目前,我們還沒有處理過二維陣列;但是,在下面可以看到,語法非常簡單。只須用逗號分隔每一維就行了。
我們將按以下方式將資訊記錄到 "mybalance" 中。陣列的第一維從 0 到 12,用於指定月份,0 代表全年。第二維是四個字母的分類帳,如 "food" 或 "inco";這是我們處理的真實分類帳。因此,要查詢全年食品分類帳的餘額,應檢視 mybalance[0,"food"]。要查詢 6 月的收入,應檢視 mybalance[6,"inco"]。
balance,第 2 部分 呼叫 doincome() 或任何其它函式時,我們將交易記錄到兩個位置 -- mybalance[0,category] 和 mybalance[curmonth, category],它們分別表示全年的分類帳餘額和當月的分類帳餘額。這讓我們稍後可以輕鬆地生成年度或月度收入/支出明細分類帳。
如果研究這些函式,將發現在我的引用中傳遞了 mybalance 引用的陣列。另外,我們還引用了幾個全域性變數:curmonth,它儲存了當前記錄所屬的月份的數值,$2(費用分類帳),$3(收入分類帳)和金額($7,美元金額)。呼叫 doincome() 和其它函式時,已經為要處理的當前記錄(行)正確設定了所有這些變數。
主塊
以下是主程式碼塊,它包含了分析每一行輸入資料的程式碼。請記住,由於正確設定了 FS,可以用 $ 1 引用第一個欄位,用 $2 引用第二個欄位,依次類推。呼叫 doincome() 和其它函式時,這些函式可以從函式內部訪問 curmonth、$2、$3 和金額的當前值。請先研究程式碼,在程式碼之後可以見到我的說明。
balance,第 3 部分 在主塊中,前兩行將 curmonth 設定成 1 到 12 之間的整數,並將金額設定成欄位 7(使程式碼易於理解)。然後,是四行有趣的程式碼,它們將值寫到陣列 globcat 中。globcat,或稱作全域性分類帳陣列,用於記錄在檔案中遇到的所有分類帳 -- "inco"、"misc"、"food"、"util" 等。例如,如果 $2 == "inco",則將 globcat["inco"] 設定成 "yes"。稍後,我們可以使用簡單的 "for (x in globcat)" 迴圈來迭代分類帳列
——字串函式和……支票簿?
格式化輸出
雖然大多數情況下 awk 的 print 語句可以完成任務,但有時我們還需要更多。在那些情況下,awk 提供了兩個我們熟知的老朋友 printf() 和 sprintf()。是的,如同其它許多 awk 部件一樣,這些函式等同於相應的 C 語言函式。printf() 會將格式化字串列印到 stdout,而 sprintf() 則返回可以賦值給變數的格式化字串。如果不熟悉 printf() 和 sprintf(),介紹 C 語言的文章可以讓您迅速瞭解這兩個基本列印函式。在 Linux 系統上,可以輸入 "man 3 printf" 來檢視 printf() 幫助頁面。
以下是一些 awk sprintf() 和 printf() 的樣本程式碼。可以看到,它們幾乎與 C 語言完全相同。 此程式碼將列印:
Jim got a 83 on the last test
foo-1
字串函式
awk 有許多字串函式,這是件好事。在 awk 中,確實需要字串函式,因為不能象在其它語言(如 C、C++ 和 Python)中那樣將字串看作是字元陣列。例如,如果執行以下程式碼: 將會接收到一個錯誤,如下所示:
awk: string.gawk:59: fatal: attempt to use scalar as array
噢,好吧。雖然不象 Python 的序列型別那樣方便,但 awk 的字串函式還是可以完成任務。讓我們來看一下。
首先,有一個基本 length() 函式,它返回字串的長度。以下是它的使用方法: 此程式碼將列印值:
24
好,繼續。下一個字串函式叫作 index,它將返回子字串在另一個字串中出現的位置,如果沒有找到該字串則返回 0。使用 mystring,可以按以下方法呼叫它: awk 會列印:
9
讓我們繼續討論另外兩個簡單的函式,tolower() 和 toupper()。與您猜想的一樣,這兩個函式將返回字串並且將所有字元分別轉換成小寫或大寫。請注意,tolower() 和 toupper() 返回新的字串,不會修改原來的字串。這段程式碼: ……將產生以下輸出:
how are you doing today?
HOW ARE YOU DOING TODAY?
How are you doing today?
到現在為止一切不錯,但我們究竟如何從字串中選擇子串,甚至單個字元?那就是使用 substr() 的原因。以下是 substr() 的呼叫方法: mystring 應該是要從中抽取子串的字串變數或文字字串。startpos 應該設定成起始字元位置,maxlen 應該包含要抽取的字串的最大長度。請注意,我說的是 最大長度 ;如果 length(mystring) 比 startpos+maxlen 短,那麼得到的結果就會被截斷。substr() 不會修改原始字串,而是返回子串。以下是一個示例: awk 將列印:
you
如果您通常用於程式設計的語言使用陣列下標訪問部分字串(以及不使用這種語言的人),請記住 substr() 是 awk 代替方法。需要使用它來抽取單個字元和子串;因為 awk 是基於字串的語言,所以會經常用到它。
現在,我們討論一些更耐人尋味的函式,首先是 match()。match() 與 index() 非常相似,它與 index() 的區別在於它並不搜尋子串,它搜尋的是規則表示式。match() 函式將返回匹配的起始位置,如果沒有找到匹配,則返回 0。此外,match() 還將設定兩個變數,叫作 RSTART 和 RLENGTH。RSTART 包含返回值(第一個匹配的位置),RLENGTH 指定它佔據的字元跨度(如果沒有找到匹配,則返回 -1)。透過使用 RSTART、RLENGTH、substr() 和一個小迴圈,可以輕鬆地迭代字串中的每個匹配。以下是一個 match() 呼叫示例: awk 將列印:
9 9 3
字串替換
現在,我們將研究兩個字串替換函式,sub() 和 gsub()。這些函式與目前已經討論過的函式略有不同,因為它們 確實修改原始字串 。以下是一個模板,顯示瞭如何呼叫 sub(): 呼叫 sub() 時,它將在 mystring 中匹配 regexp 的第一個字元序列,並且用 replstring 替換該序列。sub() 和 gsub() 用相同的自變數;唯一的區別是 sub() 將替換第一個 regexp 匹配(如果有的話),gsub() 將執行全域性替換,換出字串中的所有匹配。以下是一個 sub() 和 gsub() 呼叫示例: 必須將 mystring 復位成其初始值,因為第一個 sub() 呼叫直接修改了 mystring。在執行時,此程式碼將使 awk 輸出:
HOw are you doing today?
HOw are yOu dOing tOday?
當然,也可以是更復雜的規則表示式。我把測試一些複雜規則表示式的任務留給您來完成。
透過介紹函式 split(),我們來彙總一下已討論過的函式。split() 的任務是“切開”字串,並將各部分放到使用整數下標的陣列中。以下是一個 split() 呼叫示例: 呼叫 split() 時,第一個自變數包含要切開文字字串或字串變數。在第二個自變數中,應該指定 split() 將填入片段部分的陣列名稱。在第三個元素中,指定用於切開字串的分隔符。split() 返回時,它將返回分割的字串元素的數量。split() 將每一個片段賦值給下標從 1 開始的陣列,因此以下程式碼: ……將列印:
Jan Dec
特殊字串形式
簡短註釋 -- 呼叫 length()、sub() 或 gsub() 時,可以去掉最後一個自變數,這樣 awk 將對 $0(整個當前行)應用函式呼叫。要列印檔案中每一行的長度,使用以下 awk 指令碼: 財務上的趣事
幾星期前,我決定用 awk 編寫自己的支票簿結算程式。我決定使用簡單的 tab 定界文字檔案,以便於輸入最近的存款和提款記錄。其思路是將這個資料交給 awk 指令碼,該指令碼會自動合計所有金額,並告訴我餘額。以下是我決定如何將所有交易記錄到 "ASCII checkbook" 中:
23 Aug 2000 food - - Y Jimmy's Buffet 30.25
此檔案中的每個欄位都由一個或多個 tab 分隔。在日期(欄位 1,$1)之後,有兩個欄位叫做“費用分類帳”和“收入分類帳”。以上面這行為例,輸入費用時,我在費用欄位中放入四個字母的別名,在收入欄位中放入 "-"(空白項)。這表示這一特定項是“食品費用”。:) 以下是存款的示例:
23 Aug 2000 - inco - Y Boss Man 2001.00
在這個例項中,我在費用分類帳中放入 "-"(空白),在收入分類帳中放入 "inco"。"inco" 是一般(薪水之類)收入的別名。使用分類帳別名讓我可以按類別生成收入和費用的明細分類帳。至於記錄的其餘部分,其它所有欄位都是不需加以說明的。“是否付清?”欄位("Y" 或 "N")記錄了交易是否已過帳到我的帳戶;除此之外,還有一個交易描述,和一個正的美元金額。
用於計算當前餘額的演算法不太難。awk 只需要依次讀取每一行。如果列出了費用分類帳,但沒有收入分類帳(為 "-"),那麼這一項就是借方。如果列出了收入分類帳,但沒有費用分類帳(為 "-"),那麼這一項就是貸方。而且,如果同時列出了費用和收入分類帳,那麼這個金額就是“分類帳轉帳”;即,從費用分類帳減去美元金額,並將此金額新增到收入分類帳。此外,所有這些分類帳都是虛擬的,但對於跟蹤收入和支出以及預算卻非常有用。
程式碼
現在該研究程式碼了。我們將從第一行(BEGIN 塊和函式定義)開始:
balance,第 1 部分 首先執行 "chmod +x myscript" 命令,那麼將第一行 "#!..." 新增到任何 awk 指令碼將使它可以直接從 shell 中執行。其餘行定義了 BEGIN 塊,在 awk 開始處理支票簿檔案之前將執行這個程式碼塊。我們將 FS(欄位分隔符)設定成 "t+",它會告訴 awk 欄位由一個或多個 tab 分隔。另外,我們定義了字串 months,下面將出現的 monthdigit() 函式將使用它。
最後三行顯示瞭如何定義自己的 awk 。格式很簡單 -- 輸入 "function",再輸入名稱,然後在括號中輸入由逗號分隔的引數。在此之後,"{ }" 程式碼塊包含了您希望這個函式執行的程式碼。所有函式都可以訪問全域性變數(如 months 變數)。另外,awk 提供了 "return" 語句,它允許函式返回一個值,並執行類似於 C 和其它語言中 "return" 的操作。這個特定函式將以 3 個字母字串格式表示的月份名稱轉換成等價的數值。例如,以下程式碼: ……將列印:
3
現在,讓我們討論其它一些函式。
財務函式
以下是其它三個執行簿記的函式。我們即將見到的主程式碼塊將呼叫這些函式之一,按順序處理支票簿檔案的每一行,從而將相應交易記錄到 awk 陣列中。有三種基本交易,貸方 (doincome)、借方 (doexpense) 和轉帳 (dotransfer)。您會發現這三個函式全都接受一個自變數,叫作 mybalance。mybalance 是二維陣列的一個佔位符,我們將它作為自變數進行傳遞。目前,我們還沒有處理過二維陣列;但是,在下面可以看到,語法非常簡單。只須用逗號分隔每一維就行了。
我們將按以下方式將資訊記錄到 "mybalance" 中。陣列的第一維從 0 到 12,用於指定月份,0 代表全年。第二維是四個字母的分類帳,如 "food" 或 "inco";這是我們處理的真實分類帳。因此,要查詢全年食品分類帳的餘額,應檢視 mybalance[0,"food"]。要查詢 6 月的收入,應檢視 mybalance[6,"inco"]。
balance,第 2 部分 呼叫 doincome() 或任何其它函式時,我們將交易記錄到兩個位置 -- mybalance[0,category] 和 mybalance[curmonth, category],它們分別表示全年的分類帳餘額和當月的分類帳餘額。這讓我們稍後可以輕鬆地生成年度或月度收入/支出明細分類帳。
如果研究這些函式,將發現在我的引用中傳遞了 mybalance 引用的陣列。另外,我們還引用了幾個全域性變數:curmonth,它儲存了當前記錄所屬的月份的數值,$2(費用分類帳),$3(收入分類帳)和金額($7,美元金額)。呼叫 doincome() 和其它函式時,已經為要處理的當前記錄(行)正確設定了所有這些變數。
主塊
以下是主程式碼塊,它包含了分析每一行輸入資料的程式碼。請記住,由於正確設定了 FS,可以用 $ 1 引用第一個欄位,用 $2 引用第二個欄位,依次類推。呼叫 doincome() 和其它函式時,這些函式可以從函式內部訪問 curmonth、$2、$3 和金額的當前值。請先研究程式碼,在程式碼之後可以見到我的說明。
balance,第 3 部分 在主塊中,前兩行將 curmonth 設定成 1 到 12 之間的整數,並將金額設定成欄位 7(使程式碼易於理解)。然後,是四行有趣的程式碼,它們將值寫到陣列 globcat 中。globcat,或稱作全域性分類帳陣列,用於記錄在檔案中遇到的所有分類帳 -- "inco"、"misc"、"food"、"util" 等。例如,如果 $2 == "inco",則將 globcat["inco"] 設定成 "yes"。稍後,我們可以使用簡單的 "for (x in globcat)" 迴圈來迭代分類帳列
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24790158/viewspace-1040124/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- awk線上例項入門教程 第二部分
- awk線上例項入門教程 第一部分
- awk 入門教程
- 通用執行緒:Awk 例項,第 1 部分(轉)執行緒
- 《Flask 入門教程》第 3 章:模板Flask
- 15個實用的CSS線上例項教程CSS
- 《MySQL 入門教程》第 18 篇 連線查詢MySql
- React 入門例項教程React
- 5分鐘入門AWK
- Webpack 系列第 3 部分Web
- C# 小白菜鳥從入門到精通(第3版)教程C#
- 《Flask 入門教程》 第 2 章:Hello, Flask!Flask
- Web Components 入門例項教程Web
- Django入門指南-第1部分(環境搭建)Django
- Vue3快速入門教程Vue
- linux入門教程(3)(轉)Linux
- [譯] Xcode 和 LLDB 高階除錯教程:第 3 部分XCodeLLDB高階除錯
- 《MySQL 入門教程》第 09 篇 字串模式匹配MySql字串模式
- 《MySQL 入門教程》第 10 篇 資料排序MySql排序
- 《MySQL 入門教程》第 01 篇 MySQL 簡介MySql
- 《MySQL 入門教程》第 02 篇 MySQL 安裝MySql
- 《Flask 入門教程》第 6 章:模板優化Flask優化
- 《MySQL 入門教程》第 17 篇 MySQL 變數MySql變數
- 《MySQL 入門教程》第 19 篇 子查詢MySql
- 《HTMLCSSJavaScript網頁製作從入門到精通第3版》——2.7 水平線HTMLCSSJavaScript網頁
- D3入門教程翻譯
- Unity3D開發入門教程(二)—— Lua入門Unity3D
- Awk 20 分鐘入門介紹
- awk命令部分用法
- Nutch入門教程,第一部分
- 《MySQL 入門教程》第 07 篇 簡單查詢MySql
- 《MySQL 入門教程》第 08 篇 過濾條件MySql
- 《MySQL 入門教程》第 12 篇 分組統計MySql
- 《MySQL 入門教程》第 03 篇 管理資料庫MySql資料庫
- 《MySQL 入門教程》第 20 篇 通用表表示式MySql
- ArduinoYun快速入門教程第1章ArduinoYun概覽UI
- PHP框架Yii系列教程(一):入門例項PHP框架
- scrapy入門教程3:scrapy的shell命令