awk基礎知識小結(2)

10、循環結構
循環結構
awk 的 while 循環結構,它等同於相應的 C 語言 while 循環。
awk 還有 "do...while" 循環,它在代碼塊結尾處對條件求值,而不象標準 while 循環那樣在開始處求值。
它類似於其它語言中的 "repeat...until" 循環。
示例:
do...while 示例 {
    count=1
    do {
   print "I get printed at least once no matter what"
   } while ( count != 1 )
}


與一般的 while 循環不同,由於在代碼塊之後對條件求值,"do...while" 循環永遠都至少執行一次。
換句話說,當第一次遇到普通 while 循環時,如果條件爲假,將永遠不執行該循環。

for 循環
awk 允許創建 for 循環,它就象 while 循環,也等同於 C 語言的 for 循環:
for ( initial assignment; comparison; increment ) {
code block
}

示例:
for ( x = 1; x <= 4; x++ ) {
   print "iteration",x
}

此段代碼將打印:
iteration 1
iteration 2
iteration 3
iteration 4

break 和 continue
此外,如同 C 語言一樣,awk 提供了 break 和 continue 語句。使用這些語句可以更好地控制 awk 的循環結構。以下是迫切需要 break 語句的代碼片斷:

while 死循環
while (1) {
 print "forever and ever..."
}
因爲 1 永遠代表是真,這個 while 循環將永遠運行下去。以下是一個只執行十次的循環:

break 語句示例 x=1
while(1) {
  print "iteration",x
  if ( x == 10 ) {
     break
  }
 x++
}

這裏,break 語句用於“逃出”最深層的循環。"break" 使循環立即終止,並繼續執行循環代碼塊後面的語句。
continue 語句補充了 break,其作用如下:
x=1
while (1) {
   if ( x == 4 ) {
     x++
     continue
    }
    print "iteration",x
    if ( x > 20 ) {
        break
    }
    x++
}

這段代碼打印 "iteration 1" 到 "iteration 21","iteration 4" 除外。如果迭代等於 4,則增加 x 並調用 continue 語句,該語句立即使 awk 開始執行下一個循環迭代,而不執行代碼塊的其餘部分。如同 break 一樣,continue 語句適合各種 awk 迭代循環。在 for 循環主體中使用時,continue 將使循環控制變量自動增加。以下是一個等價循環:
for ( x=1; x<=21; x++ ) {
   if ( x == 4 ) {
       continue
   }
   print "iteration",x
}

在 while 循環中時,在調用 continue 之前沒有必要增加 x,因爲 for 循環會自動增加 x。

數組
如果您知道 awk 可以使用數組,您一定會感到高興。然而,在 awk 中,數組下標通常從 1 開始,而不是 0:
myarray[1]="jim"
myarray[2]=456

awk 遇到第一個賦值語句時,它將創建 myarray,並將元素 myarray[1] 設置成 "jim"。執行了第二個賦值語句後,數組就有兩個元素了。

數組迭代

定義之後,awk 有一個便利的機制來迭代數組元素,如下所示:
for ( x in myarray ) {
 print myarray[x]
}

這段代碼將打印數組 myarray 中的每一個元素。當對於 for 使用這種特殊的 "in" 形式時,awk 將 myarray 的每個現有下標依次賦值給 x(循環控制變量),每次賦值以後都循環一次循環代碼。雖然這是一個非常方便的 awk 功能,但它有一個缺點 -- 當 awk 在數組下標之間輪轉時,它不會依照任何特定的順序。那就意味着我們不能知道以上代碼的輸出是:
jim
456
還是:
456
jim
迭代數組內容就像一盒巧克力-您永遠不知道將會得到什麼。

11、數組下標字符串化

數組下標字符串化
雖然 awk 要執行必要的轉換來完成這項工作,但它卻可以使用某些看起來很奇怪的代碼:
a="1"
b="2"
c=a+b+3
執行了這段代碼後,c 等於 6。由於 awk 是“字符串化”的,添加字符串 "1" 和 "2" 在功能上並不比添加數字 1 和 2 難。這兩種情況下,awk 都可以成功執行運算。awk 的“字符串化”性質非常可愛 -- 您可能想要知道如果使用數組的字符串下標會發生什麼情況。例如,以下代碼:
myarr["1"]="Mr. Whipple"
print myarr["1"]
可以預料,這段代碼將打印 "Mr. Whipple"。但如果去掉第二個 "1" 下標中的引號,情況又會怎樣呢?
myarr["1"]="Mr. Whipple"
print myarr[1]

