[TCL] 基本語法與指令 - 3. 資料型態

TCL 語言的基本資料型態有:
  • string
  • list
  • array
  • handle:用於 I/O channel、socket, thread 等,此節暫不說明。

String 字串資料型態
字串是 TCL 語言最基本的資料型態,常見的字串處理指令有:string、append、format、scan 以及 binary。例如使用string 指令來計算指定字串的長度:
% set name “Brent Welch”
% string length $name
=>11

其中 string 指令的第一個參數代表對字串的操作方式。可以試著傳遞一個錯誤的參數給 string 指令,透過這個方式查看string指令有哪些可用的方法:

% string rick
bad option "rick": must be bytelength, compare, equal, first, index, is, last, length, map, match, range, repeat, replace, tolower, toupper, totitle, trim, trimleft, trimright, wordend, or wordstart

下表總結 string 指令的用法:
      
string bytelength str 傳回字串的位元組數(以UTF-8 encoding計算),傳回值可能會跟計算字元數的string length有所不同
string compare ? -nocase? ?-length len?str1 str2 比較兩字串的內容,若相同傳回『0』、其他的傳回『1』。
-nocase:表示不分大小寫
例:
% string compare –nocase Rick rick
=>0
-length:可以指定要比較的字串長度
例:
string compare –length 3 rick ricp
=>0
string equal ?-nocase? str1 str2 比較str1和str2的內容,若相同傳回『1』、否則傳回『0』
string first subString string
 startIndex
傳回 string 中相符子字串第一個出現的位置,若無則傳回『-1』。startIndex 代表從字串指定位置開始搜尋子字串。
例:
% string first rick ilovericktoo
=>5 
string index string charIndex 傳回index位置所在的字元,index的計算從『0』開始,使用『end』代表最後一個字元或使用『end-N』代表相對於最後一個字元的位置。
例:
% string index rick 2
=>c
% string index rick end
=>k
% string index rick end-2
=>i
string last string 傳回 string 中最後一個與子字串相符的位置,若無則傳回『-1』。startIndex 代表從字串指定位置開始搜尋子字串。
例:
% string frist rick rickANDrick
=>0
% string last rick rickANDrick
=>7
string length string 傳回字串的字元數
string map ?-nocase? charMap string 依據 charMap 中的字串對應表,更換字串中的內容。
例:
% string map {i c} rick
=>rcck
% string map {ri ja} rick
=>jack
% string map {r j i a} rick
=>jack
string match ?-nocase? pattern str 將字串拿來與pattern比對,若是相同則傳回『1』,若不同則傳回『0』。字串比對的方式為 glob style pattern match:
?:表示一個任意字元
*:表示隨意數量的任意字元。
[ ]:表示中括號中集合的一個任意字元,[abc]定義符合 abc 任意一個字元,[a-z] 定義符合所有小寫英文字母任意一個字元。
\?:表示一個問號字元。

例:
% string match tcl* tcltk
=>1
% string match tcl* rick
=>0
『?』表示一個任意字元。
例:
% string match ric? rick
=>1
% string match ri?? rick
=>1
% string match ric? rickpeng
=>0
『[ ]』表示在中括號集合中任意一個字元。
例:
% string match {[a-z]} rick
=>0
% string match {[a-z][a-z][a-z][a-z]} rick
=>1
string range str i j 取出str字串中從i到j位置的字元。可用 end 字元代表最後一個字元。
例:
% set x [string range iloverick 2 5]
=>over
string repeat str count 將str字串重覆印出count所表示的次數。
例:
% string repeat rick 3
=>rickrickrick
string replace str first last ?newstr? 以空字串或 newstr取代指定範圍的字串。First與last分別為所要取代的字串其頭、尾位置,newstr則為所欲代換成的字串,newstr 預設為空字串代表要截斷指定範圍的字串。
例:
% string replace iloverick 1 4 hate
ihaterick
string tolower string ?first? ?last? 將所定義的字串範圍中之字元轉換為小寫字元。
例:
% string tolower ILOVERICK 1 4
=>IloveRICK
string totitle string ?first? ?last? 將所定義的字串範圍中第一個字母轉換成大寫。
例:
% string totitle iloverick 1 4
=>iLoverick
string toupper string ?first? ?last? 將所定義的字串範圍中之字元轉換為大寫字元。
例:
% string toupper iloverick 1 4
iLOVErick
string trim string ?chars? 去除指定字串頭、尾中包含在chars中定義的任何字元。chars系統預設為空白字元。
例:
% string trim ililoverick rick
lilove
% string trim kkkloverickkkkk rick
=>love
string trimleft string ?chars? 去除在string左側包含在 chars中定義的任何字元,chars系統預設為空白字元。
例:
% string trimleft iloverick i
=>loverick
% string trimleft kkkloverickkkk lk
=>overickkkk
string trimright string ?chars? 去除在string右側包含在 chars中定義的任何字元,chars 系統預設為空白字元。
例:
% string trimright iloverick rick
=>ilove
string wordend str index 傳回指定 str 字串中指定位置上的單字的下一個位置。
例:
% string wordend {I love rick} 5
=>6
string wordstart str index 傳回str字串中指定位置上單字的起始位置。
例:
% string wordstart iloverick 9
=>0

