Tcl 程式設計/regexp
另一種語言(我不想稱之為“小”)嵌入在 Tcl 中,即正則表示式。它們可能看起來不像你想象的那樣 - 但它們遵循(許多)規則。目的是描述要匹配的字串模式 - 用於搜尋、提取或替換子字串。
正則表示式用於regexp、regsub 命令,以及lsearch 和switch 的可選使用。請注意,這種語言與 Tcl 本身有很大不同,因此在大多數情況下最好用大括號將 RE 括起來,以防止 Tcl 解析器誤解它們。
在進入詳細內容之前,讓我們先看一些例子
regexp {[0-9]+[a-z]} $input
如果 $input 包含一個或多個數字,後面跟著一個小寫字母,則返回 1。
set result [regsub -all {[A-Z]} $input ""]
從 $input 中刪除所有大寫字母,並將結果儲存到result 變數中。
lsearch -all -inline -regexp $input {^-}
返回列表 $input 中所有以 "-" 開頭的元素。
許多字元只是代表它們自身。例如
a
確實匹配字元 "a"。任何 Unicode 都可以在 \uXXXX 格式中指定。在方括號中(與 Tcl 的方括號不同),定義了一組替代項(一個“類”)
[abc]
匹配 "a"、"b" 或 "c"。兩個字元之間的連字元 (-) 表示它們之間的範圍,例如
[0-9]
匹配一個十進位制數字。要在替代項集中包含文字 "-", 請將其放在首位或末尾
[0-9-]
匹配一個數字或一個減號。用 ^ 開頭的方括號類可以被否定,例如
[^0-9]
匹配任何不是十進位制數字的字元。句號 "." 表示任何字元的一個例項。如果需要文字 "."(或一般來說,要轉義 regexp 語法中具有特殊含義的任何字元),請在它前面放一個反斜槓 "\" - 並確保正則表示式用大括號括起來,以便 Tcl 解析器不會過早地吃掉反斜槓...).
要重複一個字元(集)不止一次,可以在它後面加上量詞,例如
a+ matches one or more "a"s, a? matches zero or one "a", a* matches zero or more "a"s.
還有一種使用大括號進行數字量化的方式(再次強調,與 Tcl 的大括號不同)
a{2} matches two "a"s - same as "aa"
a{1,5} matches one to five "a"s
a{1,} one or more - like a+
a{0,1} zero or one - like a?
a{0,} zero or more - like a*
+ 和 * 量詞是“貪婪的”,也就是說它們會消耗盡可能長的子字串。對於非貪婪的行為,它會提供儘可能短的匹配,在後面新增一個 "?"。示例
% regexp -inline {<(.+)>} <foo><bar><grill>
<foo><bar><grill> foo><bar><grill
這會一直匹配到最後一個閉合括號
% regexp -inline {<(.+?)>} <foo><bar><grill>
<foo> foo
這會一直匹配到第一個閉合括號。
預設情況下,正則表示式可以在字串中的任何位置匹配。你可以將其限制在開頭 (^) 和/或結尾 ($)
regexp {^a.+z$} $input
如果輸入以 "a" 開頭並以 "z" 結尾,並且它們之間有一個或多個字元,則成功。
正則表示式的一部分可以透過用圓括號 () 將其括起來進行分組。這有幾個目的
- regexp 和regsub 可以提取或引用這些子字串
- 可以控制運算子優先順序
"|"(或)運算子具有較高優先順序。所以
foo|bar grill
匹配字串 "foo" 或 "bar grill",而
(foo|bar) grill
匹配 "foo grill" 或 "bar grill"。
(a|b|c) ;# is another way to write [abc]
為了將子字串提取到單獨的變數中,regexp 允許額外的引數
regexp ?options? re input fullmatch part1 part2...
在這裡,變數fullmatch 將接收與正則表示式re 匹配的input 的子字串,而 part1 等接收帶括號的子匹配項。由於fullmatch 通常不需要,因此使用變數名 "->" 在那個位置是一個很常見的習慣用法,例如
regexp {(..)(...)} $input -> first second
將input 的前兩個字元放在變數first 中,將接下來的三個字元放在變數second 中。如果 $input 為 "ab123",則first 將儲存 "ab",而second 將儲存 "123"。
在regsub 和regexp 中,你可以使用 \1 引用第一個帶括號的子匹配項,使用 \2 引用第二個子匹配項,等等。\0 是與上面regexp 中相同的完整匹配項。示例
% regsub {(..)(...)} ab123 {\2\1=\0}
123ab=ab123
這裡 \1 包含 "ab",\2 包含 "123",而 \0 是完整匹配 "ab123"。另一個例子,如何在一個字串中找到四個相同的連續小寫字母(第一個出現,然後是三個)
regexp {([a-z])\1{3}} $input
解析尖括號內的內容(注意,結果包含完整匹配項和子匹配項,以成對的順序排列,因此使用foreach 僅提取子匹配項)
% regexp -all -inline {<([^>]+)>} x<a>y<b>z<c>d
<a> a <b> b <c> c
在數字的整數部分的三位數分組之間插入逗號
% regsub -all {\d(?=(\d{3})+($|\.))} 1234567.89 {\0,}
1,234,567.89
在其他國家/地區,你可能會使用撇號 (') 作為分隔符,或者將數字分組為四位數(在日本使用)。
反過來,將這些格式化的數字轉換回常規方式以用於計算,任務只是刪除所有逗號。這可以用regsub 完成
% regsub -all , 1,234,567.89 "" 1234567.89
但由於任務僅涉及常量字串(逗號和空字串),因此在這裡不使用正則表示式更高效,而是使用string map 命令
% string map {, ""} 1,234,567.89
1234567.89