正则表达式深入了解
1.元字符(metacharacters)
2.模式修饰符(mode modifier)
在一段正则表达式的开始位置可以设定模式修饰符,常用的有如下:
(?i)-->不区分大小写 eg:(?i)[A-F0-9] 匹配所有十六进制的数
(?s)-->点号匹配换行符 eg:(?s)'.' 点号可以匹配换行符(默认情况下.号匹配除了换行符之外的所有单个字符)
(?m)-->^&匹配换行处 eg:(?m)^begin 匹配每一行的开始处,(?m)end$匹配每一行的结尾处
(?x)-->开启松散排列
3.正则的使用
3.1匹配字面文本及预定义字符
如果想匹配helloxiaoxiao中的xiaoxiao这段字符正则表达式可以这样写 xiaoxiao简单地把需要匹配的字符当做正则即可.
预定义字符:
| 构造 | 匹配 |
| . | 任何字符(与行结束符可能匹配也可能不匹配) |
| \d | 数字:[0-9] |
| \D | 非数字: [^0-9] |
| \s | 空白字符:[ \t\n\x0B\f\r] |
| \S | 非空白字符:[^\s] |
| \w | 单词字符:[a-zA-Z_0-9] |
| \W | 非单词字符:[^\w] |
3.2匹配不可打印字符
| 构造 | 匹配 |
| \t | 制表符 ('\u0009') |
| \n | 新行(换行)符 ('\u000A') |
| \r | 回车符 ('\u000D') |
| \f | 换页符 ('\u000C') |
| \a | 报警 (bell) 符 ('\u0007') |
| \e | 转义符 ('\u001B') |
| \cx | 对应于 x 的控制符 |
需要匹配以上的不可打印字符时可以使用转义序列与十六进制表示法均可.
3.3字符类(character class)
一对中括号称为字符类([]).在字符类中使用元字符只有三个拥有特殊的功能: \^- ,其他元字符都是字面量.\表示转义,^表示取否,-表示区间.字符类中的正则表达式是彼此独立的.
| 构造 | 匹配 |
| [abc] | a、b 或 c(简单类) |
| [^abc] | 任何字符,除了 a、b 或 c(否定) |
| [a-zA-Z] | a 到 z 或 A 到 Z,两头的字母包括在内(范围) |
| [a-d[m-p]] | a 到 d 或 m 到 p:[a-dm-p](并集) |
| [a-z&&[def]] | d、e 或 f(交集) |
| [a-z&&[^bc]] | a 到 z,除了 b 和 c:[ad-z](减去) |
| [a-z&&[^m-p]] | a 到 z,而非 m 到 p:[a-lq-z](减去) |
3.4匹配任意字符
3.5匹配文本行起始或结尾
^(\A)表示匹配文本行的起始,如:^xiaoxiao(\Axiaoxiao)只会匹配由xiaoxiao为起始的行.
&(\Z)表示匹配文本的结尾,如:xiaoxiao$(xiaoxiao\Z)只会匹配有xiaoxiao为结尾的行.
3.6 匹配整个单词及边界匹配器
\b表示匹配单词的边界.将\b放到正则表达式之前表示单词开头必须匹配,\b放在后面表示单词结尾必须匹配,\b放在前后表示只匹配对应的单词.\B表示取否,如\bcat\b可以匹配cat单词,但是\Bcat\B不能匹配cat单词,他却可以匹配staccato.
| 构造 | 匹配 |
| ^ | 行的开头 |
| $ | 行的结尾 |
| \b | 单词边界 |
| \B | 非单词边界 |
| \A | 输入的开头 |
| \G | 上一个匹配的结尾 |
| \Z | 输入的结尾,仅用于最后的结束符(如果有的话) |
| \z | 输入的结尾 |
3.7Unicode代码点/属性/区块
如果我们想要匹配一个特殊字符我们可以使用unicode表示
如:\u2122
如果我们想要匹配一类字符可以使用unicode类别
如:\p{Sc}表示匹配所有的货币符号
\p{Ps}表示匹配任意类型的左括号等.
unicode区块就是把U+0000~U+FFFF总共划分为105个区块.
使用方式为\p{IsBlockName}
3.8捕获分组和捕获匹配的子串
使用()对正则表达式进行捕获分组,进行分组后我们可以对捕获分组的反向引用.
如:\b\d\d(\d\d)-\1-\1\b
这样我们就可以匹配到类似2016-16-16/2017-17-17这样的字符串(不要考虑时间是否合法,只是举个例子).
如果是这样呢:\b\((d\d)(\d\d))-\1-\2-\3\b
分组按照从左向右左括号的出现顺序进行编号,这样的话我们就可以匹配到类似2016-2016-20-16这样的字符串.
3.9非捕获分组
捕获分组会每次把分组内匹配到的字符存储到内存中,这样我们在对他进行反向引用的时候才能拿到该分组匹配的信息.但是我们有时候只想对正则进行分组但是不会出现对他的反向引用这是候我们该怎么办呢?当然我们同样可以使用捕获分组来完成,但是这样的性能不能保障,所以就出现了非捕获分组.
(?:\d\d\d\d)-(?:\d\d)-(?:\d\d)
我们不能对非捕获分组产生反向引用,应为他不会把分组中的表达式匹配到的数据保存起来.3.10重复多次
(?:AB){2,3}表示AB可以出现2次或3次.
(?:AB){2}表示AB必须出现2次
(?:AB){2,}表示AB可以出现2次或多次
3.11惰性量词
需求:匹配一对HTML标记<p>和</p>,以及二者之间所有的文本.在标记这件的文本也可以包含其他的标记.
如果这个需求让你来解决,你第一会想到的是什么?是<p>.*</p>对吧?我们大多数都会这样想,但是这不是标准答案,准确答案应该是<p>.*?</p>.在这里面的?把*标记为一个惰性的量词.试想,如果不是这样的话,那么*号回贪心的匹配到<p>标签之后的所有字符,直到文本的结束处.此时才会匹配</p>标签,这样的话的得到的结果就是第一个<p>标签到最后一个</p>标签,如果我的文本数据里有很多段落标签,这样匹配的结果就是不正确的.
所以我们需要把*号标记成惰性量词,这样在匹配的时候匹配引擎会在匹配到<p>标签后,接下来的工作是先匹配<,如果匹配失败,才会匹配*,*匹配完成后再匹配<,如此往下,直到</p>都匹配成功后,一段完整的匹配就结束了.所以我们在适当的时候需要使用惰性量词来书写我们的正则表达式.
常见的惰性量词有:*?,+?,??,{1,4}?,把他们所对应的普通形式称为贪心量词
3.12占有量词和原子分组
正则匹配的过程中当一部分正则匹配失败后正则引擎会进行回溯匹配(就是从右向左的匹配),这样才能较准确的完成所有匹配.但是这样的回溯匹配又是由不是必须的,而且频繁的回溯会造成性能的损失.为了解决这个问题,正则中引入了占有量词和原子分组.
占有量词:*+,++,?+
占有量词永远不会回退,也不会记录任何可能的回溯位置.
原子分组:(?>regex)
其中的regex是任意的正则表达式.原子分组也不会记录任何回溯位置.
使用占有量词和原子分组的目的主要是对正则的性能优化,但是请不要乱用,否则会让你的正则匹配不到你所需的数据.
3.13环视(lockaround)
需求:匹配一段HTML文本中的<em></em>标签中的文本,但是不包含<em>标签.
解决方案:(?<=<em>)\w+(?=</em>)
环视可以放弃环视内所匹配的文本,环视内的正则表达式不会任何东西.
(?>=:逆序环视(lookbehind),逆序环视会检查在逆序环视中的文本是否在正则表达式匹配处的左边,如果逆序环视内的文本匹配,并且正则表达式匹配此次匹配才会成功.但是匹配的文本不包含逆序环视所匹配的文本.
(?=:顺序环视(lookahead),正则引擎会记住已经匹配的结果,并把它同环视关联起来.如果同顺序环视总的正则匹配成功,则完成一次匹配.
以上的环视都是肯定型环视,与之相反的有否定型环视.
(?<!text:否定型逆序环视,效果同肯定型环视相反,如果不匹配环视中的文本,则匹配成功.
(?!regex:否定型顺序环视,效果同肯定型环视相反,如果不匹配环视中的文本,则匹配成功.
因为环视中的文本在匹配后不会保存回溯位置,所以环视也拥有原子分组的特性.
3.14向正则中添加注释
当我们的正则写的过于复杂的时候,我么可以选择性的向里面添加注释.添加注释需要打开宽松排列,在正则表达式前添加模式修饰符(?x).#号标志着注释的开始,结束为行的结束.
如: \d{4} #年
-\d{2} #月
-\d{2} #日
3.15把匹配到的文本添加到替换文本中
我们需要在替代的文本中获取正则匹配的数据使用$符号获取被匹配到的文本.
如:(\b\w{4}\b)我们需要把这个正则匹配到的数据替换成原本数据拼接上HELLO,比如我们匹配到的文本为xiao,我们需要替换成xiaoHELLO,我们就可以在我们要替换的字符串中这样书写:
$1HELLO
其中的$是获取正则的组匹配数据,$1获取第一组.$0表示获取正则匹配到的一次完成文本.
以上正则使用方式符合所有Java正则流派,其他正则流派如Javascript,Ruby,PHP等流派中可能不适用.但是也只是一些细微的差别.只要掌握了此种正则流派,其他的流派稍微修改即可使用.

浙公网安备 33010602011771号