正则表达式的学习与实践
目录
正则表达中有许多的知识点,做成目录的形式方便日后查找。本文举的例子皆为php,php中的正则表达式,有preg_match($pattern, $str, $matches)和preg_match_all(…)两种 ,前者匹配成功一次就返回结果,后者是全局的正则匹配,会将所有成立的字符串存入到$matches中,两者都会返回匹配的次数。
两个基本原则
- 最左原则 ,正则匹配都是从字符串最左边开始的;
- 最长原则 ,正则匹配会匹配出符合要求的最长的字符串(贪婪模式);
定界符
一般我们使用’/’或者’#’作为定界符,但在url中带有很多的’/’,这时我们使用’#’更好
举例:
<?php
$pattern = '/http:\/\/.*com/';
$str = 'http://baidu.com';
preg_match($pattern, $str, $matches);
var_dump($matches);
?>
result:
array(1) {
[0]=>
string(16) "http://baidu.com"
}
使用’#’作为定界符
<?php
$pattern = '#http://.*com#';
$str = 'http://baidu.com';
preg_match($pattern, $str, $matches);
var_dump($matches);
?>
result:
array(1) {
[0]=>
string(16) "http://baidu.com"
}
修饰符
用于改变正则表达式的行为
i 就是一个修饰符,作用是忽略大小写;x也是修饰符,其作用是忽略空格
<?php
$pattern = '#http://.*com#i';
$str = 'http://www.youku.Com/show_page/CDE.html';
preg_match($pattern, $str, $matches);
var_dump($matches);
?>
result:
array(1) {
[0]=>
string(20) "http://www.youku.Com"
}
字符域
符号 | 含义 |
---|---|
\d | 表示匹配一个数字字符,等价于 [0-9] |
\D | 匹配一个非数字字符,等价于[^0-9] |
\f | 匹配一个换页符 |
\n | 匹配一个换行符,等价于\x0a和\cJ |
\r | 匹配一个回车符,等价于\x0d和\cM |
\s | 匹配任何空白字符 空格tab换页符等 等价于[\f\n\r\t\v] |
\S | 匹配任何非空白字符 |
\t | 匹配任何一个制表符 等价于\x09和\cI |
\v | 匹配一个垂直制表符 等价于\x0b和\cK |
\w | 匹配包括下划线的任何单个字符 等价于[A-Za-z0-9_] |
\W | 匹配任何非单次字符 |
限定符
跟在字符域后的符号,例如:
\d{n,m} 表示匹配n到m个数字
\d{i} 这是[\d]{i,i}的简写,指定匹配i次
\d{n,} 表示匹配超过n个数字
脱字符
符号 | 含义 |
---|---|
* | [\w]* 表示匹配0到多个包括下划线的字符 |
+ | [\D]+ 表示匹配1到多个非数字的字符 |
^ | 放在表达式之前,表示用字符串起始位置匹配 |
$ | 表示匹配字符串的末尾 |
[] | 方括号表示只要匹配方括号任何元素成立即可,在[]中 ^表示取反 ;可以直接使用[.],不需要对.进行转义,如[\d.]表示匹配数字或 . |
? | 表示匹配子表达式一次或零次:he(llo)? 可以匹配he 也可以匹配hello |
. | 表示匹配除换行符外的任何单个字符; (.|\n)即可匹配任何字符 |
() | 正则中的小括号很有意思,我一开始以为就是单纯地把多个表达式组合在一起而已。举个栗子,’(abc)’表示匹配字符串abc,但()还有个额外操作,就是将括号里的内容加入到匹配数组$matches中。 |
备注
'\'作为转义字符,如果要匹配‘\’,则应该这么写:
$pattern = "#\\\#"; //连续三个\才可以
'^'的例子:
<?php
$pattern = '#^http://.*com#i';
$str = 'http://www.youku.Com/show_page/CDE.html';
preg_match($pattern, $str, $matches);
var_dump($matches);
?>
表示$str的起始位置是否满足正则匹配
'[]'的例子:
<?php
$str = "absjd";
$p = '/[abc]/';
preg_match($p,$str,$matches);
var_dump($matches);
?>
结果:
array(1) {
[0]=>
string(1) "a"
}
关于?,我还出了一次糗,网上有人用这样一个正则表达式解析网页内容:
‘/\[baidu\][\s\S]*?\[\/baidu\]/’
可以匹配诸如’[baidu]abcde[/baidu]’
这样的内容
我在想*已经表示匹配0到多次了,?表示匹配0或1次,这不是画蛇添足吗,实际上,这里的?还有一种作用,叫做非贪婪符号,默认会得到符合要求的最长字符串,非贪婪符号则是得到符合要求的最短字符串。
关于’()'例子:
<?php
$str = "abcdef";
$p = '/(abc)d/';
preg_match($p,$str,$matches);
var_dump($matches);
?>
结果:
array(2) {
[0]=>
string(4) "abcd"
[1]=>
string(3) "abc"
}
若不想把()中内容加入到匹配数组,可以使用’(?:)’
例子:
<?php
$str = "abcdef";
$p = '/(?:abc)d/';
preg_match($p,$str,$matches);
var_dump($matches);
?>
结果:
array(1) {
[0]=>
string(4) "abcd"
}
通配符
符号 | 含义 |
---|---|
正向预查 | (?=) 表示匹配字符的右边 对应的否定形式(?!) |
反向预查 | (?<=) 对应的否定形式(?<!) 表示匹配字符的左边 |
举例:
表示匹配的字符’bc’,左边需要是字符’a’,右边需要是字符’d’
<?php
$pattern = '#(?<=a)bc(?=d)#';
$str = 'abcdef';
preg_match($pattern, $str, $matches);
var_dump($matches);
?>
另外,需要注意,正向预查和反向预查符都是不占字符宽度的
栗子:
<?php
$pattern1 = '#(?<=a)bc(?=d)e#';
$pattern2 = '#(?<=a)bc(?=d)de#';
$str = 'abcdef';
preg_match($pattern1, $str, $matches1);
var_dump("matches1:");
var_dump($matches1);
preg_match($pattern2, $str, $matches2);
var_dump("matches2:");
var_dump($matches2);
?>
result:
string(9) "matches1:"
array(0) {
}
string(9) "matches2:"
array(1) {
[0]=>
string(4) "bcde"
}
一些常用的正则匹配式
用正则匹配ip
- 我一开始写了
'/^\d+\.\d+\.\d+\.\d+$/'
(’.’是脱字符,需要进行转义)起始是大于等于一位的数字. 大于等于一位的数字. 大于等于一位的数字. 大于等于一位的数字为结尾
进一步用’()’优化:'/^(?:\d+\.){3}\d+$/'
; - 看到网上有人会这么写
'/^(?<!\.)[\d.]+(?!\.)$/'
起始是大于等于一位的数字,接下来是’.’或者数字都无所谓,只要结尾是数字就能完成匹配,这其实很不严谨。 - 我的写法1还是存在问题,并没有很好地限制数字长度和大小,要知道ip中数字是小于等于255的
我用上限定符对数字做一个限制:'/2([0-4]\d|5[0-5])|1?\d{1,2}/'
最终纯净版:
'/^(?:(?:2(?:[0-4]\d|5[0-5])|1?\d{1,2})\.){3}(?:2(?:[0-4]\d|5[0-5])|1?\d{1,2})$/'
不纯净版(若只取匹配数组的第一个元素则和纯净版一样):
'/^((2([0-4]\d|5[0-5])|1?\d{1,2})\.){3}(2([0-4]\d|5[0-5])|1?\d{1,2})$/'
用正则匹配手机号
手机号的正则简单的多了:'/^1[34578]\d{9}$/'
用正则匹配用户名
用户名一般为字母下划线开头,由字母数字下划线组成(可以用到\w)
'/^[A-Za-z_]\w+$/'
用正则匹配邮箱号
邮箱一般是邮箱名@域名;
邮箱名由字母数字下划线中划线组成,因此可以用[\w-]+表示
域名的规律一般为[四级域名].[三级域名].[二级域名].[一级域名]
我的写法:
'/^[\w-]+(\.[\w-]+)*@[\w-]+(.[a-z]{2,})+$/i'
这个正则式能过滤很多错误邮箱号,但邮箱的写法太多,这个正则式并不完美。
菜鸟上写的邮箱正则式是这样的:
'/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/'
其实写的和我的差不多,大家可以做一个参考。
用正则匹配密码
这里的密码比较简单,包含数字、大小写字母、和一些常用的符号,视具体需求进行修改
'/^[a-zA-Z0-9_.!@#$%^&*]{6,}$/'
JS中使用正则表达式的方法
var pattern = /^[a-zA-Z0-9_.!@#$%^&*]{6,}$/;
if (!pattern.test(password)){
alert("密码不规范");
}