Perl 6 中的正則表達式(三)

預定義 Subrules

下面這些是為任意 grammar 或 regex 預定義好的 subrules:

  • ident

    匹配一個標識符.

  • upper

    匹配單個大寫字符.

  • lower

    匹配單個小寫字符.

  • alpha

    匹配單個字母字符, 或者是一個下劃線.

    要匹配不帶下劃線的 Unicode 字母字符, 使用 <:alpha>.

  • digit

    匹配單個數字.

  • xdigit

    匹配單個十六進制數字.

  • print

    匹配單個可打印字符.

  • graph

    匹配單個圖形字符.

  • cntrl

    匹配單個控制字符. (等價于 <:Cc> 屬性). 控制字符通常不產生輸出, 相反, 它們以某種方式控制末端:例如換行符和退格符都是控制字符. 所有使用 ord() 之后小于 32 的字符通常歸類為控制字符. 就像 ord() 的值為 127 的字符是控制字符(DEL) 一樣, 128 到 159 之間的也是控制字符.

  • punct

    匹配單個標點符號字符(即, 任何來自于 Unicode General Category "Punctuation" 的字符).

  • alnum

    匹配單個字母數字字符. 那等價于 <+alpha +digit> .

  • wb

    在單詞邊界匹配成功并返回一個零寬匹配. 一個單詞邊界是這樣一個點, 它的一邊是一個 \w, 另一邊是一個 \W. (以任一順序), 字符串的開頭和結尾被看作為匹配 \W.

  • ww

    在兩個單詞字符之間匹配(零寬匹配).

  • ws

    在兩個單詞字符之間匹配要求的空白, 否則匹配可選的空白. 這等價于 \s* (ws 不要求使用 ww subrule).

  • space

    匹配單個空白字符character (和 \s 相同).

  • blank

    匹配單個 "blank" 字符 -- 在大部分區域, 這相當于 spacetab.

  • before pattern

    執行向前查看-- 例如, 檢查我們是否在某個能匹配的位置. 如果匹配成功返回一個零寬 Match 對象.

  • after pattern

    執行向后查看 --例如,檢查當前位置之前的字符串是否匹配 <pattern>(在結尾錨定). 如果匹配成功就返回一個零寬 Match 對象.

  • <?>

    匹配一個 null 字符串,即, 總是返回真.

  • <!>

    <?> 的反轉, 總是返回假
    S05-mass/stdrules.t lines 310–326
    ?

反斜線改良

  • 很多 \p\P 屬性變成諸如 <alpha><-alpha> 等內在的 grammar rules. 它們可以使用上面提到的字符類標記法進行組合.<[-]+alpha+digit>. 不管高層級字符類的名字, 所有的低層級 Unicode 屬性總是可以使用一個前置冒號訪問, 即, 在尖括號中使用 pair 標記法.因此 <+:Lu+:Lt> 等價于 <+upper+title>.

  • \L...\E, \U...\E, 和 \Q...\E 序列被廢棄了. 單個字符的大小寫修飾符 \l\u 也被廢棄了. 在極少需要使用它們的地方, 你可以使用 <{ lc $regex }>, <{tc $word}>, 等.

  • 就像上面提到的, \b\B 單詞邊界斷言被廢棄了, 并且被 <|w> (或 <wb>) 和 <!|w> (或 <!wb>) 零寬斷言代替.

  • \G 序列也沒有了. 使用 :p 代替. (注意, 雖然, 在模式內使用 :p 沒有影響, 因為每個內部模式都被隱式的錨定到當前位置) 查看下面的 at 斷言.

  • 向后引用 (例如. \1, \2, 等.) 都沒有了; 可以使用 $0, $1, 等代替. 因為正則中變量不再進行插值.
    S05-capture/dot.t lines 56–61
    ?
    數字變量被假定是每次都會改變的, 因此被看作是程序化的, 不像普通變量那樣.
    ?

  • 新的反斜線序列, \h\v, 分別匹配水平空白和垂直空白, 包括 Unicode. 水平空白被定義為任何匹配 \s 并且不匹配 \v 的東西. 垂直空白被定義為下面的任一方式:

U+000A  LINE FEED (LF)
U+000B  LINE TABULATION
U+000C  FORM FEED (FF)
U+000D  CARRIAGE RETURN (CR)
U+0085  NEXT LINE (NEL)
U+2028  LINE SEPARATOR
U+2029  PARAGRAPH SEPARATOR

?
注意 U+000D (CARRIAGE RETURN) 被認為是垂直空白.
?

  • \s 現在匹配任何 Unicode 空白字符.

  • 新的反斜線序列和 \N 匹配除邏輯換行符之外的任何字符, 它是 \n 的否定.

  • 其它新的大寫的反斜線序列也都是它們小寫身份的反義:

    • \H 匹配任何非水平空白字符.
    • \V 匹配任何非垂直空白字符.
    • \T 匹配任何非 tab 字符.
    • \R 匹配任何非 return 字符.
    • \F 匹配任何非格式字符.
    • \E 匹配任何非轉義字符.
    • \X... 匹配任何非指定的(指定為十六進制)字符.
    • 在普通字符串中反斜線轉義字面字符串在 regexes 中是允許的(\a, \x 等等). 然而, 這個規則的例外是 \b., 它被禁用了,為了避免跟之前作為單詞邊界斷言沖突. 要匹配字面反斜線, 使用 \c8, \x8或雙引號引起的 \b.

便捷的字符類

因為歷史原因和使用方便, 下面的字符類可以作為反斜線序列使用:

\d      <digit>    A digit
\D      <-digit>   A nondigit
\w      <alnum>    A word character
\W      <-alnum>   A non-word character
\s      <sp>       A whitespace character
\S      <-sp>      A non-whitespace character
\h                 A horizontal whitespace
\H                 A non-horizontal whitespace
\v                 A vertical whitespace
\V                 A non-vertical whitespace

Regexes構成一等語言

而不僅僅是字符串.

  • Perl 5 的 qr/pattern/ 正則構造器滾蛋了.

  • Perl 6 中的正則構造:

regex { pattern }    # 總是把 {...} 作為模式分隔符
rx    / pattern /    # 幾乎能使用任何字符作為模式分割符

S05-metasyntax/delimiters.t lines 6–20

你不能使用空格或字母數字作為分隔符.你可以使用圓括號作為你的 rx 分隔符, 但是這只有當你插入空格時才行(標識符后面直接跟著圓括號會被認為是函數調用):

rx ( pattern )      # okay
rx( 1,2,3 )         # 嘗試調用 rx 函數

(在 Perl 6 中 這對所有類似 quotelike 的結構都適用.)

// 匹配能使用的地方 rx 這種形式也能直接作為模式使用. regex 這種形式實際上是一個方法定義, 必須用于 grammar 類中.

  • 如果 regexrx 需要修飾符, 就把修飾符直接放在開放分隔符前面:
$regex = regex :s:i { my name is (.*) };
$regex = rx:s:i     / my name is (.*) /;    # same thing

如果使用任何括號字符作為分隔符, 那么最后的修飾符之后是需要空格的. ( 否則, 它會被看作修飾符的參數)

  • 不能使用冒號作為分隔符. 修飾符之間可以有空格:
$regex = rx :s :i / my name is (.*) /;
  • 正則構建器的名字是從 qr 修改而來, 因為它不再是一個能插值的 quote-like 操作符. rxregex 的簡寫形式.(不要對regular expressions 感到困惑, 除了它們是的時候 )

  • 像語法指示的那樣, 它現在跟 sub { ... } 構建器很像. 實際上, 這種類似物深深根植于 Perl 6 中.

  • 就像一個原始的 {...}現在總是一個閉包 (它仍然能在特定上下文中被立即執行并在其它上下文中作為對象傳遞), 所以原始的 /.../ 總是一個 Regex 對象(它可能仍然在特定上下文中被立即匹配并在其它上下文中作為對象傳遞.)

  • 特別地, 在 value 上下文(sink, Boolean, stringnumeric),或它是 ~~ 的顯式參數時, /.../會立即匹配. 否則, 它就是一個跟顯式的 regex 形式同一的Regex 構建器, 所以這個:

$var = /pattern/;

不再進行匹配并把結果設置為 $var. 相反, 它把一個 Regex 對象賦值給 $var.

  • 這種情況總是可以使用 m{...}rx{...}進行區分:
$match = m{pattern};    # 立刻匹配 regex, 然后把匹配結果賦值給變量
$regex = rx{pattern};   # Assign regex expression itself
  • 注意前面有個像這樣魔法般的用法:
@list = split /pattern/, $str;

現在來看就是理所當然的了.

  • 就是現在, 建立一個像 grep 那樣的用戶自定義子例程也成為可能:
sub my_grep($selector, *@list) {
  given $selector {
    when Regex { ... }
    when Code  { ... }
    when Hash  { ... }
    # etc.
  }
}

當你調用 my_grep 時, 第一個參數被綁定到 item 上下文, 所以傳遞 {...}/.../ 產生 CodeRegex 對象, switch 語句隨后選中它作為條件.(正常的 grep 只是讓智能匹配操作符做了所有的工作)

  • 就像 rx 擁有變體一樣, regex 聲明符也有. 特別地, regex 有兩個用在 grammars 中的變體: tokenrule.

token聲明長這樣:

token ident { [ <alpha> | \- ] \w* }

默認從不回溯. 即, 它傾向于保留任何目前位置掃描到的東西.所以,上面的代碼等價于:

regex ident { [ <alpha>: | \-: ]: \w*: }

但是相當易讀. 在 token 中裸的 *, +? 量詞絕不回溯. 在普通的 regexes 中, 使用 *:, +:, 或 ?: 阻止量詞的回溯. 如果你確實要回溯, 在量詞后面添加 ?!. ? 像平常一樣進行非懶惰匹配, 而 ! 強制進行貪婪匹配. token 聲明符就是

regex :ratchet { ... }

的簡寫.

另外一個是 rule 聲明符, 像 token 一樣, 它默認也不會回溯. 此外, 一個 rule 這樣的正則表達式也采取了 :sigspace 修飾符. rule 實際上是

regex :ratchet :sigspace { ... }

的簡寫. ratchet 這個單詞的意思是: (防倒轉的)棘齒, 意思它是不能回溯的!

  • Perl 5 的 ?...? 語法(成功一次)極少使用, 并且現在能使用 state 變量以更清晰的方式模擬:
$result = do { state $x ||= m/ pattern /; }    # 只在第一次匹配

要重置模式, 僅僅讓 $x = 0 盡管你想要 $x 可見, 你還是必須避免使用 block:

$result = state $x ||= m/ pattern /;
...
$x = 0;

回溯控制

在這些被認為是程序的而非陳述性的模式的部分中, 你可以控制回溯行為.

  • 默認的, 在 rx,m, s 中的回溯是貪婪的. 在普通的 regex 聲明中它們也是貪婪的. 在 rule 和 token 聲明中, 回溯必須是顯式的.

  • 為了強制前面的原子執行節儉回溯(有時也是所謂的"急切的匹配" 或 "最少化的匹配"), 要在原子后面追加一個 :??. 如果前面的 token 是一個量詞, : 就可以被省略, 所以 *? 就像在 Perl 5 中那樣起作用.

  • 為了強制前面的原子執行貪婪回溯, 在原子后面追加一個 :!. 如果前面的 token 是一個量詞, : 可以被省略. (Perl 5 沒有對應的結構, 因為在 Perl 5 中回溯默認是貪婪的.)

  • 為了強制前面的原子不執行回溯, 使用不帶 ?! 的單個 :. 單個冒號讓正則引擎不再重試前面的原子:
    S05-mass/rx.t lines 8–32

ms/ \( <expr> [ , <expr> ]*: \) /

(i.e. there's no point trying fewer `` matches, if there's no closing parenthesis on the horizon)

當修飾一個量詞的時候, 可以使用 + 代替 :, 這種情況下, 量詞常常是所謂的占有量詞.

ms/ \( <expr> [ , <expr> ]*+ \) /  # same thing

為了強制表達式中所有的原子默認不去回溯, 要使用 :ratchetruletoken.

ms/ [ if :: <expr> <block>
   | for :: <list> <block>
   | loop :: <loop_controls>? <block>
   ]
/

?
到目前為止 2016.4.29 ,(::) 還沒有實現。