猜想這個代碼片斷的結果比較難。awk 將 myarr["1"] 和 myarr[1] 看作數組的兩個獨立元素,還是它們是指同一個元素?答案是它們指的是同一個元素,awk 將打印 "Mr. Whipple",如同第一個代碼片斷一樣。雖然看上去可能有點怪,但 awk 在幕後卻一直使用數組的字符串下標!

瞭解了這個奇怪的真相之後,我們中的一些人可能想要執行類似於以下的古怪代碼:
myarr["name"]="Mr. Whipple"
print myarr["name"]

這段代碼不僅不會產生錯誤,而且它的功能與前面的示例完全相同,也將打印 "Mr. Whipple"!可以看到,awk 並沒有限制我們使用純整數下標;如果我們願意,可以使用字符串下標,而且不會產生任何問題。只要我們使用非整數數組下標,如 myarr["name"],那麼我們就在使用關聯數組。從技術上講,如果我們使用字符串下標,awk 的後臺操作並沒有什麼不同(因爲即便使用“整數”下標,awk 還是會將它看作是字符串)。但是,應該將它們稱作關聯數組 -- 它聽起來很酷,而且會給您的上司留下印象。字符串化下標是我們的小祕密。;)

數組工具
談到數組時,awk 給予我們許多靈活性。可以使用字符串下標,而且不需要連續的數字序列下標(例如,可以定義 myarr[1] 和 myarr[1000],但不定義其它所有元素)。雖然這些都很有用,但在某些情況下,會產生混淆。幸好,awk 提供了一些實用功能有助於使數組變得更易於管理。

首先,可以刪除數組元素。如果想要刪除數組 fooarray 的元素 1,輸入:
delete fooarray[1]

而且,如果想要查看是否存在某個特定數組元素,可以使用特殊的 "in" 布爾運算符,如下所示:
if ( 1 in fooarray ) {
print "Ayep!  It's there."
} else {
  print "Nope!  Can't find it."
}

12、格式化輸出

awk 提供了兩個函數printf() 和 sprintf()。如同其它許多 awk 部件一樣,這些函數等同於相應的 C 語言函數。
printf() 會將格式化字符串打印到 stdout,而 sprintf() 則返回可以賦值給變量的格式化字符串。
如果不熟悉 printf() 和 sprintf(),介紹 C 語言的文章可以讓您迅速瞭解這兩個基本打印函數。在 Linux 系統上,可以輸入 "man 3 printf" 來查看 printf() 幫助頁面。

以下是一些 awk sprintf() 和 printf() 的樣本代碼。可以看到,它們幾乎與 C 語言完全相同。
x=1
b="foo"
printf("%s got a %d on the last testn","Jim",83)
myout=("%s-%d",b,x)
print myout

此代碼將打印:
Jim got a 83 on the last test
foo-1


13、字符串函數

awk 有許多字符串函數。
在 awk 中,確實需要字符串函數,因爲不能象在其它語言(如 C、C++ 和 Python)中那樣將字符串看作是字符數組。
例如,如果執行以下代碼:
mystring="How are you doing today?"
print mystring[3]
將會接收到一個錯誤,如下所示:
awk: string.gawk:59: fatal: attempt to use scalar as array

雖然不象 Python 的序列類型那樣方便,但 awk 的字符串函數還是可以完成任務。讓我們來看一下。
首先,有一個基本 length() 函數,它返回字符串的長度。以下是它的使用方法:
print length(mystring)

此代碼將打印值:
24

下一個字符串函數叫作 index,它將返回子字符串在另一個字符串中出現的位置,如果沒有找到該字符串則返回 0。使用 mystring,可以按以下方法調用它:
print index(mystring,"you")

awk 會打印:
9

