
摘要
正则表达式在近乎所有语言中都可以使用,无论是前端的JavaScript、还是后端的Java、c#。他们都提供相应的接口/函数支持正则表达式。
元字符说明:匹配除换行符以外的任意字符
w:匹配字母或数字或下划线或汉字
s:匹配任意的空白符
d:匹配数字匹配单词的开始或结束
^:匹配字符串的开始
$:匹配字符串的结束
eg:匹配有abc开头的字符串:abc或者^abc
匹配8位数字的QQ号码:^dddddddd$
匹配1开头11位数字的手机号码:^1dddddddddd$
为了处理这种重复问题,正则表达式中一些重复限定符,把重复部分用适合的限定符替代
语法说明:
*重复零次或更多次
+重复一次或更多次
?重复零次或一次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n到m次
匹配8位数字的QQ号码:^d{8}$
匹配1开头11位数字的手机号码:^1d{10}$
匹配银行账号是14~18位的数字:^d{14,18}$
匹配以a开头的,0个或多个b结尾的字符串^ab*$
正则表达式中用小空格()来做分组,也就是括号中的内容成为一个整体。
因此当我们应匹配多个ab时,我们可以这么。
如匹配字符串中包括0到多个ab开头:^(ab)*
我们提到正则表达式用小空格来做分组,那么问题来了:
如果应匹配的字符串中原本就包括小括号,那是不是冲突?应该如何办?
针对这些状况,正则提供了转义的方法,也就是要把这种元字符、限定符以及关键字转义成普通的数组,做法更简答,就是在要转义的数组前面加个斜杠,也就是\即可。
如应匹配以(ab)开头:^\(ab\)*
回到我们今天的手机号匹配,我们都清楚:国内号码都来自三大网,它们都有属于自己的号段,比如联通有130/131/132/155/156/185/186/145/176等号段,假如让我们匹配一个联通的号码,那按照我们现在所学到的正则,应该无从下手的,因为此处包含了一些并列的条件,也就是“或”,那么在正则中是怎样表示“或”的呢?
正则用符号 | 来表示或,也叫做分支条件,当满足正则里的分支条件的任何一种条件时,都会变成是匹配成功。
那么我们就可以用“或”条件来处理这个难题:^(130|131|132|155|156|185|186|145|176)d{8}$
正则提供一个元字符中括号 [] 来表示区间条件。
那后面的正则我们还改成这种:
^((13[0-2])|(15[56])|(18[5-6])|145|176)d{8}$
好了,正则表达式的基本用法就提到这里了,其实它也有相当多的知识点以及元字符,我们在此只列出了个别元字符和词汇来讲,旨在给这些不懂正则以及想学正则但有看不下来文档的人做一个快速入门级的教程,看完本教程,即使你不能写出高大上的正则,至少也可写一些简单的正则以及看得懂对方写的正则了。
断言:俗话的坚信就是“我怀疑什么什么”,而正则中的坚信,就是说正则可以指引在选定的内容的后边或中间会出现满足指定规则的内容,意思正则也可以象人类这样怀疑什么什么,比如”ss1aa2bb3”,正则可以用断言找出aa2前面有bb3,也可以找出aa2后面有ss1.
零宽:就是没有宽度,在正则中,断言只是匹配位置,不占字符,也就是说,匹配结果里是不会返回断言本身。
eg:假设我们要用爬虫抓取csdn里的文章阅读量。通过查看源代码可以看见文章阅读量这个内容是这种的结构

