跳轉到內容

Tcl 程式設計/regexp

來自 Wikibooks,開放世界中的開放書籍

另一種語言(我不想稱之為“小”)嵌入在 Tcl 中,即正則表示式。它們可能看起來不像你想象的那樣 - 但它們遵循(許多)規則。目的是描述要匹配的字串模式 - 用於搜尋、提取或替換子字串。

正則表示式用於regexpregsub 命令,以及lsearchswitch 的可選使用。請注意,這種語言與 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" 結尾,並且它們之間有一個或多個字元,則成功。

正則表示式的一部分可以透過用圓括號 () 將其括起來進行分組。這有幾個目的

  • regexpregsub 可以提取或引用這些子字串
  • 可以控制運算子優先順序

"|"(或)運算子具有較高優先順序。所以

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"。

regsubregexp 中,你可以使用 \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
華夏公益教科書