正则表达式入门

55
© 2006 PISX PISX CO., LIMITED 正正正正正正正 正正

description

 

Transcript of 正则表达式入门

Page 1: 正则表达式入门

© 2006 PISXPISX CO., LIMITED

正则表达式入门

祁宏

Page 2: 正则表达式入门

www.pisx.com2

正则表达式到底是什么

关于正则表达式起源(请自己查阅相关资料!)

规则表达式,字符串规则表达式

我们用过的最简单的规则:文件查找通配符:“ ?” 和“ *”

正则表达式是对一个字符串操作与运算表达式,能由一定标准的解析程序得出运算结果,从而实现字符串匹配与替换等操作的简化。

Page 3: 正则表达式入门

www.pisx.com3

第一个正则表达式

从英文小说中查找 hi

但不能把 him,high,history 也找到,可以

\bhi\b

\b 表示的是一个空格,如果后面不一定有空格,那么, \bhi\w ,那就找到了 him,high,history

假如你要找的是 hi 后面不远处跟着一个 Lucy ,你应该用 \bhi\b.*\bLucy\b 。

其中, .* 连在一起就意味着任意数量的不包含换行的字符。

Page 4: 正则表达式入门

www.pisx.com4

第二个正则表达式

三位区号八位机号的电话号码

0\d\d-\d\d\d\d\d\d\d\d

其中 \d 指的是数字

可以简化为:

0\d{2}-\d{8}

{} 中表示的是重复的次数。

Page 5: 正则表达式入门

www.pisx.com5

正则表达式的测试

PHP 程序员最好用的测试工具是: mysql

主要因为: JS 使用的是 UNIX 正则表达式, PHP 同时支持 PERL 和 UNIX 正则表达式,而 MYSQL 也是 UNIX 正则表达式。

但使用 MYSQL 测试,最好使用 MYSQL FRONT , NAVCAT 这样,比 PHPMYADMIN 中, SQL 查询提交后 SQL 语句不再显示要好一些。

需要注意的是, SQL 语句中需要使用双引号。即让正则表达式在双引号中间。要测试匹配的字符串也要在双引号中。

例如:

mysql> select “fonfo” REGEXP “^fo$”; -> 0 (表示不匹配) mysql> select “fofo” REGEXP “^fo”; -> 1 (表示匹配) 可以看出 “ fonfo” REGEXP “^fo$” 实际是一个运算表达式。

关于 MYSQL 与正则表达式,可以参见:

MySql正则表达式功能的巧用

Page 6: 正则表达式入门

www.pisx.com6

我们走到哪了?

我们现在有以下正则表达式的元字符

\b

\w

\d

.

*

{}

这些都是什么意思?

Page 7: 正则表达式入门

www.pisx.com7

更进一步

\d+ 匹配 1 个或更多连续的数字。下限是至少一次。

而 * 的下限是 0 次

再例:

\b\w{6}\b 匹配刚好 6 个字母 / 数字的单词。

Page 8: 正则表达式入门

www.pisx.com8

常用的元字符

代码 说明

. 匹配除换行符以外的任意字符

\w 匹配字母或数字或下划线或汉字

\s 匹配任意的空白符

\d 匹配数字

\b 匹配单词的开始或结束

^ 匹配字符串的开始

$ 匹配字符串的结束

Page 9: 正则表达式入门

www.pisx.com9

类型匹配—— PERL 与 POSIX

POSIX :[:upper:] :匹配所有的大写字母[:lower:] :匹配所有的小写字母[:alpha:] :匹配所有的字母[:alnum:] :匹配所有的字母和数字[:digit:] :匹配所有的数字[:xdigit:] :匹配所有的十六进制字符,等价于 [0-9A-Fa-f]

[:punct:] :匹配所有的标点符号,等价于 [.,"'?!;:]PERL :\n 换行符 newline (’ 0A )\r 回车符 carriage return (’ 0D )\t 制表符 tab (’ 0 )\d 任一十进制数字\s 任一空白字符\w 任一“字”(可见的)的字符

Page 10: 正则表达式入门

www.pisx.com10

匹配 QQ 号

: ^\d{5,12}$

因为 QQ 号最少是 5 个数字,目前最长是 12 位数字

{5,12} 这里表示了从多少到多少

Page 11: 正则表达式入门

www.pisx.com11

字符转义

要匹配正则表达式中用的字符,如 \

这就需要转义,应当写成 \\

类似,我们还有 :\. \* \+ 等等

Page 12: 正则表达式入门

www.pisx.com12

常用的限定符

代码 / 语法 说明

* 重复零次或更多次

+ 重复一次或更多次

? 重复零次或一次

{n} 重复 n 次

{n,} 重复 n 次或更多次

{n,m} 重复 n 到 m 次

Page 13: 正则表达式入门

www.pisx.com13

字符范围

[aeiou] 匹配任何一个英文元音字母

[.?!] 匹配标点符号 (. 或 ? 或 !)

所以 [0-9] 代表的含意与 \d 就是完全一致

同理 [a-z0-9A-Z_] 也完全等同于 \w (当然,这种等同只能仅考虑英文)

错误 的 QQ 号改正: ^ [1-9] {1}\d{4,11}$

Page 14: 正则表达式入门

www.pisx.com14

反义

反义,有定义的元字符大写,即是反义,同样,字符集前加上 ^ 也是反义。所以:

代码 / 语法 说明

\W 匹配任意不是字母,数字,下划线,汉字的字符

\S 匹配任意不是空白符的字符

\D 匹配任意非数字的字符

\B 匹配不是单词开头或结束的位置

[^x] 匹配除了 x 以外的任意字符

[^aeiou] 匹配除了 aeiou 这几个字母以外的任意字符

Page 15: 正则表达式入门

www.pisx.com15

你可以写了吗?

\(?0\d{2}[) -]?\d{8}

这个表达式, (010)88886666 ,或 022-22334455 ,或 02912345678 等都不出错

但是: 010)12345678 或 (022-87654321 也不出错,怎么办?

提示:语法分组问题!

Page 16: 正则表达式入门

www.pisx.com16

并列

用 | 表示或者,把每一个规则分开,用 |合并为一组共用。

0\d{2}-\d{8}|0\d{3}-\d{7} 表示的是 3 位区号 8 位号码,或 4 位区号 7 位号码

Page 17: 正则表达式入门

www.pisx.com17

分组

字符可以指定重复次数,规则表达式如何指定重复次数?

(\d{1,3}\.){3}\d{1,3} 这是匹配什么? IP地址

(\d{1,3}\.) 这部分是 1 到 3 位数字加“ .” ,用括号表示为一个子表达式,并且 {3} 表示重复 3 次。是不是 IP地址?好象是!

错在何处?因为可以匹配 999.999.999.999

再次改进:

((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)

首位如果是 2 ,则只能是 0 到 4 。后面随意 ,首 2 位是 25 ,后面只能 0 到 5 。首位 0 或1 ,后面随意。

完美了吗?没有,为什么?

^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5]\.){3} (\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$  

Page 18: 正则表达式入门

www.pisx.com18

后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的文本 ( 也就是此分组捕获的内容 ) 可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为 1 ,第二个为 2 ,以此类推。

组号保存的结果,实际如同程序中变量或数组保存数据一样。对组号的调用,如果是使用变量。

Page 19: 正则表达式入门

www.pisx.com19

后向引用示例

后向引用用于重复搜索前面某个分组匹配的文本。例如,\1 代表分组 1 匹配的文本。难以理解?请看示例:

\b(\w+)\b\s+\1\b 可以用来匹配重复的单词,像 go go, kitty kitty 。首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字 (\b(\w+)\b) ,然后是 1个或几个空白符 (\s+) ,最后还是前面匹配的那个单词 (\1) 。

Page 20: 正则表达式入门

www.pisx.com20

命名分组

()内的子表达式,不仅可以用编号,也可以用名称,如同变量有变量名。

(?<Word>\w+)

(?’Word’\w+)

<> 或‘’内均是分组名称,这样。下面的例子:

\b(?<Word>\w+)\b\s+\k<Word>\b

(?#变量声明标志’ Word’ #变量名 \w+# 表达式 )

Page 21: 正则表达式入门

www.pisx.com21

分组语法 - 捕获

(exp) 匹配 exp ,并捕获文本到自动命名的组里

(?<name>exp) 匹配 exp,并捕获文本到名称为 name 的组里,也可以写成 (?’name’exp)

(?:exp) 匹配 exp, 不捕获匹配的文本,也不给此分组分配组号

Page 22: 正则表达式入门

www.pisx.com22

零宽断言

所谓零宽断言,实际是用语法指定匹配时的起点或终点。或者说,是一种扩展通配方式。零宽断言本意: A 表达式与 B 表达式是零宽的,即没有任何间隔的,同时,断言或确认某一关系为真,即: B 前面是 A 或不是 A为真;或者, A 后面是 B 或不是 B 为真。

表达式结构:

(?=exp) 匹配 exp 前面的位置,即某表达式后面是 exp

(?<=exp) 匹配 exp 后面的位置,即某表达式前面是 exp

(?!exp) 匹配后面跟的不是 exp 的位置,即某表达式后面不是 exp

(?<!exp) 匹配前面不是 exp 的位置,即某表达式前面不是 exp

Page 23: 正则表达式入门

www.pisx.com23

零宽断言实例

\b\w+(?=ing\b) ,匹配以 ing 结尾的单词的前面部分 ( 除了 ing 以外的部分 ) : dancing,reading

(?<=\bre)\w+\b会匹配以 re 开头的单词的后半部分 ( 除了re 以外的部分 ) , read, return

Page 24: 正则表达式入门

www.pisx.com24

负向零宽断言 (-)

前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法 ( 反义 ) 。但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?

例如,如果我们想查找这样的单词 --它里面出现了字母 q,但是 q 后面跟的不是字母 u, 我们可以尝试这样:

b\w*q[^u]\w*\b 匹配包含后面不是字母 u 的字母 q 的单词。但是如果多做测试 ( 或者你思维足够敏锐,直接就观察出来了 )

Page 25: 正则表达式入门

www.pisx.com25

负向零宽断言 (=)

你会发现,如果 q 出现在单词的结尾的话,像 Iraq,Benq ,这个表达式就会出错。这是因为 [^u]总要匹配一个字符,所以如果 q 是单词的最后一个字符的话,后面的 [^u]将会匹配 q 后面的单词分隔符 ( 可能是空格,或者是句号或其它的什么 ) ,后面的 \w*\b将会匹配下一个单词,于是 \b\w*q[^u]\w*\b 就能匹配整个 Iraq fighting 。

负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题: \b\w*q(?!u)\w*\b 。

Page 26: 正则表达式入门

www.pisx.com26

负向零宽断言实例

零宽度负向预测先行断言 (?!exp) ,断言此位置的后面不能匹配表达式 exp。例如: \d{3}(?!\d) 匹配三位数字,而且这三位数字的后面不能是数字; \b((?!abc)\w)+\b 匹配不包含连续字符串 abc 的单词。

同理,我们可以用 (?<!exp), 零宽度正回顾后发断言来断言此位置的前面不能匹配表达式 exp : (?<![a-z])\d{7} 匹配前面不是小写字母的七位数字。

Page 27: 正则表达式入门

www.pisx.com27

负向零宽断言复杂实例

一个更复杂的例子:

(?<=<(\w+)>).*(?=<\/\1>) 匹配不包含属性的简单 HTML 标签内里的内容。

(<?(\w+)>) 指定了这样的前缀:被尖括号括起来的单词 ( 比如可能是 <b>) ,然后是 .*( 任意的字符串 ), 最后是一个后缀 (?=<\/\1>) 。

注意后缀里的 \/ ,它用到了前面提过的字符转义;

\1 则是一个反向引用,引用的正是捕获的第一组,前面的 (\w+) 匹配的内容,这样如果前缀实际上是 <b> 的话,后缀就是 </b> 了。

整个表达式匹配的是 <b> 和 </b>之间的内容 ( 再次提醒,不包括前缀和后缀本身 ) 。

Page 28: 正则表达式入门

www.pisx.com28

注释

小括号的另一种用途是能通过语法 (?#comment)来包含注释。

如: ^(\d{1,2}(?#0-99)|1\d\d(?#100-199)|2[0-4]\d(?#200-249)|25[0-5]\.){3}(?#250-255) (\d{1,2} (?#-0-99) |1\d\d (?#100-199) |2[0-4]\d(?#200-249)|25[0-5])(?#250-255)$

Page 29: 正则表达式入门

www.pisx.com29

注释

要包含注释的话,最好是启用“忽略模式里的空白符”选项 (x模式修正符 ),这样在编写表达式时能任意的添加空格, Tab ,换行,而实际使用时这些都将被忽略。启用这个选项后,在 # 后面到这一行结束的所有文本都将被当成注释忽略掉。

例如,我们可以前面的一个表达式写成这样:

(?<=     # 断言要匹配的文本的前缀 <(\w+)>  #  查找尖括号括起来的字母或数字 ( 即 HTML/XML 标签 )         )       #  前缀结束 .*       #  匹配任意文本 (?=      # 断言要匹配的文本的后缀 <\/\1>   #  查找尖括号括起来的内容:前面是一个 "/" ,后面是先前捕获的标签 )        #  后缀结束

Page 30: 正则表达式入门

www.pisx.com30

修正符( 1 )

修正符用来调正表达式全局或局部的匹配选项。常用修正符:i ( PCRE_CASELESS ):匹配时忽略大小写。

m ( PCRE_MULTILINE ):当设定了此修正符,行起始 (^)和行结束 ($)除了匹配整个字符串开头和结束外

,还分别匹配其中的换行符 (\n)的之后和之前。

s ( PCRE_DOTALL ):如果设定了此修正符,模式中的圆点元字符( .)匹配所有的字符,包括换行符

。没有此设定的话,则不包括换行符。

x ( PCRE_EXTENDED ):如果设定了此修正符,模式中的空白字符除了被转义的或在字符类中的以外完全被忽略。

e :如果设定了此修正符, preg_replace() 在替换字符串中对逆向引用作正常的替

换,将其作为 PHP 代码求值,并用其结果来替换所搜索的字符串。 只有 preg_replace() 使用此修正符,其它 PCRE 函数将忽略之。

Page 31: 正则表达式入门

www.pisx.com31

修正符( 2 )

A ( PCRE_ANCHORED ):如果设定了此修正符,模式被强制为“ anchored” ,即强制仅从目标字符串的开头开始匹

配。D ( PCRE_DOLLAR_ENDONLY ):如果设定了此修正符,模式中的行结束 ($) 仅匹配目标字符串的结尾。没有此选项时,如果

最后一个字符是换行符的话,也会被匹配在里面。如果设定了 m 修正符则忽略此选项。

S当一个模式将被使用若干次时,为加速匹配起见值得先对其进行分析。如果设定了此修正

符则会进行额外的分析。目前,分析一个模式仅对没有单一固定起始字符的 non-anchored 模式有用。

U ( PCRE_UNGREEDY ):使“ ?” 的默认匹配成为贪婪状态的。X ( PCRE_EXTRA ):模式中的任何反斜线后面跟上一个没有特殊意义的字母导致一个错误,从而保留此组合以备将来扩充。默认情况下,一个反斜线后面跟一个没有特殊意义的字母被当成该字母本身。

u ( PCRE_UTF8 ):模式字符串被当成 UTF-8 。 '/[ 一 -龥 ]/u'

Page 32: 正则表达式入门

www.pisx.com32

修正符的使用

PHP

if (preg_match('/\b[A-F0-9]{8}(?:-[A-F0-9]{4}){3}-[A-Z0-9]{12}\b/simx', $subject)) {

} else {

}

JS

if (subject.match(/"[^"\r\n]*"/im)) {

} else {

}

局部修正符:/ab(?i)c/ == /ab[cC]/

Page 33: 正则表达式入门

www.pisx.com33

贪婪与懒惰

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。

考虑这个表达式: a.*b ,它将会匹配最长的以 a 开始,以b 结束的字符串。如果用它来搜索 aabab 的话,它会匹配整个字符串 aabab 。这被称为贪婪匹配。

Page 34: 正则表达式入门

www.pisx.com34

贪婪与懒惰

有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。

前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号 ? 。这样 .*? 就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。

Page 35: 正则表达式入门

www.pisx.com35

贪婪与懒惰

对于 aabab

a.*?b 匹配最短的,以 a 开始,以 b 结束的字符串。如果把它应用于 aabab 的话,它会匹配 aab和 ab (为什么第一个匹配是 aab 而不是 ab ?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配最有最大的优先权

Page 36: 正则表达式入门

www.pisx.com36

懒惰限定符

*? 重复任意次,但尽可能少重复

+? 重复 1 次或更多次,但尽可能少重复

?? 重复 0 次或 1 次,但尽可能少重复

{n,m}? 重复 n 到 m 次,但尽可能少重复

{n,}? 重复 n 次以上,但尽可能少重复

Page 37: 正则表达式入门

www.pisx.com37

条件式匹配( Conditional subpatterns )

类似三元表达式的一种结构:

(?(condition)yes-pattern)

(?(condition)yes-pattern|no-pattern)

Page 38: 正则表达式入门

www.pisx.com38

优先级顺序

操作符 描述

\ 转义符

(), (?:), (?=), [] 圆括号和方括号

*, +, ?, {n}, {n,}, {n,m} 限定符

^, $, \anymetacharacter 位置和顺序

| “ 或”操作

Page 39: 正则表达式入门

www.pisx.com39

JS 中的应用

1.js 里面通过 RegExp 类来实现正则表达式的。

var reCat = new RegExp("cat");     // 匹配串中第一个 "cat"    var reCat = new RegExp("cat","g"); // 匹配串中所有的 "cat", "g" 是 global 的意思 var reCat = new RegExp("cat","gi");  // 匹配串中所有的 "cat" ,不区分大消息, "i" 是 insensitive 的缩写 var reCat = /cat/gi;        //perl风格

Page 40: 正则表达式入门

www.pisx.com40

JS 中的应用

2.RegExp 的方法

(1)test() 判断是否匹配,返回 true 或者 false    var sTomatch ="cat";    var reCat = /cat/;    alert(reCat.test(sTomatch));//outputs true

    (2)exec() 返回一个数组,数组中的第一个条目是第一个匹配 var sTomatch = "a bat, a Cat, a fAt baT, a faT cat";    var reAt = /at/;    var arrMatches = reAt.exec(sTomatch);    alert(arrMatches.toString()); //outputs 'at'

Page 41: 正则表达式入门

www.pisx.com41

JS 中的应用

String 对象对正则表达式的支持 (1)match()方法 var sTomatch = "a bat, a Cat, a fAt baT, a faT cat";    var reAt = /at/gi;    var arrMatches = sTomatch.match(reAt);    alert(arrMatches.toString()); //outputs 'at,at, At ,aT ,aT ,at';   

Page 42: 正则表达式入门

www.pisx.com42

JS 中的应用

(2)replace()方法 var sToChange = “this red paper is not very red”;    alert(sToChange.replace(“red”,“blue”));

只替换第一个“ red” ,outputs “this blue paper is not very red”要想全部替换 , 使用全程修正符,如下:

    var sToChange = "this red paper is not very red";    var reRed = /red/gi;    alert(sToChange.replace(reRed,"blue"));   

Page 43: 正则表达式入门

www.pisx.com43

JS 中的应用

(3)split()方法 var sColor = “red,blue,yellow,green”;    var reCom = /\,/ ;  // 因为“ ,” 是正则表达式的语法元字符,所以用“ \”来转义(注:也可以不转义,因为“ ,” 不是用于匹配的。) var arrColors = sColor.split(reCom);   上面方法的效果等同于 var sColor = "red,blue,yellow,green";    var arrColors = sColor.split(",");

另外: search 方法也支持正则表达式。

Page 44: 正则表达式入门

www.pisx.com44

Php 中的应用

使用 POSIX兼容规则的函数有:ereg_replace() ereg() eregi() eregi_replace() split() spliti() sql_regcase() mb_ereg_match() mb_ereg_replace() mb_ereg_search_getpos() mb_ereg_search_getregs() mb_ereg_search_init() mb_ereg_search_pos() mb_ereg_search_regs() mb_ereg_search_setpos() mb_ereg_search() mb_ereg() mb_eregi_replace() mb_eregi() mb_regex_encoding() mb_regex_set_options() mb_split()

使用 PERL兼容规则的函数有:preg_grep() preg_replace_callback() preg_match_all() preg_match() preg_quote() preg_split() preg_replace()

Page 45: 正则表达式入门

www.pisx.com45

PHP 实例

以下代码将 pow(m) 替换成 power(m,2)

$rule=preg_replace("/pow\(([A-Za-z0-9\+\-\*\/\(\)]+)\)/i","power(\$1 ,2)",$rule);

以下代码将数据库的前缀去除,如 pisx_dbname 替换成 dbname

$cond = preg_replace('|('.$this->dbprefix.')([\w\.]+)([\W\s]+)|', "$2$3", $cond);

复习:

\W 匹配任意不是字母,数字,下划线,汉字的字符

\S 匹配任意不是空白符的字符

Page 46: 正则表达式入门

www.pisx.com46

PHP 实例

在路径后面加上“ /” , (.+?) 是匹配到的目录串,但没有后面的那个“ /”

\/* 则表明了可以匹配 0 到 1 次, \\1变量或称后向引用代表 (.+?) 中匹配的内容

$path = preg_replace("/(.+?)\/*$/", "\\1/", $path);

\\1与\$1是等价的,但 \$1 是在 PHP4.04 以后的版本才支持

匹配扩展名:

if (preg_match("|.+?\.zip$|", $prefs))

Page 47: 正则表达式入门

www.pisx.com47

正则表达式工具

RegexBuddy

是一款向导型并且可以写任何语言所用的正则表达式工具。利用它,我们可以快速创建并测试正则表达式。

Page 48: 正则表达式入门

www.pisx.com48

RegexBuddy 使用

假如是要进行替换。一开始,我们先可以创建测试模式的正则表达式,即首先以匹配方式把表达式写正确。

使用 RegexBuddy 不需要任何帮助文档。因为,可以在正则表达式录入区域点击鼠标右键,选择你要匹配的元素。修正符,则是直接点击修正符工具栏即可。

也可以点击 Create选项卡,选择插入你要匹配的元素。

同时, RegexBuddy 还提供很多现成的类库,点击 Libaray选项卡,找到合适的,直接拿来使用即可。

最后,正则表达式写好后,点击 Use选项卡,选择你需要的目标编程语言,它会为你自动生成代码。

如果匹配有问题,你还可以点击 Debug选项卡进行调试,查看为什么匹配失败。调试信息什么告诉你,你提供的样本字串到哪一步失败了。

Page 49: 正则表达式入门

www.pisx.com49

RegexBuddy图解——在 Create选项卡中操作插入

Page 50: 正则表达式入门

www.pisx.com50

RegexBuddy图解——用鼠标右键选择插入

Page 51: 正则表达式入门

www.pisx.com51

RegexBuddy图解——生成代码

Page 52: 正则表达式入门

www.pisx.com52

另一个正则表达式工具—— EmEditor

EmEditor 也可以说是一个正则表达式向导,但支持的正则表达式可能相对简单一些。不过,用它写正则表达式也是相当快的。因为,其界面是中文的。

EmEditor按 CRTL+F ,查找,点选正则表达式,然后点查找文本右侧的” >” 即可打开正则表达式录入菜单。

Page 53: 正则表达式入门

www.pisx.com53

EmEditor 中编写正则表达式

Page 54: 正则表达式入门

www.pisx.com54

参考资料

deerchao《正则表达式 30 分钟入门教程 》

马骁驰《正则表达式》

Microsoft《正则表达式简介》

Page 55: 正则表达式入门

www.pisx.com55

谢谢大家