tcl語法快速查詢手冊


0、教程

https://www.yiibai.com/tcl/tcl_environment.html
或者
https://noyesno.net/page/tcltk/20090719-87.html
或者
https://www.learnfk.com/article-tcl-tk/tk_images
或者
https://www.w3cschool.cn/doc_tcl_tk/tcl_tk-itclcmd-body-htm.html?lang=en
或者
https://blog.csdn.net/xmlbm/article/details/100038912
或者
https://blog.csdn.net/artechtor/article/details/1665930


1、安裝


1)在https://www.activestate.com/products/tcl/downloads 網站下載tcl8.6;
2)雙擊ActiveTcl-8.6.9.8609.2-MSWin32-x64-5ccbd9ac8.exe安裝包;
3)安裝時提示安裝microsoft visual C++ 2015 visual studio update3,下載安裝這個文件;
4)安裝ActiveTcl-8.6.9.8609.2-MSWin32-x64-5ccbd9ac8.exe安裝包;


2、基本語法


1)語句分隔
;或者newline
如果每個命令都使用換行符,則分號不是必須的;
2)注釋
內聯注釋用;#
puts "Hello World!" ;# my first print in Tcl program
使用#符號注釋單行
# my first program in Tcl

使用條件為0的if寫入多行或塊注釋,;
if 0 {
my first program in Tcl program
Its very simple
}
3)行繼續
\在行尾
% set a [list aa \
bb \
cc ]
aa bb cc
% set a
aa bb cc
4)變量
使用字母、數字、下划線和$符號組成變量、函數名稱;
大小寫敏感;

5)保留字,不能被用作常量或變量。常見保留字有after、continue、else、catch、file等;
保留字列表:https://www.yiibai.com/tcl/tcl_basic_syntax.html
6)特殊變量,系統內置的變量。常見的有argc、argv、env、tcl_version等;
特殊變量列表:https://www.yiibai.com/tcl/tcl_special_variables.html#article-start
特殊變量 描述
argc 指命令行參數的個數。
argv 指包含命令行參數的列表。
argv0 是指被解釋的文件或由調用腳本的名稱的文件名。
env 用於表示是環境變量數組元素。
errorCode 為最后的tcl錯誤的錯誤代碼
errorInfo 為最后Tcl錯誤的堆棧跟蹤錯誤信息
tcl_interactive 分別將其設置為1和0交互和非交互模式之間切換
tcl_library 用於設置標准Tcl庫的位置
tcl_pkgPath 提供一般都安裝包的目錄列表
tcl_patchLevel zhideshiTcl解釋目前的補丁級別
tcl_platform 用於表示使用對象,包括byteOrder,machine,osVersion平台和操作系統數組元素
tcl_precision 指的是精度,即位數轉換為浮點數時,字符串保留。默認值是12.
tcl_prompt1 指的是主提示符
tcl_prompt2 指無效的命令二次提示。
tcl_rcFileName 為用戶提供了具體的啟動文件。
tcl_traceCompile 用於控制字節碼編譯的跟蹤。用0表示無輸出,1為概要和2為詳細。
tcl_traceExec 用於控制執行的字節碼的跟蹤。用0表示無輸出,1為概要和2為詳細。
tcl_version 返回Tcl解釋器的最新版本。
7)替換與不替換
變量替換,用來返回變量的值,例如set a 3;puts $a
命令替換,用來返回命令的值,例如puts [expr 1 + 6 + 9]
" string " 帶替換的引用
{ string } 不帶替換的引用
[ string ] 命令替換
8)tcl命令的語法:commandName argument1 argument2 ... argumentN

 

3、數據類型


1)整數類型


set myVariable 18
puts $myVariable # 此時myVariable自動轉為字符串類型,再輸出
puts [expr $myVariable + 6 + 8] # 此時myVariable轉換為整形,進行數學計算


2)字符串類型


set myVariable hello # 單個單詞不需要使用雙引號
set myVariable "hello world" # 多個單詞需要使用雙引號
set s1 "Hello World"
set s2 "o"
讀取某個索引的字符
string index "Hello! " 0;#輸出H
string index "Hello! " end;#輸出!
string index "Hello! " end-4;#輸出e
截取字符串
string range "Sample string" 3 7;#輸出ple s
string range "Sample string" 3 end;#輸出ple string
字符串長度
puts [string length $s1]
大小寫轉換
puts [string toupper $s1]
puts [string tolower $s1]
去掉字符串兩邊空格
set s1 " hello world "
set s2 " "
puts [string trimright $s1 $s2];#去掉右空格
puts [string trimleft $s1 $s2];#去掉左空格
puts [string trim $s1 $s2];#去掉兩邊空格
合並字符串
set s1 "Hello"
append s1 " World"
puts $s1;#輸出“Hello World”
【注意:append命令后的變量s1直接使用,不加$符號,這個命令直接修改s1的值】
拆分字符串
puts [split "red_green_blue" _];#輸出red green blue
字符串比較
string compare "This is a tcltk example" "This is a TCLTK example";#返回1,因為ASCII碼T在t之后,所以從順序上來說前面的字符串顯然在前
string compare "This is a tcltk example" "This is a tcltk example";#返回0,兩個字符串相等
string compare "This is a TCLTK example" "This is a tcltk example";#返回-1

string equal "This is a TCLTK example" "This is a tcltk example";#不同返回0
string equal "This is a tcltk example" "This is a tcltk example";#相同返回1
搜索字符串
string first "tcltk" "This is a tcltk example";#返回10,返回第一次匹配的索引,如果找不到則返回-1
string last "is" "This is a tcltk example";#返回5,返回最后一次匹配的索引


替換字符串
string map {key1 value1 key2 value3 ...} 1abcaababcabababc;#將1abcaababcabababc中的key1替換為value1,key2替換為value2

 

3)列表類型


列表是一組元素,使用雙引號或者大括號表示簡單的列表。
set myVariable {red green blue}
set myVariable "red green blue"

