正则表达式学习笔记
(原创内容,转载请注明来源,谢谢)
首先,学习正则表达式,很推荐一篇博客,http://www.cnblogs.com/deerchao/archive/2006/08/24/zhengzhe30fengzhongjiaocheng.html,deerchao写的《正则表达式30分钟入门教程》,看完他的文章,基本上可以在实际中使用正则表达式,本文是结合此博客和一些其他书籍的内容的学习笔记。
一、基础内容
我认为的基础内容包括以下7点,掌握后可以使用正则匹配很多内容。
1、位置
正则表达式表示位置的字符有^(表示字符串开始)、$(字符串结束)、\b(字符串开始或结束)。
在明确需要匹配的位置的情况下,建议使用^、$,因为其会加快字符串的匹配速度。
2、数量
正则表达式表示数量的主要有*(匹配任意次)、?(匹配0次或1次)、+(匹配1次或多次)、[](中括号内的内容匹配其中一个一次)、{m,n}(匹配m至n次,n省略则匹大于或等于m次,逗号也省略则匹配m次)。
3、字符组
字符组为使用一些特殊的方式表示一组同样规律的内容。\d表示0-9任意一个数字,
\s表示任意个空白字符(空格),\w表示任意大小写字母、数字、下划线、中文。.(点)表示换行符以外的任意内容。
字符组和数量常常组合起来使用,如匹配四位数字可以用\d{4}。
4、反义
^符号用在正则的第一个位置表示字符串的开始,但是用在[]内部则表示非,例如[^\d]表示匹配1个不是数字的字符。其他还有\W(表示非\w)、\D(表示非\d)、\S(表示\s)、[^abc]表示匹配abc以外的字符。
5、转义
转义使用反斜杠\,这个和很多程序语言相同。即\\匹配\,\.匹配.等。
6、分支
分支符号为|,和程序语言中的||意思相似,表示或的意思。例如要匹配my和mine,可以使用m(y|ine)进行匹配。
7、分组
分组采用()(小括号)把内容放在里面,通常分组后可以加上表示数量的词,进行批量匹配。例如要匹配的内容是三个数字加一个字母a,一共匹配10次,可以用(\d{3}a){10}。
二、进阶内容
除了上述内容,正则表达式提供了一些高级的功能,让匹配更加全面与方便。
1、捕获
1) (?<name>exp),该含义为匹配表达式exp,并将其命名为name,后面的匹配中可以用\k<name>表示匹配到的内容。
例如,(?<myname>\d{4})\w?\k<myname>,表示把第一次匹配到的4个数字存在myname内,后面就用\k<mynname>再次匹配即可。
2) 捕获还有其他写法,如(exp),表示不给exp取名字,该情况下会保存在系统默认名字内,从1开始编号。例如(\d)\w(\d{5})\w\1\w\2,表示第一次捕获的一个数字存在\1内,第二次捕获的五个数字存在\2内。
3) 因此,在正则表达式中,括号应当慎用,因为每个括号正则都会将其捕获,并进行存储,如果在长字符串匹配的情况下,又使用了大量的括号,将占用较多存储空间。另外,如果不需要捕获内容,可以使用(?:exp),表示不捕获文本,也不进行编号。
2、零宽断言
零宽断言也是类似$、^等表示位置的字符,但是对该位置上的字符有一定的要求。主要有四个表达方式。
1) (?=exp),表示该位置的内容要满足exp的要求时,匹配exp之前的内容。例如\w+(?=ing),会匹配doing的do。
2) (?<=exp),表示该位置的内容要满足exp的要求时,匹配exp之后的内容。例如\w+(?<=re),会匹配reading的ading。
3) (?!exp),表示该位置的内容不是exp时,匹配exp之前的内容。例如\b\w+(?!ing)\b,会匹配不含ing结尾的任意单词。
4) (?<!exp),表示该位置的内容不是exp时,匹配exp之后的内容。例如\b\w+(?!re)\b会匹配任意不是以re开头的单词。
上述内容中,3)、4)又称为负向零宽断言。
3、注释
(?#comment)表示注释,不会被解析,仅仅是便于其他人员查看正则表达式。
4、懒惰匹配
1) 懒惰匹配表示匹配尽量少的内容,在匹配符后面加上一个?即可。*?表示重复任意次,但是尽可能的少重复;{10,}表示重复10次以上,但是尽可能少重复。其他数量词加上?也一样,表示满足基本条件的情况下尽可能少匹配。
例如,a.*?b在字符串abaab中会匹配到ab。
2) 与懒惰匹配相对应的,就是贪婪匹配,在不加?情况下,前面说的内容都是贪婪匹配。
3) 为了使程序匹配速度更快,在确定只需要懒惰匹配的情况下,需要加上?,可以最快匹配到需要的内容。
5、优先级问题
优先级从高到低,依次是:
1) \
2) ()、(?=)、(?=)、[]
3) *、+、?、{n}、{n,}、{n,m}
4) ^、$、任意字符
5) |
三、PHP正则表达式匹配函数
1、preg_math
官方文档int preg_match ( string $pattern
, string $subject
[, array &$matches
[, int $flags
= 0 [, int $offset
= 0 ]]] )
常用到前三个参数,$pattern表示匹配的模式,$subject表示需要匹配的字符串,如果提供了参数matches,它将被填充为搜索结果。$matches[0]将包含完整模式匹配到的文本,$matches[1] 将包含第一个捕获子组匹配到的文本,以此类推。
函数返回:pattern 的匹配次数。它的值将是0次(不匹配)或1次,因为preg_match()在第一次匹配后 将会停止搜索。
2、preg_match_all
官方文档int preg_match_all ( string $pattern
, string $subject
[, array &$matches
[, int $flags
= PREG_PATTERN_ORDER
[, int$offset
= 0 ]]] )
前三个参数和preg_math一样,返回完整匹配次数(可能是0),或者如果发生错误返回FALSE。该函数匹配成功一次后,会从匹配成功的最后一个位置开始,继续往后匹配。
3、常用模式
preg_math和preg_math_all的$pattern,需要输入的字符串都是 ‘%exp%’,即在正则表达式的基础上,前后加上两个%。通常来说,不使用%,而用其他符号也可以,只需要保证前后一致即可。实际工程中,为了项目统一,最好定一个一致的号码。
1) 忽略大小写
$pattern = ‘%exp%i’,即在第二个%后面加一个字母i即忽略大小写匹配。
2) 点号通配模式
点号通配模式表示元字符.(点)忽略换行。使用方法是$pattern= ‘%exp%s’
3) 多行模式
多行模式表示,当$pattern是多行内容时,如果加上$、^,该模式下,会将$、^之间的内容当成一行内容,忽略字符串当中的换行\n。使用方法是$pattern = ‘%exp%m’
4) 懒惰模式
类似于正则表达式的懒惰模式,使用方法是$pattern = ‘%exp%U’
5) 结尾限制模式
该模式下,结尾不能有换行,否则匹配失败。使用方法是$pattern = ‘%exp%D’
6) 支持UTF-8转义表达方式
如果汉字等被用UTF-8编码,则需要开启此模式进行匹配。使用方法是$pattern = ‘%exp%u’
四、实际应用
1、校验
如手机号校验,要判断手机号是否为移动的号码,即要确定开头为135-139、150-151、157-159、182、183、188的手机号,表达式如下:
(?:13[4-9]|15[01789]|18[238])\d{8}
2、数据安全
当给用户提供输入框时,用户可能往里面输入js代码对网站进行破坏,这个称为XSS攻击,因此可以用正则表达式把所有的<>或者</>去掉。表达式如下:
<\/?[^>]+>
3、URL重定向
在Apache和Nginx中,经常需要配置url的rewrite,可以把php后缀的文件重定向到html后缀的文件中,这样做便于搜索引擎的检索。
假设需要把test.php?name=a&page=1重定向为test_a_1.html,在apache的.htaccess中,可以如下方式:
RewriteEngine on
RewriteRule index.html index.php
RewriteRule test_([a-z]+)(\d?)\.htmltest.php?name=$1&page=$2[NC]
NC表示忽略大小写,rewriteengineon是开启rewrite的意思。
五、PHP正则表达式的优化
当字符串很长,需要匹配的模式串也很长的时候,需要尽可能的对正则表达式进行优化,否则会降低程序运行速度。
1、满足匹配前提下少用|
|符号效率较低,需要逐个进行匹配,例如[abc]和[a|b|c],|会把内容分别进行匹配。
2、限定量词优先
在确定需要匹配次数的情况下,尽量不要使用*、+、{n,}等不限定长度的量词,否则会进行多次的查找。
3、优先用preg_match/命中率最高的匹配项放最左侧
因为其匹配到就停止,而preg_match_all会把全部内容匹配完。同样,在多个匹配条件情况下,把最有可能命中的情况放在最左侧,则匹配到就不会继续往后匹配。
4、合理使用括号
括号会占用存储空间,大量匹配的情况下慎用。
5、使用PHP自带的一些函数
1) 当可以确定需要的字符串的位置时,尽量使用字符串匹配函数,即str开头的函数,匹配速度更快。
2) 需要匹配PHP的源码,可以安装使用PHP自带的Tokenizer分析函数,可以准确的分析PHP的变量、常量、类名、方法名等。
3) 解析URL时,可以用PHP自带的parse_url()函数,该函数可以把url的类型、host、path、query等输出。
4) 获取HTTP头,可以使用PHP自带的get_headers()函数,该函数可以捕获到HTTP头的信息,并用数组方式返回。
5) 验证邮箱信息、URL信息、数据类型等,均可以安装使用PHP的filter_var函数,该函数可以使用特定的过滤器过滤一个变量。
六、验证正则表达式的正确性
网上有很多验证工具,我个人比较喜欢的是http://tool.lu/regex/
——written by linhxx 2017.07.05