newLISP 的正则表达式接口是从 PCRE 标准函数库中得到的,继承了大部分的 功能,但不是彻底相同。git
在 newLISP 中表示一个正则表达式的时候,请使用大括号包围的方式,由于用双引号的方式,转义符号表示繁琐,行为也有一些怪异的地方。正则表达式
(regex {\Q$\E} "$") --> ("$" 0 1) (regex "\Q$\E" "$") --> nil (regex "\\Q$\\E" "$") --> ("$" 0 1)
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 语法) ] 结束一个字符类
下面讲讲每一个元字符的用法:
在正则表达式中要表示元字符自己,就要用反斜杠来转义它。
(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).
> (find { \R } "\r\n\x0b\f\r\x85" 14) nil > (find { \R } "\r\n\x0b\f\r\x85 R" 14) 7
断言描述一个边界,多是一行的开始或结束,也多是一个单词的开始或结束。
\b 匹配一个单词的边界 \B 不是一个单词的边界 \A 匹配一个字符串的开始 \Z 匹配字符串的末尾,也匹配最后一个回车 \z 匹配字符串的末尾
这些用法不能用在一个字符类中。
一对方括号定义一个字符集。
如 [aeiou] 匹配全部的元音字符,而 [^aeiou] 则匹配元音字符以外的其余全部字符。
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
分组表达式是用括号包围的部分,能够嵌套,它的做用有:
从新界定了分支的范围。例如:
cat(aract|erpillar|)
将匹配 "cat", "cataract", 或 "caterpillar". 若是没有分组标记,这个表达式 会匹配 "cataract", "erpillar" 或一个空字符串。
例如,若是字符串 "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 个分组.
重复定义了一个数量,能够跟在下面的字符后面:
一个字符的字面量 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".
断言是在匹配过程当中,对当前状态的一个测试。并不会让匹配指针发生变化。
\b \B \A \Z \z ^ $
都是一个断言描述符。
前瞻断言以 (?= 开始,用于匹配的模式,而 (?! 用于不匹配的模式:
\w+(?=;)
将匹配一个单词,跟着一个分号,但匹配结果并不包括这个分号:
foo(?!bar)
将匹配任何出现 "foo" 但后面没有跟着 "bar" 的状况.
向后看的语法是匹配 (?\<=
和 不匹配(?\<\!
:
> (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 支持反向引用:
> (regex {(\w+)\d+\1} "abc123abcd") ("abc123abc" 0 9 "abc" 0 3)
用户手册中的 regex replace find 等函数讲解了一些正则表达式应用的例子。
Last updated: 2014.06.05 Copyright 2014-2015 Michael.Song.