正则表达式
一、为什么要学习正则表达式
当我们面临如下图所示要求之类的要求时,我们就可以使用正则表达式轻松解决
解决方法如下:
先创建一个 Pattern 对象 , 模式对象, 可以理解成就是一个正则表达式对象,然后每个问题 “ 一刀 ” 即可解决
//Pattern pattern = Pattern.compile("[a-zA-Z]+");
//Pattern pattern = Pattern.compile("[0-9]+");
//Pattern pattern = Pattern.compile("([0-9]+)|([a-zA-Z]+)");
//Pattern pattern = Pattern.compile("<a target=\"_blank\" title=\"(\\S*)\"");
再提出几个问题:
面对这样的问题,我们的解决之道就是————正则表达式
二、正则表达式的基本介绍
特别强调的是:
正则表达式不是只有java才有,实际上很多编程语言都支持正则表达式进行字符串操作!
三、正则表达式语法
(一)基本介绍
(二)元字符(Metacharacter)——转义号 \\
下面通过代码来直观地展示转义字符的使用:
(本文代码均为了视觉感受已将部分注释代码打开,需注释部分重定义代码才可运行!)
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 演示转义字符的使用
*/
public class RegExp02 {
public static void main(String[] args) {
String content = "abc$(a.bc(123( )";
//匹配( => \\(
//匹配. => \\.
//String regStr = "\\.";
//String regStr = "\\d\\d\\d";
String regStr = "\\d{3}";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到 " + matcher.group(0));
}
}
}
需要用到转义符号的字符有以下:
(三)元字符——字符匹配符
下面通过代码来展示字符匹配符的使用
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 演示字符匹配符 的使用
*/
public class RegExp03 {
public static void main(String[] args) {
String content = "a11c8abc _ABCy @";
String regStr = "[a-z]";//匹配 a-z之间任意一个字符
String regStr = "[A-Z]";//匹配 A-Z之间任意一个字符
String regStr = "abc";//匹配 abc 字符串[默认区分大小写]
String regStr = "(?i)abc";//匹配 abc 字符串[不区分大小写]
String regStr = "[0-9]";//匹配 0-9 之间任意一个字符
String regStr = "[^a-z]";//匹配 不在 a-z之间任意一个字符
String regStr = "[^0-9]";//匹配 不在 0-9之间任意一个字符
String regStr = "[abcd]";//匹配 在 abcd中任意一个字符
String regStr = "\\D";//匹配 不在 0-9的任意一个字符
String regStr = "\\w";//匹配 大小写英文字母, 数字,下划线
String regStr = "\\W";//匹配 等价于 [^a-zA-Z0-9_]
\\s 匹配任何空白字符(空格,制表符等)
String regStr = "\\s";
\\S 匹配任何非空白字符 ,和\\s刚好相反
String regStr = "\\S";
. 匹配出 \n 之外的所有字符,如果要匹配.本身则需要使用 \\.
String regStr = ".";
//特别说明
//1. 当创建Pattern对象时,指定 Pattern.CASE_INSENSITIVE, 表示匹配是不区分字母大小写.
Pattern pattern = Pattern.compile(regStr/*, Pattern.CASE_INSENSITIVE*/);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到 " + matcher.group(0));
}
}
}
特别说明:
当创建Pattern对象时,指定 Pattern.CASE_INSENSITIVE, 表示匹配是不区分字母大小写.(用法如下)
(四)元字符——选择匹配符
在匹配某个字符串的时候是选择性的,即:既可以匹配这个,又可以匹配那个
(五)元字符——限定符
用于制定其前面的字符和组合项连续出现多少次
用法如下:
(特别说明:限定符 ? 的使用,需要遵守贪婪原则!)
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 演示限定符的使用
*/
public class RegExp05 {
public static void main(String[] args) {
String content = "a211111aaaaaahello";
a{3},1{4},\\d{2}
String regStr = "a{3}";// 表示匹配 aaa
String regStr = "1{4}";// 表示匹配 1111
String regStr = "\\d{2}";// 表示匹配 两位的任意数字字符
a{3,4},1{4,5},\\d{2,5}
//细节:java匹配默认贪婪匹配,即尽可能匹配多的
String regStr = "a{3,4}"; //表示匹配 aaa 或者 aaaa
String regStr = "1{4,5}"; //表示匹配 1111 或者 11111
String regStr = "\\d{2,5}"; //匹配2位数或者3,4,5
//1+
String regStr = "1+"; //匹配一个1或者多个1
String regStr = "\\d+"; //匹配一个数字或者多个数字
//1*
String regStr = "1*"; //匹配0个1或者多个1
//演示?的使用, 遵守贪婪匹配
String regStr = "a1?"; //匹配 a 或者 a1
Pattern pattern = Pattern.compile(regStr/*, Pattern.CASE_INSENSITIVE*/);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到 " + matcher.group(0));
}
}
}
(六)元字符——定位符
定位符,规定要匹配的字符串出现的位置,比如在字符串的开始还是在结束的位置,这个也是相当有用的,必须掌握!
用法如下:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 演示定位符的使用
*/
public class RegExp06 {
public static void main(String[] args) {
String content = "hanshunping sphan nnhan";
String content = "123-abc";
//以至少1个数字开头,后接任意个小写字母的字符串
String regStr = "^[0-9]+[a-z]*";
//以至少1个数字开头, 必须以至少一个小写字母结束
String regStr = "^[0-9]+\\-[a-z]+$";
//表示匹配边界的han[这里的边界是指:被匹配的字符串最后,
// 也可以是空格的子字符串的后面]
String regStr = "han\\b";
//和\\b的含义刚刚相反
String regStr = "han\\B";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到=" + matcher.group(0));
}
}
}
(七)分组
用法如下:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 分组:
*/
public class RegExp07 {
public static void main(String[] args) {
String content = "hanshunping s7789 nn1189han";
//下面就是非命名分组
//说明
// 1. matcher.group(0) 得到匹配到的字符串
// 2. matcher.group(1) 得到匹配到的字符串的第1个分组内容
// 3. matcher.group(2) 得到匹配到的字符串的第2个分组内容
String regStr = "(\\d\\d)(\\d\\d)";//匹配4个数字的字符串
//命名分组: 即可以给分组取名
String regStr = "(?<g1>\\d\\d)(?<g2>\\d\\d)";//匹配4个数字的字符串
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到=" + matcher.group(0));
System.out.println("第1个分组内容=" + matcher.group(1));
System.out.println("第1个分组内容[通过组名]=" + matcher.group("g1"));
System.out.println("第2个分组内容=" + matcher.group(2));
System.out.println("第2个分组内容[通过组名]=" + matcher.group("g2"));
}
}
}
四、应用实例
(一)对字符串进行如下验证
代码实现如下:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 正则表达式的应用实例
*/
public class RegExp10 {
public static void main(String[] args) {
String content = "13588889999";
// 汉字
String regStr = "^[\u0391-\uffe5]+$";
// 邮政编码
// 要求:1.是1-9开头的一个六位数. 比如:123890
// 2.
// 3.
String regStr = "^[1-9]\\d{5}$";
// QQ号码
// 要求: 是1-9开头的一个(5位数-10位数) 比如: 12389 , 1345687 , 187698765
String regStr = "^[1-9]\\d{4,9}$";
// 手机号码
// 要求: 必须以13,14,15,18 开头的11位数 , 比如 13588889999
String regStr = "^1[3|4|5|8]\\d{9}$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if(matcher.find()) {
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
}
}
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 演示正则表达式的使用
*/
public class RegExp11 {
public static void main(String[] args) {
//String content = "https://www.bilibili.com/video/BV1fh411y7R8?from=search&seid=1831060912083761326";
String content = "http://edu.3dsmax.tech/yg/bilibili/my6652/pc/qg/05-51/index.html#201211-1?track_id=jMc0jn-hm-yHrNfVad37YdhOUh41XYmjlss9zocM26gspY5ArwWuxb4wYWpmh2Q7GzR7doU0wLkViEhUlO1qNtukyAgake2jG1bTd23lR57XzV83E9bAXWkStcAh4j9Dz7a87ThGlqgdCZ2zpQy33a0SVNMfmJLSNnDzJ71TU68Rc-3PKE7VA3kYzjk4RrKU";
/**
* 思路
* 1. 先确定 url 的开始部分 https:// | http://
* 2.然后通过 ([\w-]+\.)+[\w-]+ 匹配 www.bilibili.com
* 3. /video/BV1fh411y7R8?from=sear 匹配(\/[\w-?=&/%.#]*)?
*/
String regStr = "^((http|https)://)?([\\w-]+\\.)+[\\w-]+(\\/[\\w-?=&/%.#]*)?$";//注意:[. ? *]表示匹配就是.本身
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if(matcher.find()) {
System.out.println("满足格式");
} else {
System.out.println("不满足格式");
}
//这里如果使用Pattern的matches 整体匹配 比较简洁
System.out.println(Pattern.matches(regStr, content));//
}
}
(二)对一个url进行解析
代码演示如下:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class homework01 {
public static void main(String[] args) {
String content = "http://www.sohu.com:8080/index.htm";
String regStr = "^([a-zA-Z]+)://([a-zA-Z.]+):(\\d+)[\\w-/]*/([\\w.]+)$";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
if(matcher.matches()){
System.out.println("整体匹配: " + matcher.group(0));
System.out.println("协议: " + matcher.group(1));
System.out.println("域名: " + matcher.group(2));
System.out.println("端口: " + matcher.group(3));
System.out.println("文件名: " + matcher.group(4));
}
else{
System.out.println("匹配失败");
}
}
}
运行结果如下:
五、正则表达式的三个常用类
六、分组、捕获、反向引用
(一)提出要求
(二)介绍
(三)看几个小案例
(四)经典的结巴程序
要求:
把 类似 : "我....我要....学学学学....编程 java!";
通过正则表达式 修改成 "我要学编程 java"
代码实现如下:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegExp13 {
public static void main(String[] args) {
String content = "我....我要....学学学学....编程java!";
//1. 去掉所有的.
Pattern pattern = Pattern.compile("\\.");
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("");
System.out.println("content=" + content);
//2. 去掉重复的字 我我要学学学学编程java!
// 思路
//(1) 使用 (.)\\1+
//(2) 使用 反向引用$1 来替换匹配到的内容
// 注意:因为正则表达式变化,所以需要重置 matcher
pattern = Pattern.compile("(.)\\1+");//分组的捕获内容记录到$1
matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println("找到=" + matcher.group(0));
}
//使用 反向引用$1 来替换匹配到的内容
content = matcher.replaceAll("$1");
System.out.println("content=" + content);
//3. 使用一条语句 去掉重复的字 我我要学学学学编程java!
content = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");
System.out.println("content=" + content);
}
}
七、String类中使用正则表达式
(一)替换功能
String 类 public String replaceAll(String regex,String replacement
(二)判断功能
String 类 public boolean matches(String regex){} //使用 Pattern
(三)分割功能
String 类 public String[] split(String regex)
代码实现如下:
public class StringReg {
public static void main(String[] args) {
String content = "2000年5月,JDK1.3、JDK1.4和J2SE1.3相继发布,几周后其" +
"获得了Apple公司Mac OS X的工业标准的支持。2001年9月24日,J2EE1.3发" +
"布。" +
"2002年2月26日,J2SE1.4发布。自此Java的计算能力有了大幅提升";
//使用正则表达式方式,将 JDK1.3 和 JDK1.4 替换成JDK
content = content.replaceAll("JDK1\\.3|JDK1\\.4", "JDK");
System.out.println(content);
//要求 验证一个 手机号, 要求必须是以138 139 开头的
content = "13888889999";
if (content.matches("1(38|39)\\d{8}")) {
System.out.println("验证成功");
} else {
System.out.println("验证失败");
}
//要求按照 # 或者 - 或者 ~ 或者 数字 来分割
System.out.println("===================");
content = "hello#abc-jack12smith~北京";
String[] split = content.split("#|-|~|\\d+");
for (String s : split) {
System.out.println(s);
}
}
}
八、正则表达式资料分享(需要自取)
(一)校验数字的表达式
1 数字:^[0-9]*$
2 n位的数字:^\d{n}$
3 至少n位的数字:^\d{n,}$
4 m-n位的数字:^\d{m,n}$
5 零和非零开头的数字:^(0|[1-9][0-9]*)$
6 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$
7 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$
8 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
9 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
10 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
11 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
12 非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
13 非负整数:^\d+$ 或 ^[1-9]\d*|0$
14 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
15 非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
16 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
17 正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
18 负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
19 浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$
(二)校验字符的表达式
1 汉字:^[\u4e00-\u9fa5]{0,}$
2 英文和数字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
3 长度为3-20的所有字符:^.{3,20}$
4 由26个英文字母组成的字符串:^[A-Za-z]+$
5 由26个大写英文字母组成的字符串:^[A-Z]+$
6 由26个小写英文字母组成的字符串:^[a-z]+$
7 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
8 由数字、26个英文字母或者下划线组成的字符串:^\w+$ 或 ^\w{3,20}$
9 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
10 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
11 可以输入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+
12 禁止输入含有~的字符:[^~\x22]+
(三)特殊需求表达式
1 Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
2 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
3 InternetURL:[a-zA-z]+://[^\s]* 或 ^https://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
4 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
5 电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
6 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
7 身份证号:
15或18位身份证:^\d{15}|\d{18}$
15位身份证:^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$
18位身份证:^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{4}$
8 短身份证号码(数字、字母x结尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$
9 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
10 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
11 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
12 日期格式:^\d{4}-\d{1,2}-\d{1,2}
13 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
14 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
15 钱的输入格式:
16 1.有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$
17 2.这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]*)$
18 3.一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]*)$
19 4.这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
20 5.必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的:^[0-9]+(.[0-9]{2})?$
21 6.这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
22 7.这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
23 8.1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
24 备注:这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
25 xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
26 中文字符的正则表达式:[\u4e00-\u9fa5]
27 双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
28 空白行的正则表达式:\n\s*\r (可以用来删除空白行)
29 HTML标记的正则表达式:<(\S*?)[^>]*>.*?|<.*? /> (网上流传的版本太糟糕,上面这个也仅仅能部分,对于复杂的嵌套标记依旧无能为力)
30 首尾空白字符的正则表达式:^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
31 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
32 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
33 IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
九、总结
结论:
正则表达式是处理文本的一把利器,学会它是每个程序员必经之路。
最后送大家一句话:
纸上得来终觉浅,绝知此事要躬行。自己动动手,敲敲代码,切忌人在床上躺,技术心中长。