string 指令注意事項

  • 字串的比較儘量使用 string compare 或 string equal,例如:

if {[string compare $s1 $s2] == 0} {
    # s1 and s2 are equal
}

if {[string equal $s1 $s2]} {
    # s1 and s2 are equal
}

避免使用 == 來比較字串,雖然下列程式碼中,比較的兩個字串有不一樣的內容,仍會輸出 ack 的訊息,這是因為 TCL 會試圖將字串轉換為數字再進行比較,於是16進位的0xa會等於十進位的10,這可不是我們所期望的結果,因此建議儘量使用 string compare 或 string equal 來取代這種寫法:

if { “0xa” == “10”} { puts “ack” }

append 指令

append 指令用來將新的項目附加到指定變數的內容後。
例:
% set foo rick
rick
% append foo i love you
rickiloveyou

format 指令

與C語言中的printf十分相似的指令,format指令依據指定的格式將字串格式化。

    format spec value1 value2 …

其中spec的參數包含了文字或是任何字元。而一般定義的關鍵字包含了六大部份:
  • 位置指示(position specifier)
  • 旗標(flags)
  • 欄寬(field width)
  • 精確度(precision)
  • 字元長度(word length)
  • 轉換字元(conversion character)

由於在格式定義的時候常會有空白夾雜其中,切記要使用雙引號或大括號將定義內容群組(Grouping)起來。

d 帶正負號整數(Signed integer)
u 無正負號整數(Unsigned interger)
i 帶正負號整數。表示成為hex(0x)或octal(0)
o 無正負號的八進位數值。(Unsigned octal)
x or X 無正負號的十六進位數值。(Unsigned hexadecimal),x表示為輸出小寫的結果。
c 把數字對映成為ASCII 字元
s 字串
f 浮點數,格式為a.b
e or E 浮點數,格式為科學符號,a.bE+-c
g or G 浮點數,格式為%f或%e,依實際長度取短的表示

位置指示的表示方法為i$,意思是直接取得第i個參數的值。參數的計數是從1開始。底下的範例透過位置指示直接取用 format 第二個參數的內容:

% format {%2$s} one two three
=>two

因為在 TCL 中,$ 符號有特殊意義,上列指令以大括號抑制了格式化字串中 $ 符號的變數置換作用,所以 i$ 的功能得以正常演出。但如果格式化字串是以雙引號來做群組 (grouping),我們必須利用反斜線仰制 $ 符號的變數置換:

% format “%2\$s” one two there
=>two


格式化旗標
- 靠左對齊
+ 顯示數值的正、負符號
space
0 以0 補滿
# 遇到octal將字首填入『0』,遇到Hex時字首填入『0x』

scan 指令

與C語言中的scanf十分相似的指令,scan指令依據指定的格式化條件剖析字串並將結果放入變數中。

    scan string format ?varName? ?varName?  …

例:
% scan “a 123 Rick” char num str
    % puts “char = $char num = $num str = $str”
    => char = 97 num = 123 str =rick