|命令 |說明
| |
|list arg1 arg2 ... |創建一個列表
| |
|lindex list index |返回列表 list 中的第 index 個元素(element)值
| |
|llength list |計算列表 list 元素個數
| |
|lrange list index1 index2 |返回指定范圍內(從 index1 到 index2)的元素
| |
|lappend list arg1 arg2 ... |將新元素追加到原來列表 list 后組成新的列表
| |
|linsert list index arg1 arg2 ... |將新元素插入到 list 中位於 index 元素之前的位置上
| |
|lreplace list index1 index2 arg1 arg2 ... |替換指定范圍的元素
| |
|lsearch ?mode? list value |根據匹配模式 mode,查找 list 中與 value 匹配的元素位置索引。mode 一般為-exact、-glob 和regexp.默認為-glob。找不到返回-1。
| |
|lsort ?switches? list |根據開關選項對列表進行排序
| |
|concat list1 list2 ... |連接多個列表內容成一個列表
| |
|join list joinChars |以 joinChars 為分隔符將列表中的元素合並在一起
| |
|split string splitChars |以 splitChars 中的字符作為分隔符將字符串分解為列表元素。
| |
|foreach var list {proc body} |遍歷列表各項,逐次將各元素值存入 var 中並執行 proc body。相當於一個循環控制語句。

list命令:用來創建列表。一個列表可以包含子列表,即列表可以嵌套。
set l1 [list Sun Mon Tues]
結果=>Sun Mon Tues

set l2 [list $l1 Wed] ;#列表 l1 含有三個元素
結果=> {Sun Mon Tues} Wed

set str1 "Sun Mon Tues"
結果=>Sun Mon Tues

set l2 [list $str1 Wed] ;#列表 l2 中含有兩個元素。第一個元素用花括號括起來。
結果=>{Sun Mon Tues} Wed ;#和上面的命令結果相同:“列表是特殊的字符串”。

concat命令:以空格為分隔符將多個列表拼裝在一起形成新的列表。它和雙引號的作用比較相似。
set x {1 2}
結果=> 1 2

set y "$x 3 " ; #$x 被替換后,作為列表結構的花括號被去掉,
結果=> 1 2 3 ; #元素被提出來和 3 一起作為新列表的元素

set y "$x {3}"
結果=> 1 2 {3}

set y [concat $x 3] ;#結果同上面的雙引號
結果=> 1 2 3

set y [concat $x {3}]
結果=> 1 2 3

lindex命令:返回列表中指定位置的特定元素。列表索引從 0 開始記數!
set x { 1 2 3 }
結果=> 1 2 3

puts [lindex $x 1]
=>2

llength命令:可以獲得一個列表內元素的個數。
set length "1 2 3 4 5"
結果=>1 2 3 4 5 ;#定義了一個字符串
set num [llength $l1] ;#這里 l1 被看作列表了
=>5

lrange命令:返回一個指定區段的列表元素,可以以 end 或者 end-n 作為索引(n 為正整數)。
lrange {1 2 3 {4 5} 6} 2 end
結果=> 3 {4 5} 6

lmap命令:在最簡單的情況下,只有一個循環變量varname,和一個列表list,list中的值會依次分配給變量varname,body是一個Tcl腳本。
lmap varname list body
body所代表的腳本會使用varname進行處理。
如果主體的執行正常完成,則body的結果追加到累加器列表中,lmap最后返回累加器列表。
如果body中最后一個命令返回值為空,則lmap的返回結果為所有值均為空值的列表。

linsert命令:用來將元素插入到一個列表的由索引指定的位置。如果索引為 0 或者更小,則元素就會被添加到最前面。
如果索引值大於或者等於列表長度,則元素被追加到列表尾部。其他情況元素被添加到指定位置之前。
注意:這個操作不會改變原來列表的內容,而是返回一個新列表。
set x {1 2}
結果=>1 2

set new [linsert $x 0 he she]
結果=>he she 1 2

set new [linsert $x end he she]
結果=>1 2 he she

set new [linsert $x 1 he she]
結果=>1 he she 2

puts $x
結果=>1 2 ;# x 的值沒有改變

puts $new
結果=>1 he she 2

lreplace命令:將一個指定區段的列表元素替換為新元素,
如果沒有指定新元素,則這個區域的元素就會被從列表中刪除。
注意:這個操作不會改變原來列表的內容,而是返回一個新列表。
set new {1 he she 2}
結果=>1 he she 2

set y [lreplace $new 1 2 B C]
結果=>1 B C 2

set y [lreplace $new 0 0]
結果=>he she 2 ;#刪除列表元素,將第 0 個元素刪除

puts $new
結果=>1 he she 2 ;#new 的內容並沒有改變,這和 string replace 相同。

set y [lreplace $new 1 2]
結果=>1 2 ;#將第 1、2 個元素刪除

lsort命令:實現對列表的排序。
注意:排序操作不影響原表,而是返回排序之后的新表。
set list "a Z z n100 n200 M p Hl hL m 1 20"
結果=>a Z z n100 n200 M p Hl hL m 1 20

lsort -ascii $list
結果=>1 20 Hl M Z a hL m n100 n200 p z

lsort -dictionary $list
結果=>1 20 a Hl hL M m n100 n200 p Z z

lsearch命令:與in命令類似。在給定列表中搜索與匹配字符串匹配的元素,成功就返回正確的元素索引,否則返回-1。
lsearch 支持通配符格式,但可以使用-exact 選項將其屏蔽而進行精確匹配。
set l1 [list This is one list]
結果=> This is one list

set index [lsearch $l1 l*]
結果=> 3

set index [lsearch -exact $l1 l*]
結果=>-1

set index [lsearch -exact $l1 list]
結果=>3

set stripped [lsearch -inline -all -not -exact $inputList $elemToRemove];#刪除元素的另一種方法是將其過濾掉

-all 返回全部匹配項
-inline 返回匹配項的值,而不是索引

concat命令:將連接多個列表
concat a b {c d e} {f {g h}}
將返回結果“a b c d e f {g h}”)
in命令:判斷列表中是否有某個字符串
if {orange in $var} {
puts "orange is in var"
}

join命令:接收一個列表,並用指定的分隔符將列表元素整合成一個字符串
join {1 {2 3} {4 5 6}} :
結果=> 1:2 3:4 5 6

split命令:作用與 join 的作用相反,它接收一個字符串,並根據給定的字符將其分割轉換成
注意:這個操作不會改變原來字符串的內容,而是返回一個新列表。
set str cm8/auto/tools/aries/ASAM/NE/SNMP/IMPL/ne_create_board.tcl
結果=>cm8/auto/tools/aries/ASAM/NE/SNMP/IMPL/ne_create_board.tcl
set s /
結果=>/