“阅读数:641“
其中也就‘641’这个是变量,也就是说不同文章不同的值,当我们获得这个字符串时,需要获取这上面的‘641’有很多种办法,但即使正则应该如何匹配呢?
语法:(?=pattern)
作用:匹配pattern表达式的后面内容,不返回原本。
这样子说,还是一脸懵逼,好吧,回归刚刚那个栗子,要取到阅读量,在正则表达式中就意味着要可匹配到‘’前面的数字内容。
按照上所说的反向先行断言可以匹配表达式里面的内容,那意思就是:(?=) 就可以匹配到后面的内容了。
匹配哪些内容呢?如果要所有内容那就是:
1
2
3
4
5
6
7
8
9
10
String reg=".+(?=</span>)";
String test = "<span class="read-count">阅读数:641</span>";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
while(mc.find()){
System.out.println("匹配结果:")
System.out.println(mc.group());
}
//匹配结果:
//<span class="read-count">阅读数:641
可是大哥我们要的也是里面的数字呀,那也简单咯,匹配数字 d,那可以改成:
1
2
3
4
5
6
7
8
9
String reg="\d+(?=</span>)";
String test = "<span class=\"read-count\">阅读数:641</span>";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
while(mc.find()){
System.out.println(mc.group());
}
//匹配结果:
//641
大功告成!
语法:(?<=pattern)
作用:匹配pattern表达式的前面的内容,不返回原本。
有先行就有后行,先行是匹配上面的内容,那后行就是匹配前面的内容啦。
上面的栗子,我们也可以用后行断言来处理。
1
2
3
4
5
6
7
8
9
10
//(?<=<span class="read-count">阅读数:)d+
String reg="(?<=<span class=\"read-count\">阅读数:)\d+";
String test = "<span class=\"read-count\">阅读数:641</span>";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
while(mc.find()){
System.out.println(mc.group());
}
//匹配结果:
//641
就这样简单。
语法:(?!pattern)
作用:匹配非pattern表达式的后面内容,不返回原本。
有正向也有负向,负向在这里其实就是非的意思。
举个栗子:比如有一句 “我爱祖国,我是祖国的花朵”
现在要找到不是’的花朵’前面的祖国
用正则就可以这么写:祖国(?!的花朵)。
语法:(?<!pattern)
作用:匹配非pattern表达式的前面内容,不返回原本。
单纯说到捕获,他的含义是匹配表达式,但捕获通常跟分组联系在一起,也就是“捕获组”。
捕获组:匹配子表达式的内容,把匹配结果储存到存储中中数字编号或显示命名的组里,以深度优先进行编号,之后可以借助序号或名称来使用这种匹配结果。
而按照命名方法的不同,又可以分为两种组。
语法:(exp)
解释:从表达式左边开始,每出现一个左括号跟它对应的右括号之间的内容为一个分组,在分组中,第0组为整个表达式,第一组开始为分组。
比如固定电话的:020-85653333
他的正则表达式为:(0d{2})-(d{8})
按照左括号的次序,这个表达式有如下分组:
序号编号分组内容00(0d{2})-(d{8})020-8565333311(0d{2})02022(d{8})85653333
我们用Java来验证一下:
1
2
3
4
5
6
7
8
9
10
String test = "020-85653333";
String reg="(0\d{2})-(\d{8})";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
if(mc.find()){
System.out.println("分组的个数有:"+mc.groupCount());
for(int i=0;i<=mc.groupCount();i++){
System.out.println("第"+i+"个分组为:"+mc.group(i));
}
}

输出结果:
1
2
3
4
分组的个数有:2
第0个分组为:020-85653333
第1个分组为:020
第2个分组为:85653333
可见,分组个数是2,但是由于第0个为整个表达式原本,因此也一起输出了。
语法:(?exp)
解释:分组的命名由表达式中的name指定
比如区号也可以这么写:(?d{2})-(?d{8})
按照左括号的次序java 正则表达式 版本号,这个表达式有如下分组:序号名称分组内容00(0d{2})-(d{8})020-856533331quhao(0d{2})0202haoma(d{8})85653333
用代码来验证一下:
1
2
3
4
5
6
7
8
9
String test = "020-85653333";
String reg="(?<quhao>0\d{2})-(?<haoma>\d{8})";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
if(mc.find()){
System.out.println("分组的个数有:"+mc.groupCount());
System.out.println(mc.group("quhao"));
System.out.println(mc.group("haoma"));
}
输出结果:
1
2
3
分组的个数有:2
分组名称为:quhao,匹配内容为:020
分组名称为:haoma,匹配内容为:85653333
语法:(?:exp)
解释:和捕获组正好相反,它拿来标识这些不需要捕获的分组,说的通俗一点,就是你可以按照需要去保存你的分组。
比如上面的正则表达式,程序不需要用到第一个分组,那就可以这么写:(?:d{2})-(d{8})
序号编号分组内容00(0d{2})-(d{8})020-8565333311(d{8})85653333
验证一下:
1
2
3
4
5
6
7
8
9
10
String test = "020-85653333";
String reg="(?:0\d{2})-(\d{8})";
Pattern pattern = Pattern.compile(reg);
Matcher mc= pattern.matcher(test);
if(mc.find()){
System.out.println("分组的个数有:"+mc.groupCount());
for(inti=0;i<=mc.groupCount();i++){
System.out.println("第"+i+"个分组为:"+mc.group(i));
}
}
输出结果:
1
2
3
分组的个数有:1
第0个分组为:020-85653333
第1个分组为:85653333
上面提到捕获,我们了解:捕获会返回一个捕获组,这个分组是保存在存储中,不仅可以在正则表达式外部通过程序进行引用,也可以在正则表达式内部进行引用,这种引用形式就是反向引用。
根据捕获组的命名规则,反向引用可分为:
数字编号组反向引用:k或 umber
命名编号组反向引用:k以及’name’
好了 讲完了,懂吗?不懂!!!
可能连后面讲的捕获有哪些用都还不懂吧?
其实也是看完捕获不懂不会用是很正常的!
因为捕获组一般是跟反向引用一起使用的。
上面说到捕获组是匹配子表达式的内容按序号以及命名保存起来从而使用。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-135746-1.html
期待
跟谁打也不要跟英美打