正则表达式
更新日期:
什么是正则表达式
编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。
基础表达式
- .所有字符
- \d:所有数字
- \D:所有非数字
- \s:空白字符
- \S:非空白字符
- \w:[a-zA-Z_0-9]
\W:非\w
//.表示任意字符 System.out.println("a".matches(".")); System.out.println("aa".matches(".a")); System.out.println("\\d"); //\\d表示是否是数字 System.out.println("123".matches("\\d\\d\\d")); System.out.println("1d32e".matches("\\d\\D\\d\\d\\D")); //\\s表示是否是空白字符 System.out.println("1 2 d".matches("\\d\\s\\s\\d\\s\\sd")); //\\w表示常用输入字符:a-z,A-Z,0-9,_ System.out.println("aa b1 22".matches("\\w\\w\\s\\w\\w\\s\\w\\w")); //[abcd]表示是否是abcd这是个字符中的某一个 System.out.println("a".matches("[abcd]")); //[a-z]表示是否是a-z之间的字符 System.out.println("D".matches("[a-zA-D]")); //[^a-z]表示不在a-z之间 System.out.println("h".matches("[^a-z]")); //也支持&&和|| System.out.println("a".matches("[a-z&&[def]]")); System.out.println("H".matches("[a-z]||[A-D]"));
* ? + 的使用
X? X,表示0个或1个
//?表示0个或者1个 System.out.println("a".matches("a?")); System.out.println("aa".matches("a?")); System.out.println("".matches("a?"));X* X, 表示0个或多个
System.out.println("aaaa".matches("a*")); //为false因为*表示的多个 System.out.println("abcd".matches("a*")); System.out.println("abcdlskdjff".matches("a[a-z]*")); //为true System.out.println("".matches("a*"));X+ X,表示1个或多个
//+表示1个或者多个 System.out.println("aa".matches("a+")); System.out.println("a".matches("a+")); //false,+表示一个或者多个 System.out.println("".matches("a+"));
范围匹配
X{n} X, 表示出现n次
System.out.println("26666".matches("\\d{5}"));X{n,} X, 表示至少出现n次
System.out.println("2666645333".matches("\\d{5,}"));X{n,m} X, 表示至少出现n次最多出现m次
//{n,m}表示至少出现n次最多出现m次 System.out.println("2kk3-12-22".matches("\\d{4}-\\d{1,2}-\\d{1,2}"));[abc] a, b, or c (simple class)
几个常用的例子:
//第一个:检测一个字符串是否是数字
System.out.println("2334.99".matches("\\d+\\.?\\d+"));
System.out.println("38".matches("\\d{1}||[12]{1}\\d{1}|3{1}[0-5]{1}"));
//第二个:检测一个字符串是否是一个电话号码0870-2233445-01
System.out.println("0870-2233445-01".matches("\\d{3,4}-\\d{7}-\\d{2,5}"));
System.out.println("0870-2233445".matches("\\d{3,4}-\\d{7}-*\\d*"));
//第三个:匹配一个IP地址 111.22.33.22
System.out.println("192.168.0.33".matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"));
System.out.println("92".matches("[1-2]?\\d{0,2}"));
System.out.println("192.234.22.33".matches("[1-2]?\\d{0,2}\\.[1-2]?\\d{0,2}\\.[1-2]?\\d{0,2}\\.[1-2]?\\d{0,2}"));
//第四:匹配一个身份证号
System.out.println("53210119761209005X".matches("\\d{15}||\\d{18}||\\d{17}[X]"));
//匹配一个电子邮件
System.out.println("ynkonghao@gmail-pun.com.ddfdfdf".matches("[\\w-\\.]*\\w+@[\\w\\.-]*\\w+\\.\\w{2,6}"));
^这个元字符有两个用法:
- ^不在[]中就表示以xx为开头
- 在[]中就表示非
$表示以xx为结尾
^和$的结合用法:比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$。
两个重要的类Pattern和Matcher
可以先将一个正则表达式编译成为一个Pattern对象,可以提高效率。通过Pattern可以获取一个Matcher对象,通过Matcher对象可以获取大量的有用信息。
Pattern p = Pattern.compile("\\d{4}");
Matcher m = p.matcher("23338888-3233-1111");
下面介绍一些常用的Matcher的方法:
判断是否匹配:m.matches(),返回一个boolean类型的返回值
将查找的指针重置:m.reset()
顺序匹配相应的字符串:m.find(),返回一个boolean类型的返回值
每进行一次find,就可以将字符串通过group获取:m.group(),返回获取到的字符串
注意:必须在find之后才能执行group,不然会报错!
m.start()和m.end()可以获取匹配字符串的开始位置和结束位置
while(m.find()) {
System.out.println(m.group()+"["+m.start()+","+m.end()+"]");
}
替换
String str = "234445sdsff3444sdss";
//第一个参数是正则表达式,第二参数是要替换的值
System.out.println(str.replaceAll("\\d", "*"));
System.out.println(str.replaceAll("\\d+", "*"));
System.out.println("13222331111".replaceAll("\\d{4}$", "****"));
“\d{4}$”指的是替换后四位!
分组和查找
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
- 分组0对应整个正则表达式
- 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.
String str = "532101197612210039,532122199802120034,534501198212110029,532101780203009"; //使用括号进行分组 Pattern p = Pattern.compile("((\\d{6})(\\d{8}))\\d{4}"); Matcher m = p.matcher(str); while(m.find()) { System.out.println(m.group()); System.out.println("生源地:"+m.group(2)+"出生日期:"+m.group(3)); }
贪婪和非贪婪模式
贪婪模式:指的是.*会匹配所有的信息,此处会找整个信息。
String h = "<table><td>你好</td><td>大家好</td><td>每个人都好</td></table>";
p = Pattern.compile("<td>(.*)</td>");
m = p.matcher(h);
while(m.find()) {
System.out.println(m.group(1));
System.out.println(m.start()+","+m.end());
}
找到的结果:
你好</td><td>大家好</td><td>每个人都好
7,44
非贪婪模式:仅仅只是匹配第一个结尾,特别注意:?接在*+之后就表示使用了非贪婪模式.
String h = "<table><td>你好</td><td>大家好</td><td>每个人都好</td></table>";
p = Pattern.compile("<td>(.*?)</td>");
m = p.matcher(h);
while(m.find()) {
System.out.println(m.group(1));
System.out.println(m.start()+","+m.end());
}
找到的结果:
你好
7,18
大家好
18,30
每个人都好
30,44
接下去会写一点网络爬虫的内容,也算是正则表达式的应用吧。
获取邮箱:
public static List<String> getEmail(String str) {
List<String> es = new ArrayList<String>();
Pattern p = Pattern.compile("[\\w\\.-]*\\w+@[\\w\\.-]*\\w+\\.\\w{2,5}");
Matcher m = p.matcher(str);
while(m.find()) {
es.add(m.group());
}
return es;
}
获取URL:
public static List<String> getUrl(String str) {
List<String> es = new ArrayList<String>();
Pattern p = Pattern.compile("<a.*?\\s+href=['\"]([^\"'>]*?)['\"].*?>(.*?)</a>");
Matcher m = p.matcher(str);
while(m.find()) {
es.add(m.group(1));
}
return es;
附录:(来自于网络 正则表达式30分钟入门教程)
| 代码 | 说明 |
|---|---|
| . | 匹配除换行符以外的任意字符 |
| \w | 匹配字母或数字或下划线或汉字 |
| \s | 匹配任意的空白符 |
| \d | 匹配数字 |
| \b | 匹配单词的开始或结束 |
| ^ | 匹配字符串的开始 |
| $ | 匹配字符串的结束 |
| 代码/语法 | 说明 |
|---|---|
| *重复零次或更多次 | |
| + | 重复一次或更多次 |
| ? | 重复零次或一次 |
| {n} | 重复n次 |
| {n,} | 重复n次或更多次 |
| {n,m} | 重复n到m次 |
| 代码/语法 | 说明 |
|---|---|
| \W | 匹配任意不是字母,数字,下划线,汉字的字符 |
| \S | 匹配任意不是空白符的字符 |
| \D | 匹配任意非数字的字符 |
| \B | 匹配不是单词开头或结束的位置 |
| [^x] | 匹配除了x以外的任意字符 |
| [^aeiou] | 匹配除了aeiou这几个字母以外的任意字符 |
| 分类 | 代码/语法 | 说明 |
|---|---|---|
| 捕获 | (exp) | 匹配exp,并捕获文本到自动命名的组里 |
| (?<name>exp) | 匹配exp,并捕获文本到名称为name的组里,也可以写成(?’name’exp) | |
| (?:exp) | 匹配exp,不捕获匹配的文本,也不给此分组分配组号 | |
| 零宽断言 | (?=exp) | 匹配exp前面的位置 |
| (?<=exp) | 匹配exp后面的位置 | |
| (?!exp) | 匹配后面跟的不是exp的位置 | |
| (?<!exp) | 匹配前面不是exp的位置 | |
| 注释 | (?#comment) | 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读 |