set l1 [split $str $s]
結果=> cm8 auto tools aries ASAM NE SNMP IMPL ne_create_board.tcl

set l2 [split $str "/."] ;#可以指定多個分割符
結果=> cm8 auto tools aries ASAM NE SNMP IMPL ne_create_board tcl

foreach命令:會遍歷整個列表,逐次取出列表的每個元素的值放到指定變量中,使用者可以在跟隨的過程體中添加必要的處理過程。
set lst "1 2 3 4"
結果是=>1 2 3 4

foreach item $lst {
puts $item
}
結果是=>
1
2
3
4

lappend命令:追加項目到列表末尾
set var orange
lappend var "red"
puts $var;#輸出orange red
【注意:lappend命令后的變量s1直接使用,不加$符號,這個命令直接修改s1的值】
lreverse命令:反轉列表元素的順序
lreverse {a a b c}
-->c b a a
lreverse {a b {c d} e f}
-->f e {c d} b a
用列表項給變量賦值
set var {orange blue red green}
lassign $var colour1 colour2
puts $colour1;#輸出orange
puts $colour2;#輸出blue

按照索引刪除列表元素:參考replace命令
按照值刪除列表元素:參考lsearch命令


4)數組(array)


set marks(english) 80
puts $marks(english)
set marks(mathematics) 90
puts $marks(mathematics)
% array get marks math
math 90
數組的大小
puts [array size marks]
【注意:數組size命令后的變量名不加$符號】
數組的索引
puts [array names marks]
【注意:數組names命令后的變量名不加$符號】
使用foreach語句遍歷數組
foreach index [array names marks] {
puts "marks($index): $marks($index)"
}
# 上面的index是臨時定義的變量


5)布爾類型


1,yes 或 true 為真值
0,no 或 false 為假值


6)字典


https://webcache.googleusercontent.com/search?q=cache:ZGvZvFZG-FkJ:https://blog.csdn.net/asty9000/article/details/90217533+&cd=1&hl=zh-CN&ct=clnk
創建字典
set colours [dict create colour3 "black" colour4 "white"]
puts $colours;#輸出colour3 black colour4 white
修改或者新增鍵值對
dict set colours colour1 red
dict set colours colour3 green
puts $colours;#輸出colour3 green colour4 white colour1 red
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict set dic f g h 8
a 1 b 2 c 3 f {g {h 8}}
【注:set命令只能每次修改一個鍵值對】
unset
dict unset dictName key ?key ...?
刪除字典dictName中key對應的值。
如果dictName中不存在key,則不進行任何操作。
當存在多個鍵時表示刪除嵌套字典中的元素,此時的鍵路徑上除了最后一個key可以不存在外,
前面的key必須存在,否則會報錯key "xxx" not known in dictionary。
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict unset dic c
a 1 b 2
remove
dict remove dictValue ?key ...?
返回一個新的字典【不改變原來字典】,字典包括鍵為key以外的所有dictValue中的鍵值對。
如果不指定任何鍵則返回的字典與dictValue相同,如果key不在dictValue中,則不進行任何操作。
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict remove $dic a
b 2 c 3
% puts [dict keys $dic]
a b c
merge
dict merge ?dictValue ...?
返回一個新的字典,包含dictValue中的所有元素,多個dictValue具有相同的key的元素的時,
最后一次出現的key對應的值會出現在新的字典中。
% set newDic [dict merge {a 1 b 2} {c 3 d 4 b 5}]
a 1 b 5 c 3 d 4
字典大小
set colours [dict create colour3 black colour4 white]
puts [dict size $colours];#輸出2
字典某個鍵的值
set colours [dict create colour3 black colour4 white]
puts [dict get $colours colour3];#輸出black
字典中的所有鍵
set colours [dict create colour3 black colour4 white]
set keys [dict keys $colours]
puts $keys;#輸出colour3 colour4
字典中的所有值
set colours [dict create colour3 black colour4 white]
set values [dict values $colours]
puts $values;#輸出black white
字典是否含有某個鍵
set colours [dict create colour3 black colour4 white]
set result [dict exists $colours colour3]
puts $result;#輸出1
字典迭代
set colours [dict create colour3 black colour4 white]
foreach key [dict keys $colours] {
puts [dict get $colours $key]
}
# 分兩行分別輸出black和white
字典遍歷列表
用於遍歷字典dictValue,在執行body中腳本時,字典中每個鍵值對的鍵和值會分別分配給名為指定的keyName和valueName變量。
dict for命令的結果為空字符串。當在body執行過程中遇到break時,會中止循環,結束dict for命令。
當遇到continue命令時,會結束本輪循環。字典按鍵值對的插入順序進行遍歷。
set colours "colour1 red colour2 white colour3 black"
dict for {key value} $colours {
puts "key:$key,value:$value"
}
append
dict append dictName key ?string ...?
將給定的string追加到字典dictName中鍵key的值后面。
如果key不存在,則相當於新增一個key的映射。
如果未指定string,則視為空字符串。
多個string會合並為一個字符串。此命令返回更新后的字典。
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
%
% dict append dic ab 4 5
a 1 b 2 c 3 ab 45
% dict append dic a 6
a 16 b 2 c 3 ab 45
%
% dict append dic ab
a 16 b 2 c 3 ab 45

lappend
dict lappend dictName key ?value ...?
將給定的value追加到dictName中指定key對應的列表中,可以同時追加多個value,
如果key對應的值不是列表,會創建一個列表,並將原有值和追加的值放到列表中。
如果key存在,但沒有指定value,則不做任何操作。
如果dictName中不存在key,則視為空列表,若此時value也未指定則會添加一個鍵為key值為空列表的元素,
若指定了value則會將value作為列表的元素。如果key對應的值不能表示為列表則會報錯。
% dict lappend dic a
a 1 b 2 c 3
% dict lappend dic a 1
a {1 1} b 2 c 3
% dict lappend dic e
a {1 1} b 2 c 3 e {}
% dict lappend dic e 1 2 3 4 5
a {1 1} b 2 c 3 e {1 2 3 4 5}
incr
dict incr dictName key ?increment?
將字典dictName中指定key對應的值增加increment。
如果未指定increment,則默認增加1。
如果不存在指定的key,則視為0。如果key對應的值不是整數則會報錯
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict incr dic a
a 2 b 2 c 3