scan 指令與 format 指令非常相像,%c 會將某個字元的 ASCII 數值讀入變數中。


Binary 指令
binary format template value ?value ….?
    Binary scan value template variable ?variable …?
這邊我們看看幾個 binary 指令常用的用法,至於詳細 template 的設定方法請參閱 manual page。

使用 c 為模板(template),將數值 97 格式化為 ASCII字元:
% binary format c 97
=>a


使用 c 為模板(template),將字元 6 的 ASCII 值讀進 var1 變數中:
% set input 6
% binary scan $input c var1
% set var1
=> 54

一次scan 多個字元碼到 list 中:
% binary scan abcde “c3” list
    % set list
    => 97 98 99
    % lindex list 1    ;# 取出 list 中第 1 個元素
    => 98

換個方式,scan 到多個個別變數中:
% binary scan abcde “ccc” x y z
    % puts “x = $x y = $y z = $z”
    % x = 97 y = 98 z = 99

List 串列資料型態

TCL 的 list 指令有 list, lindex, llength, lrange, lappend, linsert, lreplace,
lsearch, lset, lsort, concat, join, and split 等,提供您將資料放入 list、取出 list 中的元素、計算 list 的元素數量、取代 list 中的指定元素等等功能。通常我們會搭配 foreach 的使用,將 list 中的元素一一取出做運算。

然而,在表現比較複雜的資料結構時,list 並不是最好的選擇方案,在這些場合裏使用 TCL 的 array 型態會比 list 更加適合。

