正则表达式 教程 正则表达式教程 PHP 正则 技术 正则表达式教程一 —— 特殊字符(元字符) 2017-11-28 16:44 1987 更新于 2017-11-28 16:44 ## 一、概述: 正则表达式,又称规则表达式。是由普通字符(例如字符 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 (或者其它标签)中的内容,总不能为了每一个标签都写一个表达式吧,比如这个:`#(.+?)|(.+?)#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]) `后面没有跟 + ,说明是匹配一个字符 由于`this is span` 不满足内层一个字符的标签,所以没有被匹配 #### 例子7 java 版 ```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、内容是以 数字开头的,后面跟个几个字母 很简单,看上面的几个例子,动动手就可以完成 下一节讲 限定符和符号的运算优先级