dict update dictName key varName ?key varName ...? body
使用映射dictName中key對應的值的變量varName執行body中的腳本。
如果dictName沒有指定的key,則其對應的變量varname為未設置。
當body終止時,對varName所做的更改都會被會寫到字典內即使body的結果是一個錯誤或其他異常,
除非字典不可讀所有的更新都被靜默的丟棄。
除非發成異常,否則dict update的結果是body的執行結果。
建議只在本地作用域內使用此命令,因為dict update命令中的變量varName在命令完成后,除非顯示的執行unset,否則仍然存在。
此外,只有當body終止時,才會更改dictName中的內容
replace
dict replace dictValue ?key value ...?
返回一個新的字典,包括傳入的dictValue以及一些額外的或不同的鍵值對。
如果不傳如鍵值對key、value,則返回的新字典與dictValue相同。鍵值對必須是成對出現,否則會報錯。
dictValue中存在的key會用心的value替換,不存在的key會新增到新的字典。
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% set newDic [dict replace $dic a 1 b 3 d 4]
a 1 b 3 c 3 d 4


info
dict info dictValue
返回字典信息,信息的具體格式取決於字典的實現方式。由於字典是散列表實現的,所以放回結果類似於array statistics命令。
with
dict with dictName ?key ...? body
與update類似,使用dictName中key對應的值執行body中的腳本。
如果key為多個表示嵌套字典的鍵路徑,此時會使用鍵路徑對應的字典執行body中的腳本。
字典中key對應的值會映射到與key同名的變量中。
與dict update一樣,如果dictName不可讀或者字典調整導致鍵路徑沒有對應的值,則會靜默丟棄字典的更新。
除非發成異常,否則dict with的結果是body的執行結果。
建議只在本地作用域內使用此命令,因為dict with命令中的變量在命令完成后,除非顯示的執行unset,否則仍然存在。
此外,只有當body終止時,才會更改dictName中的內容。當body終止時如果dictName不是字典則會產生錯誤。
因此當字典中的鍵與字典變量dictName有沖突時不建議使用此命令。
% set info [dict create forenames Joe surname Schmoe street {147 Short Street} city Springfield phone 555-1234]
forenames Joe surname Schmoe street {147 Short Street} city Springfield phone 555-1234
% dict with info {
puts " Name: $forenames $surname"
puts " Address: $street, $city"
puts " Telephone: $phone"
}
Name: Joe Schmoe
Address: 147 Short Street, Springfield
Telephone: 555-1234
filter
dict filter dictValue filterType arg ?arg ...?
返回一個新的與指定過濾類型匹配的鍵值對的字典
有三種過濾方式:按照key過濾,按照value過濾,或者使用腳本過濾
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict filter $dic key a*
a 1
% dict filter $dic value 2*
b 2
% dict filter $dic script {k v} { expr { $k < "e"} }
a 1 b 2 c 3
map
dict map {keyName valueName} dictValue body
Tcl8.6新增的命令。
此命令通過執行body中的命令,對字典dictValue中的鍵值進行轉換,然后返回一個新的包含轉換后的鍵值對的字典。
名為keyName、valueName對應於dictValue中鍵值對的鍵與值。
每次body執行完后,變量keyName、valueName的值即為新字典的鍵值。
當在body執行過程中遇到break時,會中止循環,結束dict map命令。
當遇到continue命令時,會結束本輪循環。
另外要注意的是,如果不想對值做改變,需要對其進行顯示的set操作,否則值會被設置為body中最后一個命令的返回值。
% set dic [dict create a 1 b 2 c 3]
a 1 b 2 c 3
% dict map { k v } $dic { set k $k$k; set v $v;}
aa 1 bb 2 cc 3

5)句柄
句柄通常用於文件、網絡請求的處理
set myfile [open "filename" r]


4、數學計算


1)expr用於表示數學表達式,默認精度為12位數;
2)使用tcl_precision特殊變量來更改精度;


5、if語句


if { $a == 10 } {
puts "Value of a is 10"
} elseif { $a == 20 } {
puts "Value of a is 20"
} else {
puts "Exact value of a is: $a"
}
# 注意:else和兩邊的花括號間有空格。

TCL中有4個用於表達式的字符串操作符:eq、ne、in、ni.
1)eq和ne用來檢查字符串是否相等,如果相等eq返回1,ne返回0,如果不等ne返回1,eq返回0.
2)除了檢查字符串相等還是不相等的eq和ne操作符,<、>、<=、>=、==、!=操作符都可以進行字符串比較
3)如果指定字符串是列表的元素,in操作符返回1,否則返回0.如果字符串不是列表的元素,in操作符返回1,否則返回0


6、switch語句


set grade B;

switch $grade {
A {
puts "Well done!"
}
B {
puts "Excellent!"
}
default {
puts "Invalid grade"
}
}
puts "Your grade is $grade"


7、while循環


set a 10
while { $a < 20} {
puts "value of a is $a"
incr a;#自增1。也可以指定步長,例如-1,此時每次自減1。
}


8、for循環


for { set a 10} { $a < 20 } { incr a } {
puts "value of a is $a"
}


9、過程(也就是函數)


1)無參數過程
proc hello {} {
puts "hello world!"
}
hello;#輸出hello world!
2)多個參數過程
proc add { a b } {
puts [ expr $a + $b ]
}
puts [add 4 5];#輸出9
3)可變參數過程
proc sum { numbers } {
set result 0
foreach number $numbers {
set result [ expr $result + $number ]
}
return $result
【注意:這里要返回值,不是返回變量】
}
puts [ sum {1 2 3 4 5} ]
4)默認參數
proc add { a {b 100}} {
puts [expr $a + $b ]
}
puts [ add 15 ]
5)遞歸
proc factorial { a } {
if { $a == 1} {
return 1
}
return [expr $a*[factorial [ expr $a - 1 ]]]
}
puts [factorial 3]


10、命名空間


10.1、創建命名空間


namespace eval MyMath {
variable myResult
}
proc ::MyMath::add { a b } {
set ::MyMath::myResult [ expr $a + $b ]
}
::MyMath::add 5 6
puts $::MyMath::myResult


10.2、命名空間嵌套


