7. 正則表達式匹配规则
7.1 基本模式匹配
一切从最主要的開始。模式。是正规表达式最主要的元素。它们是一组描写叙述字符串特征的字符。模式能够非常easy,由普通的字符串组成。也能够非常复杂。往往用特殊的字符表示一个范围内的字符、反复出现。或表示上下文。比如:
^once
这个模式包括一个特殊的字符^,表示该模式仅仅匹配那些以once开头的字符串。比如该模式与字符串"once upon a time"匹配,与"There once was a man from NewYork"不匹配。正如如^符号表示开头一样,$符号用来匹配那些以给定模式结尾的字符串。
bucket$
这个模式与"Who kept all of this cash in a bucket"匹配,与"buckets"不匹配。字符^和$同一时候使用时,表示精确匹配(字符串与模式一样)。比如:
^bucket$
仅仅匹配字符串"bucket"。假设一个模式不包括^和$。那么它与不论什么包括该模式的字符串匹配。比如:模式
once
与字符串
There once was a man from NewYork
Who kept all of his cash in a bucket.
是匹配的。
在该模式中的字母(o-n-c-e)是字面的字符,也就是说,他们表示该字母本身。数字也是一样的。其它一些略微复杂的字符,如标点符号和白字符(空格、制表符等)。要用到转义序列。全部的转义序列都用反斜杠(\)打头。制表符的转义序列是:\t。所以假设我们要检測一个字符串是否以制表符开头。能够用这个模式:
^\t
类似的。用\n表示“新行”,\r表示回车。其它的特殊符号。能够用在前面加上反斜杠,如反斜杠本身用\\表示,句号.用\.表示,以此类推。
7.2 字符簇
在INTERNET的程序中。正规表达式通经常使用来验证用户的输入。当用户提交一个FORM以后,要推断输入的电话号码、地址、EMAIL地址、信用卡号码等是否有效,用普通的基于字面的字符是不够的。
所以要用一种更自由的描写叙述我们要的模式的办法,它就是字符簇。要建立一个表示全部元音字符的字符簇,就把全部的元音字符放在一个方括号中:
[AaEeIiOoUu]
这个模式与不论什么元音字符匹配,但仅仅能表示一个字符。用连字号能够表示一个字符的范围。如:
[a-z] //匹配全部的小写字母
[A-Z] //匹配全部的大写字母
[a-zA-Z] //匹配全部的字母
[0-9] //匹配全部的数字
[0-9\.\-] //匹配全部的数字。句号和减号
[ \f\r\t\n] //匹配全部的白字符
相同的,这些也仅仅表示一个字符,这是一个很重要的。假设要匹配一个由一个小写字母和一位数字组成的字符串。比方"z2"、"t6"或"g7",但不是"ab2"、"r2d3" 或"b52"的话。用这个模式:
^[a-z][0-9]$
虽然[a-z]代表26个字母的范围,但在这里它仅仅能与第一个字符是小写字母的字符串匹配。
前面以前提到^表示字符串的开头,但它还有另外一个含义。
当在一组方括号中使用^是,它表示“非”或“排除”的意思,经常常使用来剔除某个字符。还用前面的样例。我们要求第一个字符不能是数字:
^[^0-9][0-9]$
这个模式与"&5"、"g7"及"-2"是匹配的。但与"12"、"66"是不匹配的。以下是几个排除特定字符的样例:
[^a-z] //除了小写字母以外的全部字符
[^\\\/\^] //除了(\)(/)(^)之外的全部字符
[^\"\‘] //除了双引號(")和单引號(‘)之外的全部字符
特殊字符"." (点,句号)在正规表达式中用来表示除了“新行”之外的全部字符。所以模式"^.5$"与不论什么两个字符的、以数字5结尾和以其它非“新行”字符开头的字符串匹配。模式"."能够匹配不论什么字符串。除了空串和仅仅包含一个“新行”的字符串。
PHP的正规表达式有一些内置的通用字符簇。列表例如以下:
字符簇 含义
[[:alpha:]] 不论什么字母
[[:digit:]] 不论什么数字
[[:alnum:]] 不论什么字母和数字
[[:space:]] 不论什么白字符
[[:upper:]] 不论什么大写字母
[[:lower:]] 不论什么小写字母
[[:punct:]] 不论什么标点符号
[[:xdigit:]] 不论什么16进制的数字,相当于[0-9a-fA-F]
7.3 确定反复出现
到如今为止,你已经知道怎样去匹配一个字母或数字。但很多其它的情况下,可能要匹配一个单词或一组数字。 一个单词有若干个字母组成,一组数字有若干个单数组成。跟在字符或字符簇后面的花括号({})用来确定前面的内容的反复出现的次数。
字符簇 含义
^[a-zA-Z_]$ 全部的字母和下划线
^[[:alpha:]]{3}$ 全部的3个字母的单词
^a$ 字母a
^a{4}$ aaaa
^a{2,4}$ aa,aaa或aaaa
^a{1,3}$ a,aa或aaa
^a{2,}$ 包括多于两个a的字符串
^a{2,} 如:aardvark和aaab,但apple不行
a{2,} 如:baad和aaa,但Nantucket不行
\t{2} 两个制表符
.{2} 全部的两个字符
这些样例描写叙述了花括号的三种不同的使用方法。一个数字。{x}的意思是“前面的字符或字符簇仅仅出现x次”;一个数字加逗号,{x,}的意思是“前面的内容出现x或很多其它的次数”。两个用逗号分隔的数字,{x,y}表示“前面的内容至少出现x次。但不超过y次”。我们能够把模式扩展到很多其它的单词或数字:
^[a-zA-Z0-9_]{1,}$ //全部包括一个以上的字母、数字或下划线的字符串
^[0-9]{1,}$ //全部的正数
^\-{0,1}[0-9]{1,}$ //全部的整数
^\-{0,1}[0-9]{0,}\.{0,1}[0-9]{0,}$ //全部的小数
最后一个样例不太好理解,是吗?这么看吧:与全部以一个可选的负号(\-{0,1})开头(^)、跟着0个或很多其它的数字([0-9]{0,})、和一个可选的小数点(\.{0,1})再跟上0个或多个数字([0-9]{0,}),而且没有其它不论什么东西($)。 以下你将知道可以使用的更为简单的方法。
特殊字符"?
"与{0,1}是相等的。它们都代表着:“0个或1个前面的内容”或“前面的内容是可选的”。所以刚才的样例能够简化为:
^\-?[0-9]{0,}\.?[0-9]{0,}$
特殊字符"*"与{0,}是相等的,它们都代表着“0个或多个前面的内容”。 最后。字符"+"与 {1,}是相等的。表示“1个或多个前面的内容”,所以上面的4个样例能够写成:
^[a-zA-Z0-9_]+$ //全部包括一个以上的字母、数字或下划线的字符串
^[0-9]+$ //全部的正数
^\-?[0-9]+$ //全部的整数
^\-?[0-9]*\.?[0-9]*$ //全部的小数
当然这并不能从技术上减少正规表达式的复杂性。但能够使它们更easy阅读。
一、正則表達式概述:
假设原来没有使用过正則表達式,那么可能对这个术语和概念会不太熟悉。 只是。它们并非您想象的那么新鲜。
请回忆一下在硬盘上是怎样查找文件的。您肯定会使用 ? 和 * 字符来帮助查找您正寻找的文件。?
字符匹配文件名称中的单个字符,而 * 则匹配一个或多个字符。一个如 ‘data? .dat‘ 的模式能够找到下述文件:data1.dat、data2.dat等等。 假设使用 * 字符取代 ? 字符,则将扩大找到的文件数量。‘data*.dat‘ 能够匹配下述全部文件名称:data.dat、data1.dat、data12.dat等等,虽然这样的搜索文件的方法肯定非常实用。但也十分有限。 ? 和 * 通配符的有限能力能够使你对正則表達式能做什么有一个概念,只是正則表達式的功能更强大。也更灵活。
在我们编写ASP程序时。常常会推断一个字符串的有效性。如;一个串是否是数字、是否是有效的Email地址等等。假设不使用正則表達式,那么推断的程序会非常长。而且easy出错。假设使用正則表達式。这些推断就是一件非常轻松的工作了。 后面我们将介绍怎样推断数字和Email地址的有效性。
在典型的搜索和替换操作中,必须提供要查找的确切文字。 这样的技术对于静态文本中的简单搜索和替换任务可能足够了,可是因为它缺乏灵活性,因此在搜索动态文本时就有困难了,甚至是不可能的。
使用正則表達式。能完毕些什么事情呢?
測试字符串的某个模式。比如,能够对一个输入字符串进行測试。看在该字符串是否存在一个电话号码模式或一个信用卡号码模式。这称为数据有效性验证。
替换文本。能够在文档中使用一个正則表達式来标识特定文字,然后能够所有将其删除。或者替换为别的文字。
依据模式匹配从字符串中提取一个子字符串。能够用来在文本或输入字段中查找特定文字。
比如,假设须要搜索整个 web 网站来删除某些过时的材料并替换某些HTML 格式化标记,则能够使用正則表達式对每一个文件进行測试,看在该文件里是否存在所要查找的材料或 HTML 格式化标记。用这种方法。就能够将受影响的文件范围缩小到包括要删除或更改的材料的那些文件。然后能够使用正則表達式来删除过时的材料,最后,能够再次使用正則表達式来查找并替换那些须要替换的标记。
那么。正則表達式语法的语法是怎样呢?
一个正則表達式就是由普通字符(比如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式。
该模式描写叙述在查找文字主体时待匹配的一个或多个字符串。正則表達式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
这里有一些可能会遇到的正則表達式演示样例:
/^\[ \t]*$/ "^\[ \t]*$" 匹配一个空白行。
/\d{2}-\d{5}/ "\d{2}-\d{5}" 验证一个ID 号码是否由一个2位数字,一个连字符以及一个5位数字组成。
/<(.*)>.*<\/\1>/ "<(.*)>.*<\/\1>" 匹配一个 HTML 标记。
二、字符描写叙述:
\
将下一个字符标记为一个特殊字符、或一个原义字符、或一个 后向引用、或一个八进制转义符。比如,‘n‘ 匹配字符 "n"。‘\n‘ 匹配一个换行符。序列 ‘\\‘ 匹配 "\" 而 "\(" 则匹配 "("。
^
匹配输入字符串的開始位置。
假设设置了 RegExp 对象的 Multiline 属性。^ 也匹配 ‘\n‘ 或 ‘\r‘ 之后的位置。
$
匹配输入字符串的结束位置。假设设置了RegExp 对象的 Multiline 属性。$ 也匹配 ‘\n‘ 或 ‘\r‘ 之前的位置。
*
匹配前面的子表达式零次或多次。 比如。zo* 能匹配 "z" 以及 "zoo"。 * 等价于{0,}。
+
匹配前面的子表达式一次或多次。比如,‘zo+‘ 能匹配 "zo" 以及 "zoo"。但不能匹配 "z"。+ 等价于 {1,}。
?
匹配前面的子表达式零次或一次。 比如,"do(es)?" 能够匹配 "do" 或 "does" 中的"do" 。 ? 等价于 {0,1}。
{n}
n 是一个非负整数。匹配确定的 n 次。 比如,‘o{2}‘ 不能匹配 "Bob" 中的 ‘o‘,可是能匹配 "food" 中的两个 o。
{n,}
n 是一个非负整数。至少匹配n 次。比如。‘o{2,}‘ 不能匹配 "Bob" 中的 ‘o‘。但能匹配 "foooood" 中的全部 o。‘o{1,}‘ 等价于 ‘o+‘。‘o{0,}‘ 则等价于 ‘o*‘。
{n,m}
m 和 n 均为非负整数。当中n <= m。最少匹配 n 次且最多匹配 m 次。
刘, "o{1,3}" 将匹配 "fooooood" 中的前三个 o。 ‘o{0,1}‘ 等价于 ‘o?‘。请注意在逗号和两个数之间不能有空格。
?
当该字符紧跟在不论什么一个其它限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。 比如,对于字符串 "oooo"。‘o+?‘ 将匹配单个 "o",而 ‘o+‘ 将匹配全部 ‘o‘。
.
匹配除 "\n" 之外的不论什么单个字符。要匹配包含 ‘\n‘ 在内的不论什么字符,请使用象 ‘[.\n]‘ 的模式。
(pattern)
匹配pattern 并获取这一匹配。所获取的匹配能够从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合。在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 ‘\(‘ 或 ‘\)‘。
(?
attern)
匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配。不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是非常实用。比如。 ‘industr(? :y|ies) 就是一个比 ‘industry|industries‘ 更简略的表达式。
(?=pattern)
正向预查,在不论什么匹配 pattern 的字符串開始处匹配查找字符串。 这是一个非获取匹配。也就是说,该匹配不须要获取供以后使用。比如。 ‘Windows (?=95|98|NT|2000)‘ 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符。也就是说,在一个匹配发生后,在最后一次匹配之后马上開始下一次匹配的搜索。而不是从包括预查的字符之后開始。
(?!pattern)
负向预查,在不论什么不匹配Negative lookahead matches the search string at any point where a string not matching pattern 的字符串開始处匹配查找字符串。这是一个非获取匹配。也就是说,该匹配不须要获取供以后使用。
比如‘Windows (?!95|98|NT|2000)‘ 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符。也就是说,在一个匹配发生后。在最后一次匹配之后马上開始下一次匹配的搜索,而不是从包括预查的字符之后開始
x|y
匹配 x 或 y。 比如,‘z|food‘ 能匹配 "z" 或 "food"。‘(z|f)ood‘ 则匹配 "zood" 或 "food"。
[xyz]
字符集合。匹配所包括的随意一个字符。比如, ‘[abc]‘ 能够匹配 "plain" 中的 ‘a‘。
[^xyz]
负值字符集合。匹配未包括的随意字符。比如, ‘[^abc]‘ 能够匹配 "plain" 中的‘p‘。
[a-z]
字符范围。
匹配指定范围内的随意字符。比如,‘[a-z]‘ 能够匹配 ‘a‘ 到 ‘z‘ 范围内的随意小写字母字符。
[^a-z]
负值字符范围。匹配不论什么不在指定范围内的随意字符。比如,‘[^a-z]‘ 能够匹配不论什么不在 ‘a‘ 到 ‘z‘ 范围内的随意字符。
\b
匹配一个单词边界。也就是指单词和空格间的位置。比如, ‘er\b‘ 能够匹配"never" 中的 ‘er‘,但不能匹配 "verb" 中的 ‘er‘。
\B
匹配非单词边界。‘er\B‘ 能匹配 "verb" 中的 ‘er‘。但不能匹配 "never" 中的 ‘er‘。
\cx
匹配由x指明的控制字符。 比如, \cM 匹配一个 Control-M 或回车符。 x 的值必须为 A-Z 或 a-z 之中的一个。否则。将 c 视为一个原义的 ‘c‘ 字符。
\d
匹配一个数字字符。等价于 [0-9]。
\D
匹配一个非数字字符。
等价于 [^0-9]。
\f
匹配一个换页符。
等价于 \x0c 和 \cL。
\n
匹配一个换行符。
等价于 \x0a 和 \cJ。
\r
匹配一个回车符。 等价于 \x0d 和 \cM。
\s
匹配不论什么空白字符。包含空格、制表符、换页符等等。 等价于 [ \f\n\r\t\v]。
\S
匹配不论什么非空白字符。等价于 [^ \f\n\r\t\v]。
\t
匹配一个制表符。等价于 \x09 和 \cI。
\v
匹配一个垂直制表符。等价于 \x0b 和 \cK。
\w
匹配包含下划线的不论什么单词字符。等价于‘[A-Za-z0-9_]‘。
\W
匹配不论什么非单词字符。 等价于 ‘[^A-Za-z0-9_]‘。
\oNUM
匹配NUM(当中n为一个小于256的八进制换码值)。比如:\o011 匹配制表符Tab
\xNUM
匹配 num,当中 NUM 为十六进制转义值。十六进制转义值必须为确定的两个数字长。比如。 ‘\x41‘ 匹配 "A"。 ‘\x041‘ 则等价于 ‘\x04‘ & "1"。正則表達式中能够使用 ASCII 编码。.
\NUM
匹配第 NUM 个,当中 NUM 是一个正整数。对所获取的匹配的引用。比如,‘(.)\1‘ 匹配两个连续的同样字符。‘(.)\1\1‘ 匹配三个连续的同样字符。
\n
标识一个八进制转义值或一个后向引用。假设 \n 之前至少 n 个获取的子表达式,则 n 为后向引用。否则,假设 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
\nm
标识一个八进制转义值或一个后向引用。假设 \nm 之前至少有is preceded by at least nm 个获取得子表达式,则 nm 为后向引用。假设 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的后向引用。假设前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7)。则 \nm 将匹配八进制转义值 nm。
\nml
假设 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
\un
匹配 n,当中 n 是一个用四个十六进制数字表示的 Unicode 字符。比如, \u00A9 匹配版权符号 (?)。
三、优先权顺序:
\ 转义符
(), (?:), (?=), [] 圆括号和方括号
*, +, ?
, {n}, {n,}, {n,m} 限定符
^, $, \anymetacharacter 位置和顺序
| “或”操作
四、样例:
提取网页中的链接地址
(http:\/\/\[^" ‘]+)
href *= *[‘"]*(\S+)["‘]* *
((http|https|ftp):(\/\/|\\\\)((\w)+[.]){1,}(net|com|cn|org|cc|tv|[0-9]{1,3})(((\/[\~]*|\\[\~]*)(\w)+)|[.](\w)+)*(((([? ](\w)+){1}[=]*))*((\w)+){1}([\&](\w)+[\=](\w)+)*)*) "精确匹配"
匹配一个 HTML 标记
<(.*)>.*<\/\1> "第一个匹配的是(.*),后面的\1就引用他,所以就成了对称的"
<[^>]*> "html标记"
从一段HTML代码中搜索当中的图片
/\/ig,"$1"
匹配连续字符
(.)\1 匹配两个连续同样的字符,(.)\1第一个匹配的是(.),后面的\1就引用他。连起来就成了两个连续的
(.)\1\1 匹配三个连续同样的字符,假设三个连续就用(.)\1\1,后面两个\1都引用前面的(.)
替换"[abc]asdfasldkfjlasd[/abc]中的[abc]"
(/(^\[abc\])|(\[\/abc\]$)/gi,‘‘)
年月日的格式
/^(\d{4})-(\d{1,2})-(\d{1,2})$/ "yyyy-mm-dd"
/^(\d{4})/(\d{1,2})/(\d{1,2})$/ "yyyy/mm/dd"
(^[[igit:]]{4}-([1-9]|1[0-2])-([1-9]|[1-2][0-9]|3[0-1]))
(/^\d{4}-([1-9]|1[0-2])-([0-2]{0,1}[0-9]|3[0-1])$/g)
Email邮件的格式
/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/
/^[\\w-]+(\\.[\\w-]+)*@[\\w-]+(\\.[\\w-]+)$/
/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/
/(\w)+[@]{1}(\w)+[.]{1,3}(\w)+/
身份证
/([0-9]){15}/ "15位数字"
/^\d+$/ "全数字"
/^(\d{14}|\d{17})(\d|x)$/ "15位或18位"
手机电话号码
/^13[13567890](\d{8})$/ "13开头。第三位为1,3,5,6,7,8,9,0,后跟8个数字"
/^(\d{3,4}-){0,1}(\d{7,8})$/ "匹配3-4位区号和横杆,7-8位市内电话号码"
/^\d{3,4}-\d{7,8}(-\d{3,4})?$/ "区号必填为3-4位的数字,区号之后用“-”与电话号码连接。电话号码为7-8位的数字。分机号码为3-4位的数字,非必填。但若填写则以“-”与电话号码相连接"
|