概述:
正则表达式,又称规则表达式。是由普通字符(例如字符 a 到 z)以及特殊字符(称为元字符)组成的文字模式。
正则表达式作为一个模板,将某个字符模式与所搜索的字符串进行匹配。
它最初是科学家对人类神经系统工作原理的早期研究(一种用数学方式来描述神经网络的新方法)。
Unix的主要发明人 Ken Thompson 将它运用到了计算搜索算法、编辑器qed、编辑器ed(太古老了),从此被广泛应用
那么它有啥用?
它能帮你在一大堆杂乱无章的内容中,找到符合特定模式的内容
比如,爬取其它网站的内容并提取有用信息;内容关键字审核;特定文本内容整体替换
这一节将介绍一下正则表达式的一些特殊字符和简单用法
在讲这些字符/符号的时候,我会用 php 来举一些例子,基本上所有语言的正则表达式都大同小异,若你使用的是其它语言,也可直接把表达式拿去使用试试
一、特殊字符(元字符)
特殊字符即在正则表达式中有特定含义的内定字符。就好比 java,php 等语言中的关键字
1、$
匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'
例子1
$s = 'abcd efd'; preg_match('#[a-z]+?d$#s',$s,$m);//# # 之间的内容为正则表达式,s为修饰符,# 为php中的边界符号,还有 / 也是边界符 print_r($m);//$m 就是我们匹配到的信息 /* Array ( [0] => efd ) */
匹配以 d结尾(d$) 的一串字符,前面可以是任意a到z 的小写字母([a-z]+?), 其中 s 为修饰符(后面会讲),就是多行模式(Multiline),
所以匹配到了第二行的 efd(因为[a-z]+?不匹配换行),若要匹配整个换行的字符串,则:#([a-z]|\r\n)+?d$#s
2、( )
标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用
例子2
$s = 'abc123def456'; preg_match_all('#(\d+)#',$s,$m); print_r($m[1]); /* Array ( [0] => 123 [1] => 456 ) */
匹配并捕获字符串中的所有数字,$m[1] 就是捕获到的内容,关于捕获后的使用,将在下面的 \ 符号介绍
\d 等价于 [0-9],关于 [] 字符集下面会讲,只要知道 \d 是表示匹配一个数字就行了
3、+
匹配前面的子表达式一次或多次
见例子2
4、*
匹配前面的子表达式零次或多次
见例子2
5、.
匹配除换行符 \n之外的任何单字符
例子3
$s = <<<'TEXT' test!@#$ TEXT; preg_match_all('#.#s',$s,$m); print_r($m); /* Array ( [0] => Array ( [0] => t [1] => e [2] => s [3] => t [4] => ! [5] => @ [6] => # [7] => $ ) ) */
6、[ ]
标记一个中括号表达式字符集合
请看例子1,[a-z] 就是一个字符集合,可以是任意字符,比如我只要匹配 【abc,.】 这个5个字符——[abc,.]
这里有一个特殊的符号 - ,代表一个范围,常用的有 A-Z(大写的字母A到Z),a-z(小写的字母a到z) ,0-9(数字0到9)
7、?
匹配前面的子表达式零次或一次,或指明一个非贪婪限定符
例子4
$s = <<<'TEXT' a1a2abbb TEXT; //匹配以a开头后面紧跟一个数字或没有数字(用了 ? )的字符串 preg_match_all('#a\d?#',$s,$m); print_r($m); /* Array ( [0] => Array ( [0] => a1 [1] => a2 [2] => a ) ) */
8、\
将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。
比如匹配在正则中有特殊含义的字符,如 . 、 \ 、 $ 、 ( 、 [ 等等,这里就讲一下 向后引用(反向引用)
例子5
$s = <<<'TEXT' <b>this is b</b><i>this is i</i> TEXT; preg_match_all('#<([a-z]+)>(.+?)</\1>#i',$s,$m); echo "<pre>"; print_r($m); echo "</pre>"; /* Array ( [0] => Array ( [0] => <b>this is b</b> [1] => <i>this is i</i> ) [1] => Array ( [0] => b [1] => i ) [2] => Array ( [0] => this is b [1] => this is i ) ) */
我想匹配标签 b 和 i (或者其它标签)中的内容,总不能为了每一个标签都写一个表达式吧,比如这个:#<b>(.+?)</b>|<i>(.+?)</i>#i;那么多标签不得写死啊
而标签开头和结尾都是一样的,那么把匹配到的开始标签引用到结束位置不就行了?!
还记得例子2中 () 的作用吗,捕获匹配到的内容,供后面使用。而 \1 就是使用第一个 ( 开始所匹配到并被捕获的内容。在这个例子里 \1 就是匹配到的 b 或 i
那么想一想,若是在 () 中还有 (),如何使用反向引用?
好吧,不用你们想了,我来公布答案。
不管表达式中有多少个 (),都是以 ( 为顺序(从1开始),放在捕获的区域以供后面使用,最多好像是支持 99 个,没试过
在正则表达式中使用之前匹配并捕获(反向引用)的内容就是用 \1 \2 \3 ....各语言可能不同,比如 js 是 $1 ,java 是 \\1
例子6
$s = <<<'TEXT' <div><b>this is b</b></div> <div><span>this is span</span></div> <span><i>this is i</i></span> TEXT; preg_match_all('#<([a-z]+)><([a-z])>(.+?)</\2></\1>#i',$s,$m); print_r($m); /* Array ( [0] => Array ( [0] => <div><b>this is b</b></div> [1] => <span><i>this is i</i></span> ) [1] => Array ( [0] => div [1] => span ) [2] => Array ( [0] => b [1] => i ) [3] => Array ( [0] => this is b [1] => this is i ) ) */
匹配外层是多字符标签(div span 等)包裹,内层紧跟一个字符的标签(b i 等),因为 ([a-z]) 后面没有跟 + ,说明是匹配一个字符
由于<div><span>this is span</span></div> 不满足内层一个字符的标签,所以没有被匹配
例子7 java 版
import java.util.regex.*; public class Test { public static void main(String[] args) throws Exception { String str = "<div><b>this is b</b></div><div><span>this is span</span></div><span><i>this is i</i></span>"; Matcher m = Pattern.compile("<([a-z]+)><([a-z])>(.+?)</\\2></\\1>").matcher(str); while(m.find()){ System.out.println(m.group()+"\n"+m.group(1)+"\n"+m.group(2)+"\n开始位置 : "+m.start()+" \n结束位置 : "+m.end()+"\n\n"); } } }
输出
<div><b>this is b</b></div> div b 开始位置 : 0 结束位置 : 27 <span><i>this is i</i></span> span i 开始位置 : 63 结束位置 : 92
9、^
匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合
例子8
$s = <<<'TEXT' 1abc TEXT; //2def //ght //34xyz preg_match_all('#^\d[^\d]+#',$s,$m); echo "<pre>"; print_r($m); echo "</pre>"; /* Array ( [0] => Array ( [0] => 1abc ) ) */
匹配以一个数字开头后面紧跟非数字的字符串,这个例子就把 ^ 的两个概念都讲了,例子的结果就是能匹配 1abc,2def,不能匹配 ght,34xyz
这里讲讲为什么不能匹配 34xyz,因为 3 被 \d 匹配了,而 4 就只能被 [^\d]+ 匹配,但是这个集合只匹配非数字字符串,所以不满足要求,整个字符串都不能被匹配
在一个字符集中用 ^ (一定要在字符集的开始位置),如 [^abc] ,那么就表示,不匹配abc这三个字母
10、{ }
标记限定符表达式的开始
这个将在限定符一节中将,简单来说就是表示一个范围
11、|
指明两项(多项)之间的一个选择
例子9
$s = <<<'TEXT' <b>this is b</b><i>this is i</i><span>this is span</span> TEXT; preg_match_all('#<(b|i)>(.+?)</\1>#',$s,$m); print_r($m); /* Array ( [0] => Array ( [0] => <b>this is b</b> [1] => <i>this is i</i> ) [1] => Array ( [0] => b [1] => i ) [2] => Array ( [0] => this is b [1] => this is i ) ) */
匹配 b 或者 i 标签包裹的内容,若也要匹配 span, 加上即可,#<(b|i|span)>(.+?)</\1>#
特殊字符暂时就介绍这些,单个的字符都很好理解,但想写出复杂的正则表达式却无从下手。
那么如何构造复杂的表达式?
简单来说就是化整为零,正则表达式可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些符号的任意组合。
用多种元字符(特殊字符)与操作符将小的表达式结合在一起来创建更复杂的表达式。
比如 例子5 就用了很多元字符
一定要多练,才能把所有符号牢记于心,都是熟能生巧。
布置一个练习题
待匹配内容
<div>123abc,</div> <span>456def</span> <m>a1b2c3</m>
请匹配出 456def
分析下:
1、外面包裹的标签不一定是这几个(div span m)
2、内容是以 数字开头的,后面跟个几个字母
很简单,看上面的几个例子,动动手就可以完成
下一节讲 限定符和符号的运算优先级
lililala6868
xiexie