namespace eval MyMath {
variable myResult
}
namespace eval extendedMath {
namespace eval MyMath {
variable myResult
}
}
set ::MyMath::myResult "test1"
puts $::MyMath::myResult
set ::extendedMath::MyMath::myResult "test2"
puts $::extendedMath::MyMath::myResult


10.3、導入導出命名空間(不必借助空間名字,直接引用空間內的變量和過程)


namespace eval MyMath {
variable myResult
namespace export Add
}
proc ::MyMath::Add {a b } {
return [expr $a + $b ]
}
namespace import ::MyMath::*
puts [Add 3 4 ]


10.4、忘記命名空間


namespace eval MyMath {
variable myResult
namespace export Add
}
proc ::MyMath::Add {a b } {
return [expr $a + $b ]
}
namespace import ::MyMath::*
puts [Add 3 4 ]
namespace forget ::MyMath::*
puts [Add 4 5 ];#因為忘記了命名空間,所以這句會報錯


10.5、uplevel與upvar


10.5.1、命名空間層級
proc foobar {} { puts "foobar:\t[info level]"; foo; };
proc foo {} { puts "foo:\t[info level]"; bar; };
proc bar {} { puts "bar:\t[info level]" };
puts "global:\t[info level]"
foobar
上面腳本輸出如下
global: 0
foobar: 1
foo: 2
bar: 3
由此,可以看出最外層空間編號是0,多層函數調用時,空間編號依次增長。
10.5.2、uplevel:提升proc層級,去變量所在空間層級,使用外層空間變量
namespace eval foo {
namespace export bar
proc bar {} {
uplevel 1 { set name "Kid"; };#數字1表示相對層級,數字加上井號表示絕對層級
set name "blah...";
}
}
set name "Rex Tsai"
puts $name;輸出Rex Tsai
foo::bar
puts $name;輸出Kid
10.5.3、upvar
upvar - 創建鏈接到外層空間的變量
用法:upvar ?level? otherVar myVar ?otherVar myVar ...?
level,可選,指空間層級,默認為1,表示鏈接到外1層空間的變量
otherVar,外層空間的變量名
myVar,鏈接的名字
令myVar和外層空間的otherVar指向同一個內存地址,myVar與otherVar變量值相同,修改myVar等同於修改otherVar
如果外層空間中otherVar不存在,則不會創建myVar.

例一:
upvar:提升proc內的var,鏈接到外層空間變量,使用外層空間變量
namespace eval foo {
namespace export bar
proc bar {} {
upvar 1 testname j # 或是 upvar #0 testname j
set j "Kid"
}
}
set testname "Rex Tsai"
puts $testname;輸出Rex Tsai
foo::bar
puts $testname;輸出Kid

例二:
set a 1 ;#定義變量a, 並且值設為1

proc test {b} {
upvar $b mya

puts $b

puts $mya

}

test a ;#調用函數 test

a ;#參數b的值為a(變量名)

1 ;#由於upvar 使mya(變量名)指向a(變量名)指向的同一個變量,mya的為a的值

upvar使的在函數內部可以更改函數外部的變量的值
10.5.4、uplevel與upvar是通過不同方式使用外層空間變量
upvar 與 uplevel 都必須指定你想使用的命名空間層級, 如果不指定, 他會使用上一層, 默認值是 1.
另外, 也可以使用抽象的層級號碼, 他的方式是以 #開頭, 后面接一個層級數字
10.5.5、namespace upvar:通過一個變量鏈接,使用其它空間的變量
namespace upvar ::__script load script_load
load是::__script空間內的變量
可以通過script_load引用load


10.6、刪除命名空間


namespace delete MyMath


10.7、取得限定名稱的命名空間部分或者尾部命名空間


10.7.1、取得頭部的命名空間部分
% namespace qualifiers China::SiChuan
China
% namespace qualifiers China::SiChuan::Chengdu
China::SiChuan
10.7.2、取得尾部的命名空間
% namespace tail China::SiChuan
SiChuan
% namespace tail China::SiChuan::Chengdu
Chengdu
10.8、獲取空間名稱
namespace current命令: --> 獲取當前命名空間的名稱
namespace parent命令: --> 獲取當前命名空間的父命名空間
namespace childred命令: --> 獲取當前命名空間的子命名空間

11、tcl包、庫、引用文件


1)在文件new.tcl中使用source test.tcl命令后,可以在new.tcl中直接使用定義在test.tcl中的過程。

 

12、文件IO


tcl內置的處理文件的命令:open打開,read讀全部,puts寫,gets讀一行,close關閉文件。
read例子
set fp [open "input.txt" w]
puts $fp "test"
close $fp
set fp [open "input.txt" r]
set data [read $fp]
puts $data
close $fp
gets例子
set fp [open "input.txt" w]
puts $fp "test1\ntest2"
close $fp
set fp [open "input.txt" r]
while { [gets $fp data] >= 0 } {
puts $data
}
close $fp


13、錯誤處理


catch script ?resultVarName? ?optionsVarName?
如果腳本產生一個錯誤,catch將返回一個非0的整數,
如果給出了resultVarName變量,當返回1時,存儲在resultVarName中的為錯誤信息,如果返回0,存儲在resultVarName中的為腳本運行結果。
1)用catch捕獲自定義error
proc Div { a b } {
if { $b == 0 } {
error "this is error message" "this is error info" 401
} else {
return [expr $a / $b]
}
}
# 注意:被catch的腳本應該用{}包起來。
if { [catch {Div 10 0} errorMsg]} {
puts "Error Message: $errorMsg"
puts "Error Info: $errorInfo"
puts "Error Code: $errorCode"
}

2)用catch捕獲error


14、tcl內置函數


內置函數列表:https://www.yiibai.com/tcl/tcl_builtin_functions.html
1)數學函數
namespace import ::tcl::mathfunc::*
puts [abs -5];#絕對值
puts [ceil 5.2];#向上取整
puts [floor 5.4];#向下取整
puts [sin [asin 1]];#計算反正弦和正弦值
puts [double 12];#轉為浮點數
puts [fmod 5 2];#5/2的余數
puts [int 4.5];#取整
puts [pow 4 2];#乘方
puts [rand];#0~1間的隨機數
puts [round 4.6];#四舍五入取整
puts [sqrt 16];#開方
2)時間日期
# get seconds
set currentTime [clock seconds]
puts $currentTime
# get format
puts "the time is : [clock format $currentTime -format %H%M%S]"
puts "the date is : [clock format $currentTime -format %D]"
# 字符串轉為秒數
set date "Jun 15, 2014"
puts [clock scan $date -format {%b %d, %Y}]

