【野路子】正则表达式~极速学习@H_403_1@
本教程适合正则新手、初学者、看完能就用的人群。@H_403_1@
怎么入门呢?
网上的教程太多太多了,零散的也比较多,视频教程看起来更佳。但是整个过程比较漫长枯燥,可能暂时和你的需求目标不太一致,有些学不致用的样子。或者是问人、问百度最后搞出来了,可到了下次需要用到的时候,又进入萌逼状态了。@H_403_1@
正则表达式是一个什么东西呢?
如果你停留在这个问题上,这个教程可能对你作用不大。需要了解的话,详细查阅百科。@H_403_1@
这里只说一点:正则表达式在很多计算机程序设计语言上都可以进行匹配字符串的一种模式。@H_403_1@
往往用到的情况如下:数据校验、采集程序、爬虫程序,或者说我就想从目标数据拿到我想要的信息。@H_403_1@
开始了
如果你做好准备了,那么以下将以常用的两种语言进行讲述。@H_403_1@
首先来看个例程:@H_403_1@
1.获取开源中国首页基本信息
https://www.oschina.net/@H_403_1@
如:title、description、keywords@H_403_1@
怎么获取源码就不说了,右键查看源代码等,类似问题后面不再提起。@H_403_1@
a.匹个title 入个门
在线测试正则表达式地址:http://tool.oschina.net/regex/@H_403_1@
可以看到源码中的目标数据如横线所标。我们只需要匹配到对应的内容即可以。@H_403_1@
首先将对应的数据信息包括固定数据原本复制出来。@H_403_1@
标题:<title>开源中国 - 找到您想要的开源项目,分享和交流</title> 描述:<Meta name="description" itemprop="description" content="开源中国 www.oschina.net 是目前中国最大的开源技术社区。我们传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。,开源中国社区"> 关键词:<Meta name="keywords" content="开源,OSC,开源软件,开源硬件,开源网站,开源社区,java开源,perl开源,python开源,ruby开源,PHP开源,开源项目,开源代码">
举例测试标题,复制到在线测试正则里面进行调试。@H_403_1@
很显然我们匹配到结果了,这个正则表达式中可以认为不存在任何 正则表达式的语法,刚才的过程只是相当于 正常的字符串匹配。@H_403_1@
我们继续,删除部分数据(删除可变的数据),用正则表达式语法代替。@H_403_1@
正则表达式的书写原则也就是:用正则语法替换可变的数据@H_403_1@
语法:@H_403_1@
\s 空格换行等字符@H_403_1@
\S 非空格换行等字符@H_403_1@
<title>开源中国 - 找到您想要的开源项目,分享和交流</title> 改为: <title>开源中国\s-\s找到您想要的开源项目,分享和交流</title>
测试是否能匹配通过。@H_403_1@
很显然,我们匹配到了。继续改进。@H_403_1@
其中的中文字符以及逗号都是属于 “非空格”,所以我们可以通过\S代替。继续替换并测试。@H_403_1@
<title>开源中国\s-\s找到您想要的开源项目,分享和交流</title> 改为: <title>\S\S\S\S\s-\s\S\S\S\S\S\S\S\S\S\S\S\S\S\S\S\S</title>
依然能匹配到,貌似有些乱,容易出错,我们继续改进。@H_403_1@
语法:@H_403_1@
* 对前一个语法进行0个或多次匹配@H_403_1@
+ 对前一个语法进行1次以上的匹配@H_403_1@
{n,m} 指定次数匹配,如 \s{2,10} 即匹配2个到10个空格(区间均可)@H_403_1@
此时终于可以将那一堆\S代替了,简直美观。@H_403_1@
<title>\S\S\S\S\s-\s\S\S\S\S\S\S\S\S\S\S\S\S\S\S\S\S</title> 改为: <title>\S+\s-\s\S+</title> 或 <title>\S*\s-\s\S*</title> 或 <title>\S{4}\s-\s\S{16}</title>
当然,如@H_403_1@
<title>开源中国 - 找到您想要的开源项目,分享和交流</title> 例1: <title>[\u4e00-\u9fa5]+\s-\s[\u4e00-\u9fa5]+,[\u4e00-\u9fa5]+</title> 例2: <title>[^<]+</title>
普及下语法:@H_403_1@
语法:@H_403_1@
[\u4e00-\u9fa5] 中文字符,以及双字节中文[^\x00-\xff],PHP中语法为[\x{4e00}-\x{9fa5}]@H_403_1@
^ ‘非’逻辑符@H_403_1@
[] 匹配的字符范围 如[abc] ,只匹配a 或 b或 c 这三个字符@H_403_1@
上面第一个正则,很显然不需要解释,[\u4e00-\u9fa5] 这个组合是匹配中文字符的,+就是进行多个匹配。@H_403_1@
第二个正则很简洁,什么意思呢。@H_403_1@
[<] 只匹配 < (左键括号)@H_403_1@
[^<] 这里的^(非逻辑)起到的作用让这个组合变成了,只匹配 非< ,也就是说只要不是< 都可以匹配。@H_403_1@
[^<]+ 多次匹配,直到将非<字符匹配完停止。@H_403_1@
<title>[^<]+</title>@H_403_1@
此时再看这个式子,很明显了,将两个title 标记中间的所有内容匹配完,也就是我们需要的内容。@H_403_1@
b. 描述信息如何匹配?
原本数据: <Meta name="description" itemprop="description" content="开源中国 www.oschina.net 是目前中国最大的开源技术社区。我们传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。,开源中国社区">
经过以上的学习,我们可以书写几个式子,如@H_403_1@
式1: <Meta name="description" itemprop="description" content="[^"]+"> 式2: <Meta\s+name="description"\s+itemprop="[^"]+"\s+content="[^"]+"> 式3:(错误) <Meta\s+name="[^"]+"\s+itemprop="[^"]+"\s+content="[^"]+">
以上3个式子,第一个第二个都很容易理解,仔细看就能看明白。@H_403_1@
为什么第三个式子错了呢?他不能匹配数据吗?答案是可以的。@H_403_1@
在书写正则表达式的过程中,往往不要忘了我们的目的,以及降低可错性。@H_403_1@
我们举例下,第三个式子可能会匹配到什么数据?@H_403_1@
式3: <Meta\s+name="[^"]+"\s+itemprop="[^"]+"\s+content="[^"]+"> 数据1: <Meta name="description" itemprop="description" content="开源中国 www.oschina.net 是目前中国最大的开源技术社区。我们传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。,开源中国社区"> 数据2: <Meta name="robots" itemprop="robots" content="index,follow"> 数据3: <Meta name="viewport" itemprop="viewport" content="width=device-width,initial-scale=1">
以上三条数据,数据1是我们的意愿数据,但2和3不是,这就产生出错了。这并不是我们所想要的。@H_403_1@
上面的数据只是举例测试,不是开源中国的数据。@H_403_1@
所以在写正则的时候,一定要有标志性的固定字符,如上面的name="description"@H_403_1@
c.匹配关键词
这里都不用写式子了,相信各位都可以很轻易的完成。@H_403_1@
原本数据: <Meta name="keywords" content="开源,开源代码"> 式子: <Meta\s+name="keywords"\s+content="[^"]+">
d.用代码实现
使用chrome 打开开源中国,按F12 DevTools调试,选择Console控制台执行JS。@H_403_1@
js@H_403_1@
var html = document.documentElement.outerHTML; html.match(/<title>[^<]+<\/title>/); //注意上面 <\/title> 多了一个反斜杠\ ,我们要将正斜杠/ 转义掉,否则影响正则表达式。 //两边的 /是什么呢?这个是正则表达式的定界符,表示中间的内容为正则主要匹配数据、模式。
结果里面包含了title标签,也就是固定字符,这并不是我们的预期,如何解决呢?@H_403_1@
语法:@H_403_1@
html.match(/<title>([^<]+)<\/title>/); html.match(/<title>([^<]+)<\/title>/)[1];
测试如下@H_403_1@
很简单就取到我们的结果。@H_403_1@
JS的另一种写法:@H_403_1@
var regular = new RegExp('<title>([^<]+)</title>'); html.match(regular);
<?PHP $html = file_get_contents('https://www.oschina.net/'); $pattern = '/<title>([^<]+)<\/title>/'; preg_match_all($pattern,$html,$result); echo '<pre>'; print_r($result);
2.练习,匹配开源中国资讯条目
JS@H_403_1@
html.match(/<div class="Box vertical news">[^<]+<a\s+class="[^"]+"\s+href="([^"]+)"[^>]+>([^<]+)<\/a>[^<]+<span\s+class="Box-fr news-date">([^<]+)<\/span>\s+<\/div>/)
很不巧的是,智能匹配到1个,那么剩下的数据为什么没有匹配到呢?@H_403_1@
因为match这个函数默认只返回第一次匹配的结果,不会返回所有匹配到的结果。@H_403_1@
语法:@H_403_1@
g 修饰符,全文匹配所有结果@H_403_1@
/ 正则表达式 /修饰符@H_403_1@
如: /<li[^>]+>([^<]+)<\/li>/g 全文匹配所有li@H_403_1@
如以下即可匹配到所有的条目。@H_403_1@
html.match(/<div class="Box vertical news">[^<]+<a\s+class="[^"]+"\s+href="([^"]+)"[^>]+>([^<]+)<\/a>[^<]+<span\s+class="Box-fr news-date">([^<]+)<\/span>\s+<\/div>/g);
<?PHP $html = file_get_contents('https://www.oschina.net/'); $pattern = '/<div class="Box vertical news">[^<]+<a\s+class="[^"]+"\s+href="([^"]+)"[^>]+>([^<]+)<\/a>[^<]+<span\s+class="Box-fr news-date">([^<]+)<\/span>\s+<\/div>/'; preg_match_all($pattern,$result); echo '<pre>'; print_r($result);
而对于PHP而言,就不需要g了,区别于函数的选择上。@H_403_1@
perg_match 单次匹配@H_403_1@
preg_match_all 所有匹配@H_403_1@
补充学习
通过以上的学习,一些简单基本的正则匹配都可以实现。@H_403_1@
那么再补充说一说常规数据校验匹配的案例,虽然网上都有。@H_403_1@
/^\d{11}$/ 匹配11位数字(手机号)@H_403_1@
^为起始匹配也就说,第一个字符必须是 \d 也就是数字,$代表结束符,同样,匹配的这段字符必须以 \d 结束才行。@H_403_1@
/^110[0-9]{14}[0-9xX]/@H_403_1@
/^110[0-9]{14}[0-9X]/i@H_403_1@
匹配北京市的身份证号,110开头,最后一位可能为数字可能为X。两个式子都可以,区别于尾部x的大小写问题,可以写到模式里,可以写到修饰符里。@H_403_1@
修饰符 i 代表不区分大小写@H_403_1@
/[A-z_\d]+\.(jpg|png|exe)/i 文件名匹配@H_403_1@
(jpg|png|exe) 匹配jpg 或 png 或exe ,区别于[jpg|png|exe],中括号是单个字符匹配,圆括号是整体匹配。@H_403_1@
\. 匹配.字符,而不是任意字符,这里被反斜杠转义。@H_403_1@
(.*?) 所谓的万能正则 + 贪婪模式@H_403_1@
. 代表任意字符@H_403_1@
.* 代表任意字符匹配0个或者多个@H_403_1@
.*? 代表任意字符匹配0个或者多个(最少匹配,非贪婪模式)@H_403_1@
var string = 'aa<div>test1</div>bb<div>test2</div>cc'; string.match(/<div>.*<\/div>/); string.match(/<div>.*?<\/div>/);
可以看到,第一个正则没有加? 导致执行匹配到最后一个div,而增加?的只是匹配到对等的第一个div。@H_403_1@
这个也就是正则的贪婪模式,贪婪匹配到最后一个,加上?将取消贪婪模式,也就是非贪婪模式,默认匹配到第一个就停止。@H_403_1@
PHP中可以使用修饰符 大写U,来取消贪婪模式,如:/<div>.*<\/div>/U@H_403_1@
模式修正符@H_403_1@
PHP模式修饰符又叫模式修正符,是在正则表达式的定界符之外使用。主要用来调整正则表达式的解释,提扩展了正则表达式在匹配、替换等操作的某些功能,增强了正则的能力。@H_403_1@
模式修正符号 | 功能描述 |
---|---|
i | 在和正则匹配是不区分大小写 |
m | 将字符串视为多行。默认的正则开始“^”和结束“$”将目标字条串作为一单一的一“行”字符(甚至其中包括换行符也是如此)。如果在修饰符中加上“m”,那么开始和结束将会指点字符串的每一行的开头就是“^”结束就是“$”。 |
s | 如果设定了这个修正符,那么,被匹配的字符串将视为一行来看,包括换行符,换行符将被视为普通字符串。 |
x | 忽略空白,除非进行转义的不被忽略。 |
e | 只用在preg_replace()函数中,在替换字符串中逆向引用做正常的替换,将其(即“替换字符串”)作为PHP代码求值,并用其结果来替换所搜索的字符串。 |
A | 如果使用这个修饰符,那么表达式必须是匹配的字符串中的开头部分。比如说”/a/A”匹配”abcd”。 |
D | 模式中的$字符权匹配目标字符的结尾。没有此选项时,如果最后一个字符是换行符的话,美元符号也会匹配此字符之前。如果设定了修正符m则忽略此项。 |
E | 与”m”相反,如果使用这个修饰符,那么”$”将匹配绝对字符串的结尾,而不是换行符前面,默认就打开了这个模式。 |
U | 贪婪模式,和问号的作用差不多,最大限度的匹配就是贪婪模式。 |
整理总结
[a-z] //匹配所有的小写字母 [A-Z] //匹配所有的大写字母 [a-zA-Z] //匹配所有的字母 [A-z] //匹配所有大小写字母 以及 [\]^_' 这些玩意,详查ASCII字符表。 [0-9] //匹配所有的数字 [0-9\.\-] //匹配所有的数字,句号和减号 [ \f\r\t\n] //匹配所有的白字符 \d //匹配一个数字字符。等价于 [0-9]。 \D //匹配一个非数字字符。等价于 [^0-9]。 \n //匹配一个换行符。等价于 \x0a 和 \cJ。 \r //匹配一个回车符。等价于 \x0d 和 \cM。 \s //匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。 \S //匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。 \t //匹配一个制表符。等价于 \x09 和 \cI。 \ //转义符,如果匹配\字符,需使用 \\ 。 . //匹配除过"\n"之外的任何字符。 ? //匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。 + //匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。 * //匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。 ^ //匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置。 $ //匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置。 #修饰符 i //忽略大小写 s //忽略换行,当做一行匹配 U //非贪婪模式
上面整理了一些简单常用的,元字符以及模式规则,这些东西可以不用一次记住,书写的时候随时来查阅参考即可,写的多了,自然就会掌握这些规则技巧,完全不用刻意记。@H_403_1@
本文引用:@H_403_1@
PHP正则表达式模式修饰符详解@H_403_1@