兩個簡單的函數,tolower() 和 toupper()。與您猜想的一樣,這兩個函數將返回字符串並且將所有字符分別轉換成小寫或大寫。請注意,tolower() 和 toupper() 返回新的字符串,不會修改原來的字符串。這段代碼:
print tolower(mystring)
print toupper(mystring)
print mystring
……
將產生以下輸出:
how are you doing today?
HOW ARE YOU DOING TODAY?
How are you doing today?

到現在爲止一切不錯,但我們究竟如何從字符串中選擇子串,甚至單個字符?那就是使用 substr() 的原因。以下是 substr() 的調用方法:
mysub=substr(mystring,startpos,maxlen)
mystring 應該是要從中抽取子串的字符串變量或文字字符串。startpos 應該設置成起始字符位置,maxlen 應該包含要抽取的字符串的最大長度。請注意,我說的是最大長度;如果 length(mystring) 比 startpos+maxlen 短,那麼得到的結果就會被截斷。substr() 不會修改原始字符串,而是返回子串。以下是一個示例:
print substr(mystring,9,3)

awk 將打印:
you
如果您通常用於編程的語言使用數組下標訪問部分字符串(以及不使用這種語言的人),請記住 substr() 是 awk 代替方法。
需要使用它來抽取單個字符和子串;因爲 awk 是基於字符串的語言,所以會經常用到它。

一些更耐人尋味的函數
首先是 match()。match() 與 index() 非常相似,它與 index() 的區別在於它並不搜索子串,它搜索的是規則表達式。match() 函數將返回匹配的起始位置,如果沒有找到匹配,則返回 0。此外,match() 還將設置兩個變量,叫作 RSTART 和 RLENGTH。RSTART 包含返回值(第一個匹配的位置),RLENGTH 指定它佔據的字符跨度(如果沒有找到匹配,則返回 -1)。通過使用 RSTART、RLENGTH、substr() 和一個小循環,可以輕鬆地迭代字符串中的每個匹配。以下是一個 match() 調用示例:
print match(mystring,/you/), RSTART, RLENGTH
awk 將打印:
9 9 3

字符串替換
現在,我們將研究兩個字符串替換函數,sub() 和 gsub()。這些函數與目前已經討論過的函數略有不同,因爲它們確實修改原始字符串。以下是一個模板,顯示瞭如何調用 sub():
sub(regexp,replstring,mystring)

調用 sub() 時,它將在 mystring 中匹配 regexp 的第一個字符序列,並且用 replstring 替換該序列。sub() 和 gsub() 用相同的自變量;唯一的區別是 sub() 將替換第一個 regexp 匹配(如果有的話),gsub() 將執行全局替換,換出字符串中的所有匹配。以下是一個 sub() 和 gsub() 調用示例:
sub(/o/,"O",mystring)
print mystring
mystring="How are you doing today?"
gsub(/o/,"O",mystring)
print mystring

必須將 mystring 復位成其初始值,因爲第一個 sub() 調用直接修改了 mystring。在執行時,此代碼將使 awk 輸出:
HOw are you doing today?
HOw are yOu dOing tOday?

當然,也可以是更復雜的規則表達式。我把測試一些複雜規則表達式的任務留給您來完成。

通過介紹函數 split(),我們來彙總一下已討論過的函數。split() 的任務是“切開”字符串,並將各部分放到使用整數下標的數組中。以下是一個 split() 調用示例:
numelements=split("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",mymonths,",")

調用 split() 時,第一個自變量包含要切開文字字符串或字符串變量。在第二個自變量中,應該指定 split() 將填入片段部分的數組名稱。在第三個元素中,指定用於切開字符串的分隔符。split() 返回時,它將返回分割的字符串元素的數量。split() 將每一個片段賦值給下標從 1 開始的數組,因此以下代碼:
print mymonths[1],mymonths[numelements]
……將打印:
Jan Dec

特殊字符串形式
簡短註釋 -- 調用 length()、sub() 或 gsub() 時,可以去掉最後一個自變量,這樣 awk 將對 $0(整個當前行)應用函數調用。要打印文件中每一行的長度,使用以下 awk 腳本:
{
 print length()
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章