set date "2020-12-28 18:43:42"
set date [clock scan $date -format {%Y-%m-%d %H:%M:%S}]
puts $date
3)執行系統命令
puts [exec ping 127.0.0.1]
puts [exec ipconfig]


15、正則表達式


1、基本格式
關鍵字 表達式 字符串 匹配值 分組1 分組2 ...
regexp {([A-Z,a-z]*).([A-Z,a-z]*)} "Tcl Tutorial" a b c
puts "Full Match: $a";#輸出"Tcl Tutorial"
puts "Sub Match1: $b";#輸出"Tcl"
puts "Sub Match2: $c";#輸出"Tutorial"
2、常用參數
-nocase:忽略大小寫
-line:新行敏感,換行后忽略字符。
-start index:搜獲模式開始設置偏移。
3、常見用法
通常,我們並不關心匹配值,只關心分組,使用->存儲匹配值,格式如下
regexp $exp $string -> sub1 sub2


16、文件操作


文件測試
file exists $name ; # 判斷文件是否**存在**
file isdirectory $name ; # 判斷文件是否是**目錄**
file isfile $name ; # 判斷文件是否是**文件**

file readable $name ; # 判斷文件是否**可讀**
file writable $name ; # 判斷文件是否**可寫**
file executable $name ; # 判斷文件是否**可執行**
file owned $name

文件路徑、文件名
set name /path/a/b/c.txt

file extension $name ; # .txt
file dirname $name ; # /path/a/b
file rootname $name ; # /path/a/b/c
file tail $name ; # c.txt

file nativename $name ; # 系統默認格式的文件名

文件信息
set name /path/a/b/c.txt

file size $name ; # 單位 字節(bytes)
file stat $name finfo
array get finfo * ; # {atime, ctime, dev, gid, ino, mode, mtime, nlink, size, type, uid}

file separator ; # 系統支持的文件路徑分隔符

文件操作
file mkdir $name ; # mkdir -p $name 創建目錄,自動創建父目錄

file copy $source $target ; # copy 文件: cp $source $target

file delete $path ; # 刪除文件 rm $path
file delete --force $path ; # 強制刪除非空目錄 rm -rf $path

file rename $source $target ; # mv $source $target

獲取鏈接(link)的最終目標文件
在copy操作中,如果遇到 link,而link的目標文件又是相對路徑的時候,就需要找到link所最終指向的文件。

proc file_read_link {file} {
while {[file type $file] == "link"} {
set file [file join [file dirname $file] [file readlink $file]]
}
return $file
}
巧妙地利用了 file join 的特性省略了對目標文件是相對路徑還是全路徑的判斷。

 

17、格式化format


字符 說明
d 有符號整數
u 無符號整數
i 有符號整數。變元可以是十六進制(0x)或八進制(0)格式
o 無符號八進制數
x 或X 無符號十六進制數
c 將整數映射到對應的 ASCII 字符
s 字符串
f 浮點數
e 或E 科學記數法表示的浮點數
g 或G 以%f 或%e 格式(要短一些)來表示的浮點數
- 使字段左對齊
+ 字段右對齊
space 在數字前加一個空格,除非數字帶有前導符號。這在將許多數字排列在一起時非常有用
0 使用 0 作為補白
# 前導 0 表示八進制,前導 0x 表示十六進制數。浮點數中總要帶上小數點。不刪除末尾的 0(%g)

例一
#要取第 2 個變元值,即 5。位置說明符的格式為 2$,並用\來引用符號$:
%set res [format "%2\$s" 1 5 9]
=>5
%puts $res
=>5
%set str [format "%3\$s %1\$s %2\$s" "are" "right" "You"]
=> You are right

例二
%format "%x" 20
=>14 ;# 將 20 轉換為十六進制數
%format "%8x" 20
=> 14 ;# 將 20 轉換為十六進制數,8 位數據寬度,右對齊
%format "%08x" 20
=>00000014 ;#與上一命令相似,但用 0 添齊
%format "%-8x" 20
=>14 ;#寬度 8 位,左對齊
%format "%#08x" 20
=>0x000014

 

18、特殊語法


1){*}
功能:將包含多個元素的列表視為多個值,而不是一個值。
舉例:
% set list1 [list 1 2 3]
>>1 2 3
% set list2 [list a b c]
>>a b c
% set concatenation [list {*}$list1 {*}$list2]
>>1 2 3 a b c
% set concatenation [list $list1 $list2]
>>{1 2 3} {a b c}
2)args
功能:當args是最后一個參數時,它是一個列表,由剩余的全部傳入參數對它賦值。
舉例:
%proc demo {first {second "none"} args} {
puts "first = $first"
puts "second = $second"
puts "args = $args"
}
% demo one two three four five
>>first = one
>>second = two
>>args = three four five
3)subst
功能:通常用於花括號{}內部的反斜線、命令和變量替換
語法:subst ? -nobackslashes? ? -nocommands? ? -novariables? string
描述:這個命令對 string變元執行變量、命令和反斜杠替換,然后返回替換后的結果,替換的方式和執行Tcl命令的方式相同, string變元被替換兩次,一次為Tcl命令的剖析器,另外一次為subst命令。
如果指定了-nobackslashes、-nocommands或-novariables標志位,那么相應的替換將不會執行,比如如果指定了-nocommands標志位,命令替換就不會發生,中括號被當作為普通字符處理。

舉例:
set body {{"oldPsw":"$old_passwd","newPsw":"$new_passwd"}}
set body [subst -nocommands -nobackslashes $body]


19、類與實例(面向對象)