list arg1 arg2….. 將 list 指令的參數將建構成一個 list。
例:
% list rick test
=>rick test
用 set 指令建構一個list:
% set mylist {rick test}
=>rick test
lindex list i 傳回在list之中第i個項目的內容。可以指定多個 index,以取出巢狀 list 中的項目。
例:
% set x {r i c k}
=>r i c k
% lindex $x 2
=>c
llength list 傳回在list之中的項目數。
例:
% set x {r i c k}
=>r i c k
% llength $x
=>4
lrange list i j 傳回list之中從i至j的項目。
例:
% set list {t p t s 1}
=>t p t s 1
% set y [lrange $list 1 2]
=>p t
lappend listVar arg arg….. 附加項目到listVar之後
例:
% set list rick
=>rick
% lappend list iloveyou
=>rick iloveyou
Linsert list index arg arg.. 將資料插入在第i個項目之前。
例:
% set list {i love rick}
=>i love rick
% linsert $list 1 really
=>i really love rick
lreplace list i j arg arg… 將 list 中從i 到 j 個的項目取代為指定的參數。lreplace 傳回取代後的新 list。
例:
% set list {i love rick}
=>i love rick
% set list2 [lreplace $list 1 1 hate]
=>i hate rick
lsearch ?options? list pattern 傳回與 pattern 比對相符合的第一個元素位置,傳回 -1 如果沒有相符的元素。預設的比對方式為 Glob。
例:
% set list {i love rick}
=>i love rick
% lsearch $list love
=>1
lset listVar index
?index…? value
使用 value 設定 list 第i 個元素的值。
例:
% set list {i love rick}
=>i love rick
% lset list 1 hate
=>i hate rick
lsort ?switches? list 根據switch 所指定的選項為 list 排序。可用的選項有-ascii, -dictionary, -integer, -real, -increasing, -decreasing, -index ix, -unique, -command command.。
例:
% set list [list maa cxlin ognoc]
% lsort $list
=>cxlin maa ognoc
concat list list … 將多個 list 串成一個。
例:
% set list1 [list 1 maa]
% set list2 [list 2 cxlin]
% set list3 [list 3 ognoc]
% set listAll [concat list1 list2 list3]
% llength $listAll
=>6
join list ?joinString? 將list中的項目合併成一個字串。可以指定 joinString 作為每個項目間的分隔,預設的分隔字元為空白。
例:
% set list [list i love rick]
% join $list
=>i love rick
% join $list ###
=>i###love###rick
split string
?splitchars?
將字串分割成一個list。可以指定 splitChars 作為字串中每個項目的分隔識別符號。
例:
% set list [split ”a b c”]
% length $list
=>3
% set list [split “a#b#c” “#”]
=>a b c
% lrange $list 0 end
=>a b c

通常我們會把 list 與 foreach 結合運用。例如底下的 foreach迴圈列出環境變數的名稱及內容(array names 可取出指定陣列的所有 index):
% foreach index [array names env] { 
    %     puts “$index = $env(index)”
    % }
    =>OS = Windows NT
    =>windir = C:\Windows
    =>ComSpec = C:\WINDOWS\system32\cmd.exe
    =>(略)

利用 foreach 與 list 寫個 join 指令:
proc join {list sep{} } {
   set s {}  ;# s is the current separator
   set result {}
   foreach x $list {
      append result $s $x
      set s $sep
   }
   return $result
}


Array 陣列資料型態

TCL 的array 和 Perl 的 associative array 類似,陣列是以字串作為索引。陣列在 TCL 語言中舉足輕重,許多資料結構都是以陣列為基礎設計而成。陣列元素內容的設定一樣是使用 set 指令:
% set price(apple) 10
% set price(orange) 12
% set quantity(apple) 5
% set discount(apple) 0.8
puts $price(apple)
=>10

array exists arr 判斷 arr 是否為一個陣列。傳回 1 代表是陣列。
例:
% set price(apple) 10
% array exists price
=>1
array get arr
?pattern?
將 arr 的索引及元素值交錯建立成一個 list 後傳回。可以使用樣式(Pattern)來做比對。
例:
% set price(apple) 10
% set price(orange) 12
% array get price
=>orange 12 apple 10
array names arr
?mode? ?pattern?
傳回 arr 的索引list。可以使用 mode 指定樣式的比對方式 (可為 –exact、-glob(default) 或 –regexp)。
例:
% array names price
=>orange apple
array set arr list 根據list的內容建構一個陣列。
例:
% array set price [list apple 10 orange 12]
% array get price
=>orange 12 apple 10
array size arr 傳回 arr 的大小。
例:
% array size price
=>2
array unset arr
?pattern?
釋放符合樣式的陣列元素。如果沒有指定樣式,則會釋放整個陣列。
例:
% array set price [list apple 10 orange 12]
% array get price
=>orange 12 apple 10
% array unset price app*
% array get price
=>orange 12
% set price(apple) 10
% array get price
=>orange 12 apple 10
% array unset price
% set price
=>can't read "price": no such variable
array startsearch arr 起始陣列的搜尋功能,並傳回一個標記(token id)。
array nextelement arr id 在陣列token-id所標記的搜尋中,將下一個元素的索引傳回。沒有元素時傳回空字串。
array anymore arr id 檢查陣列token-id所標記的搜尋中,是否還有元素可取用,還有元素傳回1否則傳回0。
array donesearch arr id 結束 id 所識別的搜尋。
array statistics arr 傳回arr陣列雜湊表(hash table)的統計表。

陣列也常拿來與 foreach 迴圈搭配使用,例如:
% set price(apple) 10
% set price(orange) 12
% array get price
=>orange 12 apple 10
% foreach {key value} [array get price] {
     “price($key) = $value”
}
=>price(orange) = 12
=>price(apple) = 10

或者這樣寫,程式的結果也會一樣:
% foreach key [array names price] {
    “price($key) = $price($key)”
}

因為使用 array get 或foreach 時,TCL 的作法是先產生一個暫時性的list,這樣會浪費一些記憶體空間與執行速度。改用 array 的搜尋功可以加快陣列元素巡訪(iterate)的速度:
% set searchToken [array startsearch price]
% while {[array anymore price $searchToken]} {
    set key [array nextelement price $searchToken]
    set value $price($key)
    puts “$key = $value”
 }
 array donesearch price $searchToken
=>orange = 12
=>apple = 10

------------------------------------
相關系列文章:

留言

這個網誌中的熱門文章

[TCL] 基本語法與指令 - 2. TCL 語法

[TCL] 基本語法與指令 - 1. TCL 簡介