newLISP 正則表達式簡介

newLISP 正則表達式規範

newLISP 的正則表達式接口是從 PCRE 標準函數庫中得到的,繼承了大部分的 功能,但不是徹底相同。git

在 newLISP 中表示一個正則表達式的時候,請使用大括號包圍的方式,由於用雙引號的方式,轉義符號表示繁瑣,行爲也有一些怪異的地方。正則表達式

(regex {\Q$\E} "$")
--> ("$" 0 1)
(regex "\Q$\E" "$")
--> nil
(regex "\\Q$\\E" "$")
--> ("$" 0 1)

支持字節和 utf-8 字符的處理

newLISP 支持基於字節和 utf-8 字符的正則表達式處理。只有支持 utf-8 的 newLISP 版本才能處理 utf-8 字符串。express

當啓動一個 REPL 時,出現包含 UTF-8 提示的版本才支持 utf-8 的字符處理:函數

newLISP v.10.6.0 32-bit on Win32 IPv4/6 UTF-8 libffi, options: newlisp -h

> (regex {大象} "大象無形" 2048)
("大象" 0 6)
> (regex {大象} "大象無形")
("大象" 0 6)

在 utf-8 版本的 newLISP 中,regex 的默認正則標識是 2048, 而普通版本的默認正則標識是 0.測試

> (regex {[a-z]+} "thanks")
("thanks" 0 6)
> (regex {[a-z]+} "thanks" 0)
("thanks" 0 6)

字符和元字符

一個正則表達式是一個模式,用於從左到右匹配一個字符串。正則表達式中的大部分字符表明本身,用於匹配字符串中相同的字符。做爲一個簡單的例子,這個匹配模式:ui

The quick brown fox

將會匹配和它同樣的字符串片斷:this

> (regex {The quick brown fox} "get it: The quick brown fox")
--> ("The quick brown fox" 8 19)

當不區分大小寫選項開啓時,匹配的內容將和大小寫無關。spa

> (regex {The quick brown fox} "THE QUICK BROWN FOX" 1)
--> ("THE QUICK BROWN FOX" 0 19)

正則表達式的威力來自可選的分支和重複的模式。描述它們須要用到元字符,它們將按照特殊的規則處理,已經不是本來的意思了。指針

有兩套不一樣的元字符:在正則表達式的方括號 [...] 之間的部分,還有以外的部分。方括號以外的元字符有:code

