|
|
正则表达式之基本概念
& s. S; C7 W6 Z3 q& g1 n5 ^! |% {+ h+ C3 ~) b+ L
在我们写页面时,往往需要对表单的数据比如账号、身份证号等进行验证,而最有效的、用的最多的便是使用正则表达式来验证。那什么是正则表达式呢?
. F# [! C' C1 D正则表达式(Regular Expression)是用于描述一组字符串特征的模式,用来匹配特定的字符串。 它的应用非常广泛,特别是在字符串处理方面。其常见的应用如下:* l2 S* o# k) k
9 W' w- D7 I* v: w- 验证字符串,即验证给定的字符串或子字符串是否符合指定的特征,例如,验证是否是合法的邮件地址、验证是否是合法的HTTP地址等等。 2 t1 @! t& R# W7 n" @
- 查找字符串,从给定的文本当中查找符合指定特征的字符串,这样比查找固定字符串更加灵活。 . m5 X B/ J: g X' }2 Q3 D* @$ F# m
- 替换字符串,即查找到符合某特征的字符串之后将之替换。 ' ~' ?! h+ e- Y+ j0 q# k; o# S
- 提取字符串,即从给定的字符串中提取符合指定特征的子字符串。) m+ T/ J& \/ p
第一部分:正则表达式之工具* E |/ T6 f' r$ n8 n
' z- W1 s7 ~! t! \& P
正所谓工欲善其事必先利其器! 所以我们需要知道下面几个主要的工具:
) z$ o0 g1 M( I: G4 U5 Ghttp://www.regexpal.com/ 这个网站中,我们可以在线测试正则表达式。
$ Y# S ?6 p A2 _http://regexr.com/ 这个网站更为推荐,它自身还包括了一个实例使我们直接测试。' c0 U5 N% y d6 x0 D% @4 Z, p
第二部分:正则表达式之元字符
! }7 e+ @3 D1 c5 M6 }" v1 w8 R1 [( p7 e
正则表达式中元字符恐怕是我们听得最多的了。元字符(Metacharacter)是一类非常特殊的字符,它能够匹配一个位置或者字符集合中的一个字符。 如.、\w等都是元字符。 F1 `7 A p- ?8 D- ?/ p
刚刚说到,元字符既可以匹配位置,也可以匹配字符,那么我们就可以通过此来将元字符分为匹配位置的元字符和匹配字符的元字符。
+ h9 t' w$ d" ~/ O1 V% C/ [+ PA匹配位置的元字符---^、$、\b2 k( E! ? } B5 r% V \. L
$ k/ D1 v& N$ T6 N/ \即匹配位置的元字符只有^(脱字符号)、$(美元符号)和\b这三个字符。分别匹配行的开始、行的结尾以及单词的开始或结尾。它们匹配的都只是位置。9 J6 V% g% |& W/ o7 T' f2 N
1.^匹配行的开始位置
9 _% B& J/ L3 W& g. ]
8 j9 X& v: o! J9 |0 I如^zzw匹配的是以"zzw"为行开头的"zzw"(注意:我这里想要表达的是:尽管加了一个^,它匹配的仍是字符串,而不是一整行!),如果zzw不是作为行开头的字符串,则它不会被匹配。
. Y; x* R4 W3 A6 Q1 |# d/ B5 A8 ?5 P; q3 c3 X3 u' K, K9 w; V& i
2.$匹配行的结尾位置
+ i1 ]) A0 ], n5 k# |) e' @
- \+ y( Z% w0 D' V' x( E9 S2 g2 q如zzw$匹配的是以"zzw"为行结尾的"zzw"(同样,这里$只是匹配的一个位置,那个位置是零宽度,而不是一整行),如果zzw不是作为行的结尾,那么它不会被匹配。
& t) x. ]6 X& e1 Q( Y; I% V/ N) `8 e4 t% ^+ J
于是结合^和$我们就不难猜测^zzw$匹配的是某行中只有zzw这个字符串了。
2 E& j; ]" z3 d6 ^ ?/ l3 P% N( O# X* Q) f
而^$匹配的则是一个空行,这个空行中不包含任何字符串。
! ^$ J, `5 A# A2 j% z7 R3. \b匹配单词的开始或结束3 f7 m& x0 }- m( l
# n# l: q. s2 Y* \3 P! {: {) l
如 \bzzw匹配的是zzw之前是空格符号、标点符号或换行符号的zzw(注意:但\b匹配的仅是一个零宽度的位置,而不会匹配空格、标点符号或换行符号)。- |% M! V$ v& x8 j6 l
而zzw\b匹配的是zzw之后是空格符号、标点符号或换行符号的zzw(同样,\b匹配的是一个零宽度的位置)。
0 A4 j4 Q! D. n! I7 z- z显然 \bzzw\b匹配的就是zzw的前后必须是空格符号、标点符号或换行符号的zzw。; ^' e2 T5 I0 b
" _; T1 W4 T" c! c" eB 匹配字符的元字符---. 、\w、\W、\s、\S、\d、\D- m" R c# G. d1 G- m1 ~* \, T
% y7 w N8 J8 D5 X' |
即匹配字符的元字符共有七个。
$ G+ j# ^: C1 \, k8 F& M o6 E8 H& U其中.(点号)表示匹配除换行符之外的任意字符;
' e; `3 _2 @3 Y) n 8 m5 o' T, y" |* _8 }3 P
- \w 匹配单词字符(不仅仅是字母,还有下划线、数字和汉字);\W匹配任意的非单词字符(注意,与\w刚好相反);
, c2 {$ f8 n1 O& S; l - \s匹配的是任意的空白字符(如空格、制表符、换行符、中文全角空格等);
@* A/ q+ X) C) P- p - \S匹配的是任意的非空白字符(注意:刚好和\s相反);
8 P: _, v3 O! I - \d匹配任意的数字;
- f% L3 a3 i+ j5 K& B. o% s - \D匹配任意的非数字字符(注意:刚好和\d相反)。
+ |+ M5 q$ Q& m" |$ d 例子如下所示:
' Y( R z) X$ Y$ Q' i# V! t
4 Z* X: @* }3 h/ S- o# A8 [* e/ {上面的四个例子从左到右依次表示:, c, x7 \: Z/ C' Q
* M! n/ k# @" g+ @1 S- ^.$表示匹配一行中的唯一一个任意的非换行符的字符 ) o7 q2 x3 t) d2 P5 X9 Y
- \ba\w\w\w\w\w\w\w\w\w\b表示匹配以字母a开始的后面有9个字母字符的单词。 (注意:其中的a并不是元字符,就是一个普通的字符,我们称之为字符串字面值(string literal)-所谓字符串字面值,就是字面上看起来是什么就是什么) 3 c X5 D* u- [
- \b\w\w\w\d\d\d\d\D\b表示匹配以3个字母字符开始后面紧跟着四个数字字符且最后一个不是数字字符的单词。9 }" `5 q/ X+ S5 A+ j
第三部分:正则表达式之文字匹配! N2 I6 R7 w9 q
; S) ~* h, k I- G5 M这一部分我们将通过字符类、字符转义、反义这几个部分来学习。
2 E$ x1 f9 {2 a" qA、字符类
/ M% d/ d7 O6 ^
3 n# A' s9 h9 f$ \3 M7 a字符类是正则表达式中的“迷你”语言,可以在[]中定义。: O9 H4 a5 x7 ], R" i! _5 d
最简单的字符类可以由[]和几个简单的字母组成。比如[aeiou]可以匹配aeiou五个字母中的任意一个。[0123456]可以匹配0123456这七个数字中的任意一个。而可以匹配到HTML标记中的中的任意一个标记。而[bhc]at就会匹配字符串bat、hat、cat。也就是说字符类[]中的多个字符只会匹配其中一个。
% X i9 s' Q- Q( Q4 e9 h2 u但是显然[0123456]、这样的表示太麻烦,需要写的很多,所以我们可以使用 -连字符)来简写之,如[0-6]和。于是可知[0-9]和\d的作用是一样的。[a-z]可以表示所有的小写字母,[A-Z]可以表示所有的大写字母。[a-zA-Z]可以表示所有的大写字母和小写字母。5 N+ }, X; L0 L: {$ v0 x& [$ k: A
值得注意的是 -(连字符) 只有在字符类中的中间位置时才是“到”的意思。而[-b]5中-没有在两个字符之间,所以它表示的是-5或者b5。# E4 g8 b( O- }$ E; ^" U# c
除此之外,我们知道^时只匹配行的开头,但是如果^出现在字符类中的第一个位置,那么它表示否定该字符类。如[^123]表示匹配不是数字1或2或3的其他任意字符。[^-]表示匹配不是-的任意字符。
2 S" j. t( v$ l# ?, R! s1 G) V$ r c/ L由此我们也可以发现:在字符类中使用元字符(-、^等)时,不需要进行转义运算。1 `0 {; y1 ?2 q
更常用的有[^aeiou]匹配元音之外的字符、[0-9a-zA-Z_]匹配任何数字、字母(大写和小写)和下划线,这等同于\w、[^0-9a-zA-Z_]匹配任何非数字、字母(大写和小写)和下划线,这等同于\W。
( x9 @ b8 g; a' B" o n- d4 K$ E* E) G, j& `. T2 q
B、字符转义
$ Y/ x7 c+ A9 _3 I! R! z0 T: |- D) w
我们知道元字符如&、^、.等都表示着特殊的涵义,如果我们希望把他们看作一般的字符去匹配字符串,并且恰好他们又都不在字符类(如[&]中),我们就需要使用\(反斜杠)进行转义了。- x. X" @6 `: K% V2 D
如我们可以使用www.jb51.net来匹配www.jb51.net。 我们可以用\*来匹配字符串中的*(通配符)。 我们还可以通过\\来匹配\。举例如下所示:. z6 R4 \+ l, u- J7 R, [
/ q& F3 w3 C# A5 l$ b U
C.反义
8 f; L# |% _4 }0 v) o. l1 u0 m. T( w% u( s. }2 g/ m
实际上我们在前面已经说过了,这里再说一遍是因为希望可以引起注意,即^再字符类中的最前面时表示对这个字符类中的字符表示否定。如a[^b]匹配a和a后面不是b的字符。又如]>表示匹配的字符。举例如下:& m' o8 h4 N) O& K; h
. a- M7 o2 g5 _3 T# L9 q% i; ^
第四部分:正则表达式之限定符! S4 \6 z# @4 f/ L) Y* R. o
$ l2 j1 X# S/ z* r" J2 e! ~
什么是限定符呢? 我们知道,在前面的例子中,我曾经使用过\ba\w\w\w\w\w\w\w\w\w\b表示匹配以字母a开始的后面有9个字母字符的单词,显然这样写是十分麻烦的,如果我们能把\w这些重复的用简单的形式来写就好了~ 没错,限定符就是干这个的,利用限定符我们可以重新写成\ba\w{9}\b。对,就是这么简单! 下面让我们了解更多吧。/ A% w$ t- w! M4 E
* v( n* |% c# E/ }# | p6 A
- {n} 表示重复n次,如\w{5}表示匹配个单词字符。 9 l2 i* t. i9 y6 j& Y0 ?
- {n,} 表示重复至少n次,如\w{5}表示匹配至少5个单词字符,也可以是6个,7个…… ( M* F# f. \7 _/ _' f" O
- {n,m} 表示重复至少n次,最多m次,如\w{5,10} 表示匹配至少5个,最多10个单词字符。 + K6 E1 a# Z7 N% `) v4 a: J9 P
- * 表示重复至少0次。 等同于{0,} ,即hu*t可以匹配ht或hut或huut或huuut……
F) y. p! m& R" u/ h* H; G. X - + 表示重复至少1次。 等同于{1,},即hu+t可以匹配 hut或huut或huuut……
" J4 m2 J; }* } K5 q - ? 表示重复0次或1次。等同于{0,1},即colou?r表示匹配color或者是colour。
+ c4 Z0 _" g1 ^" r* n# F; e 显然,上面的限定符所指的i安定都是限定的前面的某一个字符。
" p4 k" L% L3 r% f% w7 ~) G- a但是,如果我们在上面的限定符之后加上一个?呢 ,这时我们称之为懒惰限定符。 相应地,我们称上面几种匹配为贪婪匹配。
# J2 ~' y- d& g& i5 Y
" h, I1 w0 Y% {. ?! J% U- {n}? 等同于{n} 2 m9 V( l0 `( O$ X0 W1 I$ o
- {n,}? 尽可能少的使用重复,但至少使用n次
" S U" U$ K/ t; D: Y" |/ } - {n,m}? 重复n次到m次之间,但要尽可能少的使用重复。
' ^1 Z$ o: G( a" q1 X3 {1 c! G$ \ - *? 尽可能少的使用重复的第一个匹配 9 H$ e0 U) C6 k5 x& a
- +? 尽可能少的使用重复,但至少使用1次
& f4 r$ U2 `# ^+ `1 N - ?? 使用零次重复(如果有可能)或者一次重复
' e& N2 u4 @/ W) Y 比如:对于aabab这个要 匹配的字符串而言,使用a.*b会匹配aabab,而如果使用a.*?b就会匹配aab和ab,而不是匹配所有。
! G* N+ D8 P7 @0 o- c) r H# k% c! m1 ^- O
第五部分:正则表达式之字符的运算' c& h% E& Q& Y
7 V ]% q9 q8 m4 K7 y字符的运算包括替换、分组和反向引用,下面我将逐项介绍。
# X8 \6 d$ h# eA 替换
8 C7 V. P4 M+ M0 ^* f! U- V6 J8 ]( m/ |
什么是替换呢? 显然就是指一个不行,我用另一个来替换,比如0\d{3}-\d{7}|0\d{2}-\d{8} 就表示匹配前4位为区号、后7位为本地号码的电话号码,亦可匹配前3位为区号、后8位为本地号码的电话号码。| 表示的就是替换了。 又如[Jj]ack和Jack|jack的匹配效果是一样的都是用来匹配Jack或jack的。也就是说,替换|是或运算的关系。2 d# l2 {5 F7 Z0 [* c$ G9 ]
在一般的或运算中0 0结果为0, 0 1结果为1, 1 0结果为1, 1 1结果为1。那么在正则表达式中同样是这样,如果一个都匹配不上就不匹配;如果有一个能匹配上就匹配一个;如果两个都能匹配上,就匹配两个。 举例如下所示:8 R. b$ _9 Q4 M, r P+ R! E+ D4 h% k
6 N9 n+ l2 _& l# O I6 }' R: [8 @! V
B.分组2 W( l! C' X# f1 _# x0 O/ W
9 x; X( l$ Y" E* D7 a% Q' k正则表达式中,分组也是一个非常重要的概念。看似复杂,实际上分组就是使用"("和")",即左圆括号和右圆括号将某些字符括起来看成一个整体来处理。! w. f; `7 H6 `) R0 k/ J
比如我们希望匹配abcabc。如果是abc{3}匹配的就是abccc,这不能达到预期,所以我们可以对abc分组,即(abc){3}就可以匹配到我们想要的字符串了。" e8 W! g8 E8 n/ s9 t1 Z% J
又如(\d{1,3}\.){3}\d{1,3}也用了分组的运算方式,它可以用来匹配简单的IP地址,如下所示:9 k, }4 ]5 F* O( a
1 k; Q' h. m5 |( a/ j! ~
C 反向引用" X" l$ k" l; @2 Z7 {9 J
+ ]6 {) d8 b S7 L0 R
上面我们通过()可以进行分组,而分组的同时,每一个组被自动赋予了一个组号,该组号可以代表该组的表达式。( k6 f+ S0 s6 d% J! P" c. D
编组的规则是:从左到右、以分组的左括号"("为标志,第一个分组的组号为1,第二个分组的组号为2,以此类推。; J6 O6 C7 _# Q C/ _/ u; _$ G, `$ ^
这时,反向引用就派上用场啦。我们就可以用它来反向引用使用()括起来的字符组了。具体怎么引用呢?规则如下:4 X" e1 ~! ?% D/ `% N* C" r& Y5 V7 W) _
; x( ]2 ?" _5 l5 D8 o2 Y3 G+ ~7 ?- K- \数字,使用数字命名的反向引用。注:这个是通用的一种方式。 - i, A3 Z6 s' B! N9 m
- \k,使用指定命名的反向引用。注:这个是.NET Frameword支持的一种方式。
4 O5 f2 m4 l7 t6 } 下面的几个例子是使用数字命名的反向引用:% t% m+ a, Q. s4 A
3 y; ^+ R5 t$ T! O# B
其中我们可以看到第一个和第二个匹配的并不相同---第一个匹配的是两个任意单词字符组成的单词,而第二个由于是使用了反向引用,那么它就必须是两个重复单词字符组成的单词。) C) R1 w. [3 N7 }3 \6 V
最后一个我使用了两个分组,根据分组规则可知\w{3}\d{2}是第一个分组,\d{2}是第二个分组。 同样注意:反向引用的是同样的字符,如www55www5566,最后两位不同,这就无法正确匹配了。
3 ^# {5 _) v$ p: }/ F" G+ c使用指定命名(也就是自定义命名)的反向引用9 n( g3 _9 C; W2 q& b6 F
对于上面的第二个例子,我们用自定义命名的反向引用可以写成\b(?\w)\k\b或者是\b(? 'myName' \w)\k\b。 本想举例试一下,结果都提示错误,可能是上面提到的两个在线网站不支持吧~9 Q( Y, w0 Z* i- E0 n8 F3 ^
当然,如果我们希望只是将之看作一个整体,而不希望给其编号,可以使用(?:expression)的方式。如下所示:
% k1 Q1 p$ x' Q! h! u: I s& i8 `# Q5 p$ y$ C
另外,下面几个也是常用的分组:0 Z' ]/ N) k1 q$ R) \, R& d7 _
I; M( z q8 [1 o' p* f
- (?=expression)匹配字符串expression前面的位置 7 K1 d+ Y. `9 E
- (?!expression)匹配后面不是字符串expression的位置
* T. O$ i$ H! n* ?7 ^) j% M - (?(?>expression)只匹配字符串expression一次 5 A; b) q/ B0 U' y7 S/ |( `# ^
D.零宽度断言
/ t1 F1 S* g3 A& h0 t2 c! a
- f! I R) x& n2 K$ K) i之前介绍的^和$都是匹配的一个满足一定条件的位置。这里把满足的一个条件成为断言或零宽度断言。, h# n9 t+ U( @( q# q. f9 ?) ^
常用的有:
R- x: O$ h! S 6 q4 l! i9 s: F e$ T, H) w
- ^ 匹配行的开始位置
. w: t7 i* F4 s$ l5 e0 O - $ 匹配行的结束位置 ( F4 \# s: n+ G; S+ q% {$ V6 h- Q
- \A 匹配必须出现在字符串的开头
& H1 t- ]9 R5 _% p - \Z 匹配必须出现在字符串的结尾或字符串结尾处的\换行符好n之前 + j+ P, W2 c& e; A( n
- \z 匹配必须出现在字符串的结尾 ( b8 V9 G: g6 b: U3 @9 S6 [% w: n( q/ ^
- \G 匹配必须出现在上一个匹配结束的地方
7 K' F- G) g) v$ U - \b 匹配字符的开始或结束位置
$ d- ?. g0 w5 e8 x - \B 匹配不是在字符的开始或结束位置% i, }0 u# o; p) b0 y, g
之前所提到的(?=expression)、(?!expression)、(?其中(?=expression)称为零宽度预测先行断言,它断言自身位置的后面能够匹配表达式expression。如\b\w+(?=ed\b)可以匹配以字符串ed结尾的单词的前面部分,如reseted中的reset。
" y. H' \8 o, y7 k' Z
p; c4 b4 x# ~% \, `: v- Q6 F' D2 M其中(? |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|