:: 還有通過 | 從「最長 token」中隱藏右側的任何陳述性匹配的效果。為了確定性,只有 :: 左側的東西被求值。

如果沒有當前 LTM 備選分支,那么 :: 什么也不會做?!府斍啊故潜粍討B地定義的,而非詞法地。subrule 中的 :: 會影響閉合的備選分支。
? ?

  • ::>進行求值會拋棄當前最里面的臨時備選分支中所有保存的選擇點。它因此表現得像 "then" 那樣。
ms/ [
    || <?{ $a == 1 }> ::> <foo>
    || <?{ $a == 2 }> ::> <bar>
    || <?{ $a == 3 }> ::> <baz>
    ]
/

這兒省略了一部分還沒實現的內容。

Regex 子例程, 具名和匿名

  • subregex 之間的類推更為相似.

  • 就像你可以有匿名子例程和具名子例程...

  • 所以你也可以有匿名正則和具名正則(還有 tokens 和 rules)

token ident { [<alpha>|\-] \w* }
# 然后...
@ids = grep /<ident>/, @strings;
  • 就像上面的例子標示的那樣, 引用具名正則也是可以的, 例如:
regex serial_number { <[A..Z]> \d**8 }
token type { alpha | beta | production | deprecated | legacy }

在其它作為具名斷言的正則表達式中:

rule identification { [soft|hard]ware <type> <serial_number> }

這些使用關鍵字聲明的 regexes 官方類型為 method, 是從 Routine 派生出來的.
?
通常, 任何 subrule 的錨定是由它的調用上下文控制的. 當 regex, token, 或 rule 方法被作為 subrule 調用時, 前面被錨定到當前位置(與 :p 一樣), 而后面沒有被錨定, 因為調用上下文可能會繼續解析. 然而, 當這樣一個方法被直接智能匹配, 它會自動的錨定兩端到字符串的開始和結尾. 因此, 你可以使用一個匿名的 regex 子例程作為單獨的模式來直接模式匹配:
?

$string ~~ regex { \d+ }
$string ~~ token { \d+ }
$string ~~ rule  { \d+ }

它們等價于

$string ~~ m/^ \d+ $/;
$string ~~ m/^ \d+: $/;
$string ~~ m/^ <.ws> \d+: <.ws> $/;

基本經驗就是關鍵字定義的方法絕對不會做 .*? 那樣的掃描, 而如引號那種形式的 m//s/// 在缺少顯式錨定的時候會做這樣的掃描.

rx//// 這兩種形式, 怎么掃描都可以: 當被直接用在智能匹配或布爾上下文中時, 但是當它被作為一個 subrule 間接被調用時,就不會掃描. 即, 當直接使用時, rx// 返回的對象表現的像 m//, 但是用作 subrule 時, 表現的就像 regex {}:

$pattern = rx/foo/;
$string ~~ $pattern;                  # 等價于 m/foo/;
$string ~~ /'[' <$pattern> ']'/       # 等價于 /'[foo]'/

空模式是非法的

S05-mass/rx.t lines 2378–2392
S05-metasyntax/null.t lines 17–25

在 Perl 6 中, Null regex 是非法的:

//

要匹配一個零寬字符, 需要顯式地表示 null 匹配:

 / '' /;
 / <?> /;

例如:

split /''/, $string

分割字符串。所以這樣也行:

split '', $string

同樣地,匹配一個空的分支,使用這個:

 /a|b|c|<?>/
 /a|b|c|''/

更容易捕獲錯誤:

/a|b|c|/

作為一種特殊情況, 匹配中的第一個 null 分支會被忽略:

ms/ [
         | if :: <expr> <block>
         | for :: <list> <block>
         | loop :: <loop_controls>? <block>
    ]
  /

這在格式化 regex 時會有用。

但是注意, 只有第一個分支是特殊的, 如果你這樣寫:

ms/ [
             if :: <expr> <block>              |
             for :: <list> <block>             |
             loop :: <loop_controls>? <block>  |
    ]
  /

就是錯的。因為最后一個分支是 null 。
然而, non-null句法結構有一種退化的情況能匹配 null 字符串:

$something = "";
/a|b|c|$something/;

特別地, <?> 總是成功地匹配 null 字符串, 并且 <!> 總是讓任何匹配都會失敗.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容