\      轉義字符,有幾種用處
  ^      字符串開始的斷言 (在多行模式下能夠匹配行的開始)
  $      字符串結束的斷言 (在多行模式下能夠匹配性的結束)
  .      匹配除回車以外的任何字符(默認)
  [      開始一個字符類的定義
  |      開始一個可選的分支
  (      開始一個子表達式
  )      子表達式結束
  ?      (? 擴展了 ( 的意思,也表示 0 或 1 的匹配數量
         也能夠表示最少的匹配
  *      0 或更多的匹配數量
  +      1 或更多的匹配數量,是貪婪匹配符
  {      開始一個 "最少/最多" 的數量界定

在方括號內的模式描述叫 "字符類", 字符類中的元字符有:

\      字符轉義
  ^      把類取反,若是在字符類定義的第一個字符
  -      定義字符的範圍鏈接符
  [      POSIX 字符類定義開始符號 (只有跟隨有效的 POSIX 語法)
  ]      結束一個字符類

下面講講每一個元字符的用法:

反斜槓(BACKSLASH) \

在正則表達式中要表示元字符自己,就要用反斜槓來轉義它。

(regex {\(\)\{\}\[\]\*\?\+\.\$\^\|} "(){}[]*?+$^|")
--> ("(){}[]*?+.$^|" 0 13)

若是不想寫這麼多反斜槓,有個等價的表示方法:

(regex {\Q(){}[]*?+.$^|\E} "(){}[]*?+.$^|")
--> ("(){}[]*?+.$^|" 0 13)

這種寫法在字符類內部也能夠:

(regex {[\Q(){}[]*?+.$^|\E]+} "(){}[]*?+.$^|")
 --> ("(){}[]*?+.$^|" 0 13)

不能打印的字符

newLISP 處理正則表達式,首先是按照字符串的規則處理其中的一些轉義字符:

|-----------+---------------------------------------------|
| 字符      | 描述                                        |
+===========+=============================================+
| \"        | 在字符串內部表示雙引號                      |
|-----------+---------------------------------------------|
| \n        | 回車符 (ASCII 10)                           |
|-----------+---------------------------------------------|
| \r        | 換行符 (ASCII 13)                           |
|-----------+---------------------------------------------|
| \b        | 退格符 (ASCII 8)                            |
|-----------+---------------------------------------------|
| \t        | TAB 符 (ASCII 9)                            |
|-----------+---------------------------------------------|
| \f        | 換頁符 (ASCII 12)                           |
|-----------+---------------------------------------------|
| \nnn      | 三個數字的八進制的字符 ASCII 值             |
|           | (nnn 從 000 到 255)                         |
|-----------+---------------------------------------------|
| \xnn      | 兩個數字的十六進制字符 ASCII 值             |
|           | (xnn 從 x00 到 xff)                         |
|-----------+---------------------------------------------|
| \unnnn    | 四個十六進制數字表示的 unicode 字符         |
|           | four nnnn hexadecimal digits.               |
|           | newLISP 在 UTF8 版本中自動轉化成 UTF8 字符  |
|-----------+---------------------------------------------|
| \\        | 反斜槓本身 (ASCII 92)                       |
|-----------+---------------------------------------------|

一般的字符類

下面的字符類是一些一般使用到的字符類:

\d     任何十進制數字
  \D     任何不是十進制的數字字符
  \s     任何空白字符
  \S     任何不適空白的字符
  \w     任何單詞的字符
  \W     任何不適單詞的字符

這些字符類有三對,分別表明另一個範圍的補集,若是一個字符匹配其中一個, 那麼就補會再匹配另一個了。

這些字符集在字符類內部和外部都是有效的。

\s 不會匹配 VT 符號(CODE 11). 這和 POSIX 中的 [:space:] 不一樣。\s 匹配的 字符有 HT(9), LF(10), FF(12), CR(13) 和 space(32).

newLISP 不支持 \R (換行符序列)

> (find { \R } "\r\n\x0b\f\r\x85" 14)
nil
> (find { \R } "\r\n\x0b\f\r\x85 R" 14)
7

簡單斷言(Simple assertions)

斷言描述一個邊界,多是一行的開始或結束,也多是一個單詞的開始或結束。

\b     匹配一個單詞的邊界
  \B     不是一個單詞的邊界
  \A     匹配一個字符串的開始
  \Z     匹配字符串的末尾,也匹配最後一個回車
  \z     匹配字符串的末尾

這些用法不能用在一個字符類中。

方括號和字符集

一對方括號定義一個字符集。

如 [aeiou] 匹配全部的元音字符,而 [^aeiou] 則匹配元音字符以外的其餘全部字符。

POSIX 字符類

newLISP 支持 POSIX 的字符類:

[01[:alpha:]%]

匹配 "0", "1", 任意字母, 或 "%". 支持的有:

alnum    letters and digits
  alpha    letters
  ascii    character codes 0 - 127
  blank    space or tab only
  cntrl    control characters
  digit    十進制字符,和 \d 相同
  graph    printing characters, excluding space
  lower    小寫字符
  print    打印字符,包括空格
  punct    打印字符,包括數字和字母
  space    空白字符,[\s\x{0b}]
  upper    大寫單詞
  word     單詞字符,和 \w 相同
  xdigit   十六進制數字

POSIX 字符集能夠取反:

[12[:^digit:]]

在 UTF-8 模式下,POSIX 字符集並不會匹配超過 128 的字符。

分支符

垂直分割符定義了兩個分支:

gilbert|sullivan

這個表達式既能夠匹配 "gilbert" 也能夠匹配 "sullivan". 分支能夠有許多,分支的內容能夠爲空。

word1 | word2 | word3 | word4

內部標記

大小寫不敏感 (?i:pattern) -- 大寫字母和小寫字母是同樣的:

(find {(?i:pattern)} "PATTERN") --> 0

多行模式 (?m:pattern) -- ^ 只是匹配行首,而 $ 只匹配行尾

(find {(?m:^abc$)} "ddd\nabc\nfff" 1) --> 3

點擴展模式 (?s:pattern) - 點 (.) 能夠匹配任意字符,包括回車符。

(find {(?s:\A.*?\z)} "hello\world" 1)  --> 0

註釋模式 (?x:pattern) -- 表達式中的空白將被默認刪除,# 號之後到行尾的是註釋

一般能夠一塊兒用:

(find {(?xms: hello # this is comment
              \s+
              world) "hello world" 1)
--> 1

表達式分組

分組表達式是用括號包圍的部分,能夠嵌套,它的做用有:

  1. 從新界定了分支的範圍。例如:

    cat(aract|erpillar|)

將匹配 "cat", "cataract", 或 "caterpillar". 若是沒有分組標記,這個表達式 會匹配 "cataract", "erpillar" 或一個空字符串。

  1. 它同時定義了一個能夠捕獲的分組表達式。意思是說,當進行匹配時,匹配到 分組表達式的字符串將被保存起來。在 newLISP 中,$1, $2, $3 等內部變量將 會保存分組表達式匹配到的字符串片斷。

例如,若是字符串 "the red king" 被下面的模式匹配過:

the ((red|white) (king|queen))

那麼捕獲的子字符串有 "red king", "red", 和 "king", 分別被保存在 $1, $2, $3 中。

若是分組括號第一個字符是問號 (?...), 那麼這個分組不用於捕獲,只用於分組:

the ((?:red|white) (king|queen))

此次捕獲的子字符串只有 "white queen" 和 "queen", 分別被保存在 $1 和 $2 中。 newLISP 最多能夠捕獲 65535 個分組.

命名捕獲 -- newLISP 不支持

重複

重複定義了一個數量,能夠跟在下面的字符後面:

一個字符的字面量 abc
  點 . 
  字符類
  反向引用
  分組表達式 (除非是個斷言)

一般的重複數量包括一個最小值和一個最大值,用大括號包圍在一塊兒,用逗號分隔。 最大的數值不能大於 65536,並且第一個數字必須比第二個要小:

z{2,4}

會匹配 "zz", "zzz", 和 "zzzz". 右大括號自己不是特殊字符,除非先看到左大括號。 若是第二個數字沒有,但逗號有的話,那麼就沒有最大的限制。若是逗號和最大值都忽略了, 那麼就是一個固定的數量限制。

[aeiou]{3,}

這個模式匹配至少 3 個字母,但最多能夠匹配許多許多,而:

\d{8}

只匹配正好 8 個數字。缺乏最小值的數量限制標記是不合法的。就好象:

\w{,10}

這只是能匹配自身的普通字符而已。

爲方便起見,有三個數量限定符設置了簡寫形式:

*    等價於 {0,}
  +    等價於 {1,}
  ?    等價於 {0,1}

一般,數量限定符都是 "貪婪的", 意思是說,他們會盡可能匹配最多的字符,直到 再也匹配不到東西。

若是,一個數量限制標記後面跟一個問號,那麼這個表達式就會變得再也不 "貪婪「, 它將盡可能捕獲儘量少的字符,只要知足條件就能夠了。

/\*.*?\*/

這是 C 語言註釋的模式。它一般是能夠正常工做的。

反向引用

分組捕獲的結果,不但在系統變量中保存,在正則表達式中一樣能夠調用:

(sens|respons)e and \1ibility

將會匹配 "sense and sensibility" 和 "response and responsibility", 而不是 "sense and responsibility".

斷言 (ASSERTIONS)

斷言是在匹配過程當中,對當前狀態的一個測試。並不會讓匹配指針發生變化。

\b \B \A \Z \z ^ $

都是一個斷言描述符。

前瞻斷言 Lookahead assertions

前瞻斷言以 (?= 開始,用於匹配的模式,而 (?! 用於不匹配的模式:

\w+(?=;)

將匹配一個單詞,跟着一個分號,但匹配結果並不包括這個分號:

foo(?!bar)

將匹配任何出現 "foo" 但後面沒有跟着 "bar" 的狀況.

顧後斷言 lookbehind

向後看的語法是匹配 (?\<= 和 不匹配(?\<\!:

> (regex {(?<=[a-z]+)\d+} "..123ab456")
("456" 7 3)
;; 前面匹配的模式不能有不定的數量匹配符號
>(regex {(?<=[a-z]+)\d+} "..123ab456")
ERR: regular expression in function regex :
"offset 10 lookbehind assertion is not fixed length"

> (regex {(?<=[a-z][a-z])\d+} "..123ab456")
("456" 7 3)

> (regex {(?<![a-z])\d+} "..123ab456")
("123" 2 3)

> (regex {(?<![a-b]|[c-d])\d+} "..123ab456")
("123" 2 3)

> (regex {(?<![a-b]|[c-d][e-f])\d+} "..123ab456")
("123" 2 3)

> (regex {ab(?=[0-9])} "abcdab12")
("ab" 4 2)

註釋

newLISP 支持在正則表達式內插入註釋,這讓表達式更具可讀性:

> (regex {(?#this is comment)ab} "ab")
("ab" 0 2)

newLISP 支持遞歸調用

捕獲值做爲函數

newLISP 支持反向引用:

> (regex {(\w+)\d+\1} "abc123abcd")
 ("abc123abc" 0 9 "abc" 0 3)

調用外部函數 -- newLISP 不支持

參考資料

用戶手冊中的 regex replace find 等函數講解了一些正則表達式應用的例子。

Last updated: 2014.06.05 Copyright 2014-2015 Michael.Song.

相關文章
相關標籤/搜索