第一章:正则表达式入门
1:正则表达式是一些用来匹配和处理文本的字符串。正则表达式是用正则表达式语言创建的。
2:使用正则表达式解决匹配问题,同一个问题往往会有多种解决方案。
3:学习正则表达式的关键是实践,实践,在实践。
第二章:匹配单个字符
1:大多数正则表达式引擎的默认行为是只返回第一个匹配结果。但是多数正则表达式的实现都提供了一种能够把所有的匹配结果全部找出来的机制。
2:正则表达式是区分大小写的。
3:”.”,可以匹配除换行符以外的任一单个字符。比如正则表达式c.t,将匹配cat和cot等。
4:”\”为转义字符,比如需要匹配”.”本身,则必须在”.”的前面加上一个”\”,对其进行转义。同样的,如果需要匹配”\”本身,则表达式为”\\”。
第三章:匹配一组字符
1:元字符[和]用来定义一个字符集合,其含义是必须匹配该集合里的字符之一。
在正则表达式里,可以使用元字符[和]来定义一个字符集合,字符集合的匹配结果是能够与该集合中的任意一个成员相匹配的文本。比如正则表达式[ns],匹配字符n或者s;[0123456789]匹配任何一个数字。
2:字符区间:正则表达式中,可以用 “-“定义字符区间。比如[0-9],它与[0123456789]完全等价。
3:A-z,该区间很少用,因为它匹配ascii字符A到ascii字符z之间的所有字母,所以还包含着[和^等特殊字符。
4:”-“是一个特殊的元字符,作为元字符,它只能用在[和]之间。在字符集合以外的地方,”-“只是一个普通字符,只能与’-’本身相匹配。所以,在正则表达式里,”-“字符不需要被转义。
5:字符集合可以给出多个字符区间,比如[0-9A-Za-z]。
6:如果需要这样匹配:除了那个字符集合里的字符,其他字符都可以匹配。则需要在字符集合中使用元字符”^”。比如表达式[^0-9]匹配的是任何不是数字的字符。
7:”^”的效果是将作用于给定字符集合里的所有字符或字符区间,而不是仅限于紧跟在^字符后面的那一个字符或字符区间。所以,表达式[^0-9a-z],只能匹配” sdf123*&”中的”*”和”g”。
第四章:使用元字符
1:元字符是具有特殊含义的字符,比如 .和 \ 都是元字符。因元字符具有特殊的含义,所以这些字符就无法代表他们本身。比如说,你不能用[来匹配[本身。
2:每个元字符都可以通过给它加上” \ “进行转义。比如要相匹配[和],就必须对这两个字符进行转义。比如正则表达式"a\[0\]"匹配字符串a[0]。
3:转义字符 “\” 本身也是元字符,所以要想匹配” \ “本身,必须使用 “\\”。
4:元字符大致分为两种:一种是用来匹配文本的,比如“ .”。另一种是正则表达式的语法所要求的,比如[和]。
5:匹配空白字符的元字符如下表:
比如,对于下面的文本,如果需要匹配空行,则正则表达式就是“\n\n”(或者“\r\n\r\n”)。
a
b
c
具体匹配结果如下:
\r\n是windows所使用的文本行结束标签。unix和linux系统使用\n表示换行。
6:一些常用的字符集合可以用特殊元字符来代替。这些元字符匹配的是某一类别的字符,所以称为字符类。
7:\d:匹配任何一个数字字符,等价于[0-9]
\D:匹配任何一个非数字字符,等价于[^0-9]
比如a\[\d\]就可以匹配a[0],a[1]等。
8:\w:匹配任何一个字母、数字、或是下划线字符。等价于[a-zA-Z0-9_]
\W:是\w的反义,等价于[^a-zA-Z0-9_]
9:\s:匹配任何一个空白字符,包括空格,等价于[\f\n\r\t\v](v后面有一个空格)
\S:匹配任何一个非空白字符,等价于[^\f\n\r\t\v] (v后面有一个空格)
10:\xXX:比如\x0a,可以匹配ascii值为0x0a的字符,也就是\n。所以,\x61可以匹配ascii值为0x61的字符,也就是a。
\0XX:比如\011,这事8进制,可以匹配ascii值为9的字符,也就是\t。
11:POSIX字符类是许多(不是所有)正则表达式都支持的一种简写形式。下面是posix字符类的形式:
posix字符必须包括在[:和:]之间,所以[:xdigit:]等价于\d
第五章:重复匹配
1:要想匹配同一个字符、或字符集合的多次重复,只需要简单的给这个字符、或字符集合后面加上元字符”+”即可。元字符”+”匹配一个或多个字符。比如a+将匹配一个或多个连续出现的a。类似地,[0-9]+将匹配一个或多个连续的数字。
2:比如”\w+@\w+\.\w+”可以匹配:”ben@forta.com”、”support@forta.com”等
3:”+”还可以用来匹配一个或多个字符集合。比如正则表达式” [\w.]+@[\w.]+\.\w+”,可以匹配”ben@urgent.forta.com”,而使用”\w+@\w+\.\w+”则不行。
[\w.]+ 将匹配一个或者多个数字、字母、下划线和“ . ”符号,所以ben@urgent.forta.com可以匹配了。
注意,这里集合中的“ . ”字符没有转义,而且匹配了“ . ”(在字符集合中“ . ”将作为字面字符而不是元字符)。一般的,元字符如“ . ”和“ + ”等用于字符集合的时候是作为字面含义使用的,因此没有必要转义。尽管如此,对其进行转义也没错,所以,[\w.]和[\w\.]的功能是一样的。
4:“ * ”字符和“ + ”字符的使用方法是一样的,也是在字符或者字符集合后加上“ * ”字符从而匹配零个或者多个字符和字符集合。
5:”?”匹配一个字符、或字符集合的零次或一次出现。比如”https?://[\w./]+”将匹配以”http”或”https”开头的URL地址。
6:”[\r]?”和”\r?”的作用是一样的。”[]”一般是用来定义多个元素的集合,但是有些开发者即使是在单个字符上也会加上用来避免歧义。
7:为了对重复性匹配有更多的控制,正则表达式语言提供了一个用来设定重复次数的语法。重复次数要用”{”和”}”字符来给出,把数值写在他们之间。
注意,”{”和”}”也是元字符,在使用字面含义的时候需要转义。
8:如果需要为重复匹配次数设定一个精确的值,则把数字写在{}中即可,比如{3}意味着模式里的前一个字符、或字符集合必须在原始文本里连续重复出现3次才算是一个匹配;如果只重复了2次,则不算是匹配。
9:{}语法还可以用来为重复匹配次数设定一个区间,也就是设定一个最小值和一个最大值。比如{2,4}表示,最少重复2次,最多重复4次。比如\d{1,2}将匹配一个或两个数字字符。
10:{}语法的最后一种用法是给出一个最小的重复次数。比如{3,}表示至少重复3次。
11:之前的匹配都是贪婪匹配,有时候需要进行非贪婪匹配。比如对于下面的文本:
This offer is not available to customers livingin <B>AK</B> and <B>HI</B>.如果需要将<B>AK</B>和<B>HI</B>分别匹配出来,则使用表达式”<[Bb]>.*<\[Bb]>”,则会匹配:
” <B>AK</B> and<B>HI</B>”。
这是因为”*”和”+”都是贪婪型元字符,它们在进行匹配的时候,都是多多益善的。会尽可能的从一段文本的开头一直匹配到文本的末尾。
如果需要进行非贪婪匹配,则只需要在贪婪元字符后加上”?”即可。比如”*?”, ”+?”, ”{n,}?”等。所以,上面的例子,正确的表达式应该是” <B>.*?</B>”。
第六章:位置匹配
1:某些场合,只需要对某段文本的特定位置进行匹配,这就引出了位置匹配的概念。比如cat可以匹配cat,也能匹配scattered中的cat,如果只想把单词cat找出来,则需要使用边界限定符,也就是正则表达式中一些特殊的元字符,用来表明想让匹配操作发生在什么位置。
2:\b可以指定单词边界,它可以匹配一个单词的开始或结尾。比如\bcat\b,就可以只匹配文本中的单词cat,因为单词cat前后都有一个空格,空格是用来分隔单词的字符之一。而scattered中的cat不能与该模式匹配,因为他的前一个字符为s,后一个字符为t,这两个字符都不能与\b匹配。
3:其实,正则表达式不懂英语,也不知道什么事单词边界。\b仅仅匹配这样的位置,这个位置位于一个能够用来构成单词的字符(字母、数字、下划线,也就是\w)和一个不能用来构成单词的字符之间。所以,其实上面的表达式\bcat\b也可以匹配” %^&cat&^”中的cat,因为cat的前后字符,都不能用来构成单词。
4:\b匹配且只匹配一个位置,不匹配任何字符。用\bcat\b匹配到的字符串长度为3,而不是5.
5:如果想匹配一个非单词边界,也就是字母、数字、下划线之间,或者非字母数字下划线之间的位置,可以使用\B。比如表达式\B-\B可以匹配”color - coded”中的”-”,而不能匹配”nine-digit”中的”-”。
这是因为第一个字符串中,”-”前后都是空格,空格和”-”都不是单词组成字符(字母,数字,下划线),所以\B可以匹配该位置。
而第二个字符串中, “-”前后都是字母,也就是他前后的位置是字母和非字母数字下划线之间的位置,所以\B不能匹配。
所以,一定要从本质上理解\b和\B的含义。
6:字符串边界,用来定义字符串边界的元字符有两个:一个用来定义字符串开头的^,另一个是用来定义字符串结尾的$。
^是几个有几种用途的元字符中的一个。如果位于字符集合开始处的话,则表示求非;如果在字符集合外面,则将匹配字符串的开始位置。
7:下面的例子,为了匹配整个字符串:
“haha this a
a
acv”
需要使用模式:” ^\s*.+\s*.*\s*.*$”,^表示该字符串的开头,\s*表示0或多个空白符,.+表示一个或多个除换行符之外的任意字符,\s*表示空白符,.*匹配换行符之外的多个字符,最后的$表示字符串的结尾。
8:(? m)记号可以使正则表达式引擎将换行符当做一个字符串分隔符来对待。这就是分行匹配模式。但是该记号也有许多正则表达式引擎不支持,不赘述。
9:有些正则表达式实现还支持使用“ \A ”匹配字符串的开始,“ \Z ”匹配字符串的结束。如果支持的话,则这些元字符的功能和“ ^ ”、“ $ ”是一样的。
第七章:使用子表达式
1:在之前的例子中,用来表明重复次数的元字符,只作用于紧挨着它的前一个字符或元字符。比如模式 {2,},本意是希望把” ”这样的字符串连续两次或者更多次的重复找出来,但是,因为{2,}只作用于紧挨着它的前一个字符,也就是分号。所以,这个模式只能匹配像 ;;;;这样的文本,而无法匹配 。
2:可以使用子表达式,子表达式被当做一个独立元素来使用。子表达式必须用()括起来。”(“和”)”是元字符,必要时需要转义。
所以,上面的例子可以用:( ){2,}进行匹配。其中( )就是一个子表达式,紧跟在后面的{2,}将作用于这个子表达式。
3:子表达式不一定只对重复匹配有用,比如模式”19|20\d{2}”,本意是想匹配以19或20开头的年份,结果,他只能匹配19或20XX这样的数字。这是因为|操作符会把位于它左边和右边的两个部分作为一个整体来看待,所以,模式19|20\d{2}等价于(19)|(20\d{2})。所以,只能使用子表达式,正确模式为(19|20)\d{2}。
4:子表达式允许嵌套。比如为了匹配正确的IP地址,可以使用下面的模式:
((\b([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])\b)\.){3}(\b([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])\b)
它可以匹配XXX.XXX.XXX.XXX的字符串,其中XXX为0-255的数。这个模式乍看比较复杂,可以分拆来看:
[1-9]?\d匹配数字0-99(0-9和10-99),1\d{2}匹配数字100-199,2[0-4]\d匹配数字200-249,25[0-5]匹配数字250-255。
前后加上\b,表明是一个数字,否则,像260这样的数会匹配60成功。
所以,(\b([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])\b),可以匹配0-255的数字了,剩下的也就很好解释了,不再赘述。
第八章:回溯引用
1:子表达式的另一个重要用途就是回溯引用。
2:比如,对于这样的例子:需要把一段文本中,找出连续重复出现的单词。这个例子中,在搜索某个单词的第二次出现时,这个单词必须是已知的。回溯引用允许正则表达式引用前面的匹配结果。
这个例子,可以使用模式:[]+(\w+)[ ]+\1 在该模式中,[ ]+匹配单词前后的多个空格,\w+匹配第一个单词,注意他是在括号里的,这就是一个子表达式。这个子表达式把整个模式的一部分单独划分出来,以便以后引用。这个模式的最后一部分是\1;这就是一个回溯引用,他引用的正式前面那个子表达式:比如如果(\w+)匹配到单词of,那么\1也就匹配到of。
3:回溯引用指的是模式的后半部分引用在前半部分中定义的子表达式。\1代表模式中的第一个子表达式,\2代表第2个子表达式,以此类推。注意,不同的正则表达式在实现回溯引用的语法方面往往有着巨大的差异。
子表达式是通过他们的相对位置来引用的,\1对应第1个子表达式。回溯引用匹配通常从1开始计数。在许多实现里,第0个匹配(\0)可以用来代替整个正则表达式。
4:正则表达式还可以用来完成替换操作。替换操作需要用到两个正则表达式:一个用来给出搜索模式,另一个用来给出匹配文本的替换模式。回溯引用可以跨模式使用,在第一个模式里被匹配的子表达式可以用在第二个模式里。
在替换时,回溯引用需要使用$,而不是\。比如模式搜索模式为”[]+(\w+)[ ]+\1”,匹配模式为”$1444”,就可以把文本“thisis a a test”替换为”this is a444 test”。
同样的,搜索模式”(\d{3})(-)(\d{3})(-)(\d{4})”,替换模式为” ($1)$3$4$5”,可以把文本” 313-555-1234”替换为”(313) 555-1234”。
5:有些正则表达式允许使用下面的元字符对字母进行大小写转换:
\l和\u只能把下一个字符或子表达式转换为小写或大写。\L和\U将把它后面的所有字符转换为小写或大写,知道遇到\E为止。
比如搜索模式”(<H1>)(.*?)(</H1>)”,匹配模式”$1\U$2\E$3”,可以将文本”<H1>hello, world</H1>”,替换为”<H1>HELLO, WORLD</H1>”
第九章:前后查找
1:举例说明,比如需要找HTML中包含在<TITLE>和</TITLE>之间的文字,你可能很快想到应该使用的表达式是:”<TITLE>.*?</TITLE>”.
但是,如果只需要<TITLE>和</TITLE>之间的文字,而不希望包含<TITLE>和</TITLE>,上面这个模式就不合适了。
此时,就需要使用“前后查找”了。
2:“前后查找”是这样的一种模式,它包含的匹配本身并不返回,而是用于确定正确的匹配位置,它本身并不是匹配结果的一部分。常见的正则表达式实现都支持向前查找,但支持向后查找的就不多了。
3:向前查找指定了一个必须匹配,但是不在结果中返回的模式。向前查找实际就是一个子表达式,一个以?=开头的子表达式,需要匹配的文本跟在=后面。
4:比如,对于字符“http://www.forta.com/”,需要找出该URL地址中的协议类型,那么可以使用模式:”.+(?=:)”,结果就是”http”,这就是一个典型的向前查找模式。
因为协议名和主机名之间有”:”分隔。模式”.+”匹配任意文本,子表达式”(?=:)”匹配”:”,用”?=”向正则表达式引擎表明:只要找到”:”就行了,不需要把它包括在最终的匹配结果里。在上面的例子中,如果模式是”.+(:)”的话,则结果是”http:”。
任何一个子表达式都可以转换为一个向前表达式,只要给他加上一个?=前缀即可。
5:除了向前查找,许多正则表达式还支持向后查找,向后查找的操作符是”?<=”。可以记为”向......之后查找”。?<=和?=的具体使用方法大同小异,它必须在一个子表达式里,而且后面根要匹配的文本。
比如对于文本”ABC01:$23.45”,需要把其中的金额提取出来,而且不包含$,则可以使用这样的模式:”(?<=\$)[\d.]+”。
”(?<=\$)”匹配$,但是不消费它:最终的匹配结果中只有价格数字。
6:回到最初的例子,可以结合向前查找和向后查找,使用这样的模式:”(?<=<TITLE>).*?(?=</TITLE>)”,该模式中,”(?<=<TITLE>)”是向后查找,”(?=</TITLE>)”是向前查找。
十:嵌入条件
1:例子:(123)456-7890和123-456-7890是可接受的北美电话号码格式,怎么能用正则表达式进行匹配呢?如果电话号码里有一个左括号”(“,则模式中必须有”)”匹配,否则,就必须匹配”-”。这种情况,必须使用条件处理。注意:并不是所有的正则表达式实现都支持条件处理。
2:嵌入条件不外乎两种情况:根据一个回溯引用来进行条件处理;根据一个前后查找来进行条件处理。
3:定义条件的语法是”(?(backreference)true-regex|false-regex)”,所以,上面例子的正确模式是:”(\()?\d{3}(?(1)\)|-)\d{3}-\d{4}”,其中”(\()?”匹配一个可选的左括号”(”,注意,左括号必须进行转义。这里使用了子表达式。
“\d{3}”匹配3位数字,”(?(1)\)|-)”,就是一个回溯引用条件,它将根据条件是否满足,选择是匹配”)”,还是”-”。注意,右括号同样需要转义。这里条件是?(1),指:如果第一个回溯引用存在,也就是找到了一个左括号,则需要有”)”进行匹配,否则,”-”匹配。
回溯条件中的false可以省略,也就是这样的形式:”(?(backreference)true-regex)”。
4:除了回溯查找条件,还有前后查找条件。前后查找条件只在一个向前查找或向后查找操作区的成功的情况下,才允许一个表达式被使用。
比如,需要匹配”11111”或”11111-4444”这样的字符串。必须使用这样的模式:”\d{5}(?(?=-)-\d{4})”,在该模式中,”\d{5}”匹配前5位数字。接下来”(?(?=-)-\d{4})”就是一个向前查找条件。这个条件使用了”?=-”来匹配一个连字符,但是并不消费它。如果这个条件满足,则”-\d{4}”将匹配后面的连字符和4为数字。
5:在实际工作中,签入了前后查找条件的模式相当少见,因为往往可以用更简单的办法达到同样的目的。
---------------------------------------------