這里介紹基於TclOO的類與實例。
1)聲明一個類
oo::class create MyExampleClass {
# 關鍵字constructor,類實例化時調用這個方法
constructor {args} {# like [proc]
# Do something with the args...
}
# 公共方法用關鍵字method定義,方法名必須是小寫字母開頭
method foo {bar boo} {# like [proc]
puts "bar=$bar, boo=$boo"
}
# 特殊的公共方法
# 當調用未定義的方法時,會轉為調用unknown方法。
method unknown {args} {
puts "unknown is ok"
}
# 通過關鍵字detroy調用destructor方法
destructor {# No arguments
# Clean up here if necessary
}
}
2)類實例化
# 實例化時指定一個名稱
MyExampleClass create exampleObj someArgumentToConstructor...
exampleObj foo 1 2; # prints 'bar=1, boo=2'
exampleObj destroy; # wipes the object out
# 實例化時,賦值給變量
set obj [MyExampleClass new]; # 'Unnamed' object
$obj foo 3 4; # prints 'bar=3, boo=4'
$obj destroy
3)定義實例變量
oo::class create Example2 {
constructor x {
variable X $x
}
method addToX y {
variable X
set X [expr {$X + $y}]
return
}
destructor {
variable X
puts "X is $X"
}
}
Example2 create eg 3
eg addToX 5
eg addToX 2
eg destroy; # Prints 'X is 10'
4)私有方法
# 私有方法名字不以小寫字母開頭。
# 通過關鍵字my調用私有方法。
...
method ThePrivateMethod ... ...
method aPublicMethod ... {
puts "before the private call"
my ThePrivateMethod ...
puts "after the private call"
}
...
5)修改類
# 先定義類的名字。
# 再通過oo::define添加類內部的方法。
oo::class create Example2a
# One syntax style: single argument is definition script
oo::define Example2a {
constructor x {
variable X $x
}
destructor {
variable X
puts "X is $X"
}
}
Example2a create foo 3
# Other syntax style: multiple arguments give a single definition
oo::define Example2a method addToX y {
variable X
set X [expr {$X + $y}]
return
}
6)類的繼承
# 定義父類
oo::class create fruit {
method eat {} {
puts "yummy!"
}
}
# 定義子類
oo::class create banana {
# 繼承父類
superclass fruit
constructor {} {
my variable peeled
set peeled 0
}
method peel {} {
my variable peeled
set peeled 1
puts "skin now off"
}
method edible? {} {
my variable peeled
return $peeled
}
# 修改繼承自父類的方法
method eat {} {
if {![my edible?]} {
my peel
}
next
}
}
set b [banana new]
$b eat → prints "skin now off" and "yummy!"
fruit destroy
$b eat → error "unknown command"
7)self
詳見:https://www.tcl-lang.org/man/tcl/TclCmd/self.htm

[self]:返回當前實例的信息
oo::class create c {
method foo {} {
puts "this is the [self] object"
}
}
c create a
c create b
a foo → prints "this is the ::a object"
b foo → prints "this is the ::b object"
8)next和nextto
詳見:https://www.tcl-lang.org/man/tcl/TclCmd/next.htm
用法1:調用類或者父類中的同名方法
oo::class create theSuperclass {
# 父類中的example方法
method example {args} {
puts "in the superclass, args = $args"
}
}
oo::class create theSubclass {
superclass theSuperclass
# 子類中的example方法
method example {args} {
puts "before chaining from subclass, args = $args"
next a {*}$args b
next pureSynthesis
puts "after chaining from subclass"
}
}
theSubclass create obj
# 實例中的example方法
oo::objdefine obj method example args {
puts "per-object method, args = $args"
next x {*}$args y
next
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
# 調用example方法時,會依次調用實例、類、父類中的example方法
obj example 1 2 3
# 打印如下
per-object method, args = 1 2 3
before chaining from subclass, args = x 1 2 3 y
in the superclass, args = a x 1 2 3 y b
in the superclass, args = pureSynthesis
after chaining from subclass
before chaining from subclass, args =
in the superclass, args = a b
in the superclass, args = pureSynthesis
after chaining from subclass

應用一:
[self next]:判斷父類中有無當前方法
nextto和
oo::class create fruit {
method eat {} {
puts "yummy!"
}
method myeat {} {
my eat
}
}
# 定義子類
oo::class create banana {
# 繼承父類
superclass fruit
constructor {} {
my variable peeled
set peeled 0
}
method peel {} {

if {[self next] ne {}} {
puts "has_next"
puts [self next]
} else {
puts "not has"
}

}
# 修改繼承自父類的方法
method eat {} {
if {[self next] ne {}} {
puts "has_next"
puts [self next]
} else {
puts "not has"
}
}
}
set b [banana new]
$b peel;#輸出not has
$b eat;#輸出has_next
9)oo::class
The oo::class class is the class of all classes;every class is an instance of this class, which is consequently an instance of itself.
每一個類都是oo::class的實例。

This class is a subclass of oo::object, so every class is also an object.
oo::class是oo::object的子類,所以每個類都是一個對象。

10)info object isa object用於測試特定單詞是否指向對象,即變量是否存在
% info object isa object abcde
0
% oo::object create abcde
::abcde
% info object isa object abcde
1
% abcde destroy
% info object isa object abcde
0

info object isa category object ?arg?
This tests whether object (a TclOO object) is a member of the given category (possibly as modified by arg). The supported categories are:

info object isa class object
Is object a class? (Being a class means being an instance of oo::class or one of its subclasses.)

info object isa metaclass object
Is object a metaclass? (A metaclass is a subclass of oo::class.)

info object isa mixin object class
Is class directly mixed into object?

info object isa object command
Is command really an object? (If it is, it will be an instance of oo::object or any of its subclasses; that's the whole TclOO class system though.)

info object isa typeof object class
Is object of the type of the given class? (It is if it is an instance of the class or a one of its direct or indirect subclasses.)
11)類對象自己的方法,類似於靜態方法概念
CLASS_DEFINE AUTOGUI {
superclass OBJ
self {
method class_init {} {
}
}
}


20、twapi

 

1)最小化指定應用的窗口
package require twapi
if {$argc != 1} {
puts stderr "Usage: [info nameofexecutable] $argv0 APPNAME"
puts stderr "Example: [info nameofexecutable] $argv0 notepad"
exit 1
}

set appname [lindex $argv 0]
# Add an extension if none specified
if {[file extension $appname] == ""} {
append appname .*
}

# Get all pids with that app name
set pids [twapi::get_process_ids -glob -name $appname]

# Only minimize if they are marked as visible. This is important
# else hidden windows will be placed on the taskbar as icons
foreach hwin [twapi::find_windows -pids $pids -visible true] {
twapi::minimize_window $hwin
}

2)向指定窗口發送文本信息
package require twapi
if {$argc != 2} {
puts stderr "Usage: [info nameofexecutable] $argv0 WINDOWTITLE TEXT"
exit 1
}

set title [lindex $argv 0]
set data [lindex $argv 1]
# Get all windows with that title
set windows [twapi::find_windows -text $title]
if {[llength $windows]} {
set win [lindex $windows 0]
# Set the focus to the window
twapi::set_focus $win
# Feed data into the input queue
twapi::send_input_text $data
} else {
puts stderr "No windows found with title '$title'"
}

 

20、socket

 

20.1、socket 服務端開啟
格式:socket –server command ?options? port
-server :表明開啟的是服務器端
port:端口
command:當有客戶端來連接的時候,執行這個過程,這個過程有三個參數
channel:給新客戶端的通道
address:提供給客戶端連接的 ip 地址
port:端口

20.2、客戶端連接服務器端
格式:socket ?options? host port
host port :客戶端連接的服務器 ip和端口

20.3、fileevent定義了一個句柄,滿足條件時執行
格式:fileevent channelId readable? script?
fileevent channelId writeable? script?
readable:當通道 channelId 有數據准備好被讀了,執行腳本 script
writeable:當通道 channelId 有數據准備好接收數據了,執行腳本 script

20.4、vwait 命令使執行暫停,直到 varName 被賦值,即便賦值前后相同
格式:vwait varName


例子一:簡單的客戶端連接服務端
-----------------------------------------------------------------------------------------
server:
proc accept {chan addr port} {
puts "$addr:$port says [gets $chan]"
puts $chan goodbye;#向通道發送goodbye
close $chan
}
socket -server accept 12345
vwait forever

client:
set chan [socket 127.0.0.1 12345]
puts $chan hello;#向通道發送hello
flush $chan
puts "server says [gets $chan]"
close $chan

運行結果:
-servr
127.0.0.1:3148 says hello
-client
server says goodbye

 

例子二:顯示服務端時間
格式:clock seconds
功能:返回從計算機紀元開始的秒數,不同操作系統開始時間可能不同,所以這個值通常用來作為命
令 clock format 的輸入
clock format [clock seconds] 返回當前時間
clock format [clock seconds] -format %H:%M:%S 以“時:分:秒”形式返回當前時間
-----------------------------------------------------------------------------------------
server:
proc Server {channel clientaddr clientport} {
puts "Connetion from $clientaddr $clientport registered"
puts $channel [clock format [clock seconds]]
close $channel
}
socket -server Server 9911
vwait forever

client:
set chan [socket 127.0.0.1 9911]
gets $chan line;#讀取通道中的信息
close $chan
puts "The time on $chan is $line"

運行結果:
-server
Connetion from 127.0.0.1 3163 registered
-client
The time on sock268 is Tue Dec 15 09:07:00 +0800 2009

20、子進程調用--open&exec
1.格式:open |progName ?access?

功能:為管道返回一個文件描述符。如果proName用括號括起來,可以包含參數。

2.格式:exec ?switches? arg1? arg2?...?argN?

功能:執行子進程。

*switches :-keepnewline 管道輸出的每行后面加個新行。

--:標志開關結束,哪怕后面跟着有-開頭的字符串,都作為參數處理

*argN 參數可以是可執行程序;作為子程序運行的命令行;輸出重定向。

輸出重定向包括:
序號 重定向 描述
1 | 將標准輸出重定向到標准輸入中
2 <fileName 管道中的第一個程序將讀入文件內容
3 <@fileID 管道中第一個程序將讀入文件描述符中的內容,該文件描述符是使用open.."r" 的返回
4 <<value 管道中第一個程序讀到這個值
5 >fileName 管道中最后一個程序的輸出內容覆蓋寫入文件
6 >>filename 管道中最后一個程序的輸出內容寫入文件,添加到文件的末尾。
7 2>filename 管道中所有程序的標准錯誤輸出到文件,覆蓋文件原有內容
8 2>>filename 管道中所有程序的標准錯誤輸出到文件,添加到文件末尾
9 >@file 管道中程序輸出內容寫到文件描述符,該文件描述符是用open .. W打開

代碼示例如下:
set io [open "|C:/Windows/system/teacup.exe" r+];#open調用外部程序
set invert [exec C:/Windows/system/teacup.exe];#exec調用外部程序

因為管道的例子在 windows 下比較難舉,現以 unix 下一段代碼為例子,通過管道讀出 ps(列出進程)

命令的輸出:
tclfolder%tclsh
% set fid [open "|ps -ef" r+]
file5
% gets $fid line;puts "line: $line"
line: UID PID PPID C STIME TTY TIME CMD
% gets $fid line;puts "line: $line"
line: root 1 0 0 Nov 11 - 0:01 /etc/init
% gets $fid line;puts "line: $line"
line: root 86118 1 0 Nov 11 - 5:00 /usr/sbin/syncd 60
% gets $fid line;puts "line: $line"
line: tclll 90248 893094 0 11:54:55 pts/14 0:00 -csh
% gets $fid line;puts "line: $line"
line: root 110730 1 0 Nov 11 - 0:00 /usr/sbin/uprintfd
% gets $fid line;puts "line: $line"
line: root 118848 1 0 Nov 11 - 0:00 /usr/ccs/bin/shlap64
% exit
tclfolder%

 


注意事項:
1、
set a "hello"
puts "$a"
但不能寫成
puts {$a}
這樣的話$a不會做變量替換。
基本的替換規則:""號中的變量、命令、\替換;{}中的內容不替換。
這里的""和{}都是最外層的,如果是在里面,不影響外層是否替換的規則。

2、給參數傳空值:用引號
proc cal {{a 1} {b 2} {c 3}} {
puts "!$a!$b!$c!"
}
cal 1 "" 3;#輸出!1!!3!

3、給參數傳"[{"a":1,"b":2},{"a":2,"b":4}]"形式的數據,方括號需要進行轉義或者用{}
set data '\[{"a":1,"b":2},{"a":2,"b":4}\]'
puts $data

4、多加空格引起報錯
set data '\[{"a":1,"b":2},{"a":2,"b":4} \]';#右花括號和斜杠之間有一個空格,會引起報錯
puts $data
報錯提示如下
wrong # args: should be "set varName ?newValue?"

5、單引號與雙引號的區別是?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM