整理笔记——正则表达式总结

来源:互联网 发布:数据封装过程 编辑:程序博客网 时间:2024/05/29 21:37

本文参考书籍:《Mastering Regular Expressions,3rd Edition》Jeffrey E.F Friedl 著,余晟 译,中文名《精通正则表达式》

关于正则表达式

正则表达式术语

正则(regex)

正则表达式regular expression的简写,全称念起来麻烦,合成了这个regex简称。正则表达式是强大、便捷、高效的文本处理工具。实际上,我们最常见的应用是在这两方面:1.验证,2.搜索/替换。用于验证时,通常需要在前后分别加上^和$,以匹配整个待验证字符串;搜索/替换时是否加上此限定则根据搜索的要求而定,此外,也有可能要在前后加上\b而不是^和$。常见正则表达式参照下文所列表格。

匹配(matching)

就是指正则表达式匹配一个字符串的时候,能在字符串中找到匹配文本。比如s能匹配sb中的s,sb能匹配sb中的sb。

元字符(metacharacter)

元字符其实是指有特殊功能的字符,下文表格可清晰看到各种元字符的作用。需要注意的是,元字符并不是在任何情况下都表示特殊功能,比如连字符-只有在字符组内部即[]内部的时候才为发挥元字符的作用,表示范围,如[a-zA-Z]表示所有的英文字母,但是到了字符组外部即[]外部,就只是普通字符了,如a-z表示第一个字符是a第二是-第三个是z。再比如.表示任意一个字符,在字符组内部即[]内为普通字符,在外部的时候才表示为元字符,如a.[b.]可以匹配abb或ac.或a..     例子很显然,不做解释了。另外脱字符^在字符组外部是一个意思,表示行起始位置,在字符组内部紧接着[后写的时候,表示非的意思,举例:^[^a-zA-Z^1]$匹配的是有且仅有一个非字母的字符的一行字符串,第一个脱字符表示起始位置,第二个表示非,后面是范围即所有英文字母,其他情况下又表示别的意思(《精通正则表达式》里说有别的意思,个人常见的是前两种,没再研究,若有补充请在评论里相告于我)。

流派(flavor)

flavor这词其实有风味的意思,不同地域不同风味嘛,所以不难理解,不同的工具使用不同的正则表达式完成不同的任务,表面上是元字符的规定不一样,实际上远不止这些,但可暂且如此理解。比如java和其他语言用正则时,元字符的规定不一样,写的就不一样,流派就是解释这个现象的词。

语法

元字符

字符

说明

\

将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,“n”匹配字符“n”。“\n”匹配换行符。序列“\\”匹配“\”,“\(”匹配“(”。

^

匹配输入字符串开始的位置。多行模式下,^ 还会与“\n”或“\r”之后的位置匹配。

$

匹配输入字符串结尾的位置。多行模式下,$ 还会与“\n”或“\r”之前的位置匹配。

*

>=0次匹配前面的字符或子表达式。例如,zo* 匹配“z”和“zoo”。* 等效于 {0,}。

+

>=1次匹配前面的字符或子表达式。例如,“zo+”与“zo”和“zoo”匹配,但与“z”不匹配。+ 等效于 {1,}。

?

0或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}

Mn 是非负整数,其中 n <= m。匹配至少 n 次,至多m 次。例如,“o{1,3}”匹配“fooooood”中的头三个 o。'o{0,1}' 等效于 'o?'。注意:您不能将空格插入逗号和数字之间。

?

当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是“非贪心的”。“非贪心的”模式匹配搜索到的、尽可能短的字符串,而默认的“贪心的”模式匹配搜索到的、尽可能长的字符串。例如,在字符串“oooo”中,“o+?”只匹配单个“o”,而“o+”匹配所有“o”。

.

匹配除“\n”之外的任何单个字符。若要匹配包括“\n”在内的任意字符,请使用诸如“[\s\S]”之类的模式。

(pattern)

匹配 pattern 并捕获该匹配的子表达式。可以使用 $0…$9 属性从结果“匹配”集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用“\(”或者“\)”。

(?:pattern)

匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用“or”字符 (|) 组合模式部件的情况很有用。例如,'industr(?:y|ies) 是比 'industry|industries' 更经济的表达式。

(?=pattern)

执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95|98|NT|2000)' 匹配“Windows 2000”中的“Windows”,但不匹配“Windows 3.1”中的“Windows”。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

(?!pattern)

执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95|98|NT|2000)' 匹配“Windows 3.1”中的 “Windows”,但不匹配“Windows 2000”中的“Windows”。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

x|y

匹配 xy。例如,'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_]”等效。

\xn

匹配 n,此处的 n 是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,“\x41”匹配“A”。“\x041”与“\x04”&“1”等效。允许在正则表达式中使用 ASCII 代码。

\num

匹配 num,此处的 num 是一个正整数。到捕获匹配的反向引用。例如,“(.)\1”匹配两个连续的相同字符。

\n

标识一个八进制转义码或反向引用。如果 \n 前面至少有 n 个捕获子表达式,那么 n 是反向引用。否则,如果n 是八进制数 (0-7),那么n 是八进制转义码。

\nm

标识一个八进制转义码或反向引用。如果 \nm 前面至少有 nm 个捕获子表达式,那么 nm 是反向引用。如果 \nm 前面至少有n 个捕获,则n 是反向引用,后面跟有字符m。如果两种前面的情况都不存在,则 \nm 匹配八进制值nm,其中nm 是八进制数字 (0-7)。

\nml

n 是八进制数 (0-3),ml 是八进制数 (0-7) 时,匹配八进制转义码 nml

\un

匹配 n,其中 n 是以四位十六进制数表示的 Unicode 字符。例如,\u00A9 匹配版权符号 (©)。

重复

常用的限定符

代码/语法

说明

*

重复零次或更多次

+

重复一次或更多次

?

重复零次或一次

{n}

重复n次

{n,}

重复n次或更多次

{n,m}

重复n到m次

 

分组

我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作。

(\d{1,3}\.){3}\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\d{1,3}匹配1到3位的数字(\d{1,3}\.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。

IP地址中每个数字都不能大于255. 但是 01.02.03.04 这样前面带有0的数字, 是不是正确的IP地址呢? 答案是: 是的, IP 地址里的数字可以包含有前导 0 (leading zeroes).

不幸的是,它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话,或许能简单地解决这个问题,但是正则表达式中并不提供关于数学的任何功能,所以只能使用冗长的分组,选择,字符类来描述一个正确的IP地址:((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)

理解这个表达式的关键是理解2[0-4]\d|25[0-5]|[01]?\d\d?,这里我就不细说了,你自己应该能分析得出来它的意义。

反向引用

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

关于分组:

  • 分组0对应整个正则表达式

  • 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号

  • 你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.

反向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。示例:

\b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)。

你也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+)(或者把尖括号换成'也行:(?'Word'\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,你可以使用\k<Word>,所以上一个例子也可以写成这样:\b(?<Word>\w+)\b\s+\k<Word>\b需要注意的是,java不支持命名捕捉。

使用小括号的时候,还有很多特定用途的语法。下面列出了最常用的一些:

常用分组语法

分类

代码/语法

说明

捕获

(exp)

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

(?<name>exp)

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

(?:exp)

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

零宽断言

(?=exp)

匹配exp前面的位置

(?<=exp)

匹配exp后面的位置

(?!exp)

匹配后面跟的不是exp的位置

(?<!exp)

匹配前面不是exp的位置

注释

(?#comment)

这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

我们已经讨论了前两种语法。第三个(?:exp)不会改变正则表达式的处理方式,只是这样的组匹配的内容不会像前两种那样被捕获到某个组里面,也不会拥有组号。(?:exp)不能用于提取文本,而只能用来规定多选结构(|)和量词(+ * ? {})的作用对象。非捕获型括号的作用:1 让复杂的表达式变得清晰 2 提高效率,有的分组不需要捕获只是起到1的作用。

锚点及零宽断言(Anchors&"Zero-Width Assertions")

锚点,零宽断言(也就是预搜索)并不会匹配实际的文本,而是寻找文本中的位置。另外,单词分解符\b也是如此。接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言/环视

顺序环视(lookahead,也叫零宽断言)

断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。

(?=exp)肯定顺序环视positive lookahead(也叫零宽度正预测先行断言),从左向右查看文本,看当前位置的右边能否匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配singdanc

(?<=exp)肯定逆序环视(也叫零宽度正回顾后发断言),从右向左查看文本,看当前位置的左边能匹配表达式exp比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading

假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})+\b,用它对1234567890进行查找时结果是234567890

下面这个例子同时使用了这两种断言:(?<=\s)\d+(?=\s)匹配以空白符间隔的数字(再次强调,不包括这些空白符)

逆序环视(lookbehind也叫负向零宽断言)

如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样:

\b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b

否定顺序环视(也叫做零宽度负预测先行断言)(?!exp)右侧文本不能匹配表达式exp例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词

同理,我们可以用(?<!exp),否定逆序环视(也叫做零宽度负回顾后发断言)左侧文本不能匹配表达式exp(?<![a-z])\d{7}匹配前面不是小写字母的七位数字

一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容(?<=<(\w+)>)指定了这样的前缀被尖括号括起来的单词(比如可能是<b>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。整个表达式匹配的是<b>和</b>之间的内容(再次提醒,不包括前缀和后缀本身)。

这个例子稍微注意下,本人做java的,而java里头使用Lookbehind即逆序环视时是不支持+,*的,只支持有限次数的重复字符匹配,但lookahead顺序环视支持。

匹配模式

许多正则引擎都支持多种不同的模式,它们规定了正则表达式应该如何解释和应用。下面列出几种模式:

忽略大小写的匹配模式

简言之,匹配的时候大小写是一样的,a和A是一回事。更多的介绍请参考《精通正则表达式》的第三章。

点号通配模式(dot-match-all match mode也叫单行模式)也叫单行模式

.能够匹配所有,通常情况下.只能匹配任意的一个字符,不能匹配换行符,单行模式下,.点号能匹配换行符,字符串内部的换行符也能匹配。

如.a能匹配下面的字符串,第一行是"a,"由.点号匹配,a自然由a匹配,而第二行的a实际上是换行符和a,换行符由.点号匹配,a由a匹配

"a

a"

增强的行锚点模式(Enhanced  line-anchor match mode 也叫多行文本模式)

该模式影响的是行锚点^ $的匹配。通常情况下^不能匹配字符串内部的换行符,该模式下能匹配,$类似,也能匹配字符串内部的换行符了。

另外,解释一下,多行模式和单行模式没有一毛钱关系。单行模式修改的是.点好的匹配规则,多行模式修改的^ $的匹配规则。单行模式命名不当,让人错以为与多行模式有关系。

贪婪与懒惰

当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。

有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:

a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)ab(第四到第五个字符)

为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。

懒惰限定符

代码/语法

说明

*?

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

+?

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

??

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

{n,m}?

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

{n,}?

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

常用的正则表达式

说明正则表达式网址(URL)[a-zA-z]+://[^\s]*IP地址(IP Address)((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)电子邮件(Email)\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*QQ号码[1-9]\d{4,}HTML标记(包含内容或自闭合)<(.*)(.*)>.*<\/\1>|<(.*) \/>密码(由数字/大写字母/小写字母/标点符号组成,四种都必有,8位以上)(?=^.{8,}$)(?=.*\d)(?=.*\W+)(?=.*[A-Z])(?=.*[a-z])(?!.*\n).*$日期(年-月-日)(\d{4}|\d{2})-((1[0-2])|(0?[1-9]))-(([12][0-9])|(3[01])|(0?[1-9]))日期(月/日/年)((1[0-2])|(0?[1-9]))/(([12][0-9])|(3[01])|(0?[1-9]))/(\d{4}|\d{2})时间(小时:分钟, 24小时制)((1|0?)[0-9]|2[0-3]):([0-5][0-9])汉字(字符)[\u4e00-\u9fa5]中文及全角标点符号(字符)[\u3000-\u301e\ufe10-\ufe19\ufe30-\ufe44\ufe50-\ufe6b\uff01-\uffee]中国大陆固定电话号码(\d{4}-|\d{3}-)?(\d{8}|\d{7})中国大陆手机号码1\d{10}中国大陆邮政编码[1-9]\d{5}中国大陆身份证号(15位或18位)\d{15}(\d\d[0-9xX])?非负整数(正整数或零)\d+正整数[0-9]*[1-9][0-9]*负整数-[0-9]*[1-9][0-9]*整数-?\d+小数(-?\d+)(\.\d+)?不包含abc的单词\b((?!abc)\w)+\b

Java操作正则表达式

java.util.regex使用传统型的NFA引擎,逆序环视的字表达式exp中不能使用+ *,因为java在使用逆序环视时不支持无限次数的重复,只支持有限次数重复,则?可用。这点在逆序环视的介绍中已经标红强调了。

java.util.regex 是一个用正则表达式所订制的模式来对字符串进行匹配工作的类库包。

它包括两个类: PatternMatcher

Pattern一个 Pattern 是一个正则表达式经编译后的表现模式。Matcher一个 Matcher 对象是一个状态机器,它依据 Pattern 对象做为匹配模式对字符串展开匹配检查。

pattern对象是调用其compile()方法,如果正则只用一次,编译时机无关紧要,但是使用多次,预编译正则对象就可重复使用,节省内存,提高效率。Pattern 对象将会使用matcher()方法来生成一个 Matcher 实例,接着便可以使用该 Matcher 实例以编译的正则表达式为基础对目标字符串进行匹配工作,多个 Matcher 是可以共用一个 Pattern 对象的。

package com.company.regex;import java.util.regex.Pattern;public class TestRegex {      public static void main(String[] args) throws Exception {         // 生成一个 Pattern, 同时编译一个正则表达式        Pattern p = Pattern.compile("[/]+");         // 用 Pattern 的 split() 方法把字符串按"/"分割        String[] result = p.split( "Kevin has seen 《 LEON 》 seveal times,because it is a good film." +"/ 凯文已经看过《这个杀手不太冷》几次了,因为它是一部" +"好电影。/ 名词 : 凯文。");         for (int i=0; i<result.length; i++)             System.out.println(result[i]);       } }


 

输出结果为:

 Kevin has seen 《 LEON 》 seveal times,because it is a good film. 凯文已经看过《这个杀手不太冷》几次了,因为它是一部好电影。名词 : 凯文。

很明显,该程序将字符串按"/"进行了分段,我们以下再使用 split(CharSequence input, int limit) 方法来指定分段的段数,程序改动为:
String[] result = p.split("Kevin has seen 《 LEON 》 seveal times,because it is a good film./ 凯文已经看过《这个杀手不太冷》几次了,因为它是一部好电影。/ 名词 : 凯文。",2);

这里面的参数"2"表明将目标语句分为两段。

输出结果则为:

 Kevin has seen 《 LEON 》 seveal times,because it is a good film. 凯文已经看过《这个杀手不太冷》几次了,因为它是一部好电影。/ 名词 : 凯文。

 

一个 Matcher 实例是被用来对目标字符串进行基于既有模式(也就是一个给定的 Pattern 所编译的正则表达式)进行匹配查找的,所有往 Matcher 的输入都是通过 CharSequence 接口提供的,这样做的目的在于可以支持对从多元化的数据源所提供的数据进行匹配工作。

我们分别来看看各方法的使用:

★ matches()/lookingAt ()/find():
一个 Matcher 对象是由一个 Pattern 对象调用其 matcher() 方法而生成的,一旦该 Matcher 对象生成 , 它就可以进行三种不同的匹配查找操作:

  1. matches() 方法尝试对整个目标字符展开匹配检测,也就是只有整个目标字符串完全匹配时才返回真值。
  2. lookingAt () 方法将检测目标字符串是否以匹配的子串起始。
  3. find() 方法尝试在目标字符串里查找下一个匹配子串。

以上三个方法都将返回一个布尔值来表明成功与否。

★ replaceAll ()/appendReplacement()/appendTail():
Matcher 类同时提供了四个将匹配子串替换成指定字符串的方法:

  1. replaceAll()
  2. replaceFirst()
  3. appendReplacement()
  4. appendTail()

replaceAll() 与 replaceFirst() 的用法都比较简单,请看上面方法的解释。我们主要重点了解一下 appendReplacement() 和 appendTail() 方法。

appendReplacement(StringBuffer sb, String replacement) 将当前匹配子串替换为指定字符串,并且将替换后的子串以及其之前到上次匹配子串之后的字符串段添加到一个 StringBuffer 对象里,而 appendTail(StringBuffer sb) 方法则将最后一次匹配工作后剩余的字符串添加到一个 StringBuffer 对象里。

例如,有字符串 fatcatfatcatfat, 假设既有正则表达式模式为"cat",第一次匹配后调用 appendReplacement(sb,"dog"), 那么这时 StringBuffer sb 的内容为 fatdog,也就是 fatcat 中的 cat 被替换为 dog 并且与匹配子串前的内容加到 sb 里,而第二次匹配后调用 appendReplacement(sb,"dog"),那么 sb 的内容就变为 fatdogfatdog,如果最后再调用一次 appendTail(sb), 那么 sb 最终的内容将是 fatdogfatdogfat。示例如下

 

package com.company.regex;import java.util.regex.Matcher;import java.util.regex.Pattern;public class TestRegex {      public static void main(String[] args) throws Exception {       // 生成 Pattern 对象并且编译一个简单的正则表达式"Kelvin"          Pattern p = Pattern.compile("Kelvin");           // 用 Pattern 类的 matcher() 方法生成一个 Matcher 对象          Matcher m = p.matcher("Kelvin Li and Kelvin Chan are both working in " +  "Kelvin Chen's KelvinSoftShop company");           StringBuffer sb = new StringBuffer();           int i=0;           // 使用循环将句子里所有的 kelvin 找出并替换再将内容加到 sb 里          while(m.find()) {               i++;               m.appendReplacement(sb, "Bruce");               System.out.println("第"+i+"次匹配后 sb 的内容是:"+sb);           }           // 最后调用 appendTail() 方法将最后一次匹配后的剩余字符串加到 sb 里;          m.appendTail(sb);           System.out.println("调用 m.appendTail(sb) 后 sb 的最终内容是 :"+   sb.toString());      } }


 

 

group()/group(int group)/groupCount():用法示例如下,group(0)等效group() 表示整个待匹配的字符串

import java.util.regex.*;  public class GroupTest{     public static void main(String[] args)                          throws Exception {         Pattern p = Pattern.compile("(ca)(t)");                Matcher m = p.matcher("one cat,two cats in the yard");         StringBuffer sb = new StringBuffer();         boolean result = m.find();         System.out.println("该次查找获得匹配组的数量为:"+m.groupCount());         for(int i=1;i<=m.groupCount();i++){          System.out.println("第"+i+"组的子串内容为: "+m.group(i));         }     }  } 


 

输出为:
该次查找获得匹配组的数量为:2
第 1 组的子串内容为:ca
第 2 组的子串内容为:t

 


 

写一个最简单的网络爬虫,api请参考java api文档

package com.company.regex;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;import java.util.regex.Matcher;import java.util.regex.Pattern;public class Demo1 {public static void main(String[] args) { try {URL url=new URL("http://www.baidu.com");URLConnection urlConnection = url.openConnection();urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 5.1; rv:24.0) Gecko/20100101 Firefox/24.0");InputStream is = urlConnection.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(is, "utf-8"));StringBuffer buffer = new StringBuffer("");String line =null;while ((line = br.readLine()) != null) {buffer.append(line);}is.close();br.close();Pattern pattern=Pattern.compile("(<head>.*</head>)");Matcher matcher=pattern.matcher(buffer.toString());while(matcher.find()){System.out.println(matcher.group());}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} }}

Javascript操作正则表达式

TODO 补上javascript的正则

0 0
原创粉丝点击