Java 正则表达式
什么是正则表达式?
正则表达式(regex):通常被用来检索、替换那些符合某个模式(规则)的文本。
格式:
字符串.matches(匹配规则); // 并且会返回 boolean
元字符
表示字符
元字符 | 匹配规则说明 |
---|---|
. | 匹配任何字符(与行结束符可能匹配也可能不匹配) |
\d | 匹配数字,即 [0-9] |
\D | 匹配非数字,即 [^0-9] |
\s | 匹配空白字符,即 [ \t\n\x0B\f\r] |
\S | 匹配非空白字符即 [^\s] |
\w | 匹配单词字符即 [a-zA-Z_0-9] |
\W | 匹配非单词字符即 [^\w] |
注意:任何预定义字符没有加上数量词之前只能匹配一个字符。
表示数量
元字符 | 匹配规则说明 |
---|---|
? | 一次或一次也没有 |
* | 零次或多次 |
+ | 一次或多次 |
恰好 n 次 | |
至少 n 次 | |
至少 n 次,但不超过 m 次 |
示例:
"123".matches("\\d{1,4}"); // true
表示范围
元字符 | 匹配规则说明 |
---|---|
[abc] | a、b 或 c |
[^abc] | 除了 a、b、c 外的任何字符(\r、\n 为 true) |
[a-zA-Z] | a-z 或 A-Z |
[a-d[m-p]] | (并集)a-d 或者 m-p |
[a-z&&[def]] | (交集)d、e 或 f |
注意:不管范围词多长,没有加上数量词之前只能匹配一个字符。
表示边界
元字符 | 匹配规则说明 |
---|---|
^ | 匹配开头 |
$ | 匹配结尾 |
\b | 表示单词的开始或者结束的边界,不匹配任何字符(包括不匹配空字符) |
示例:
"hello,world".matches("hello\\b,world");
"xxx".matches("[^ts]he"); // 匹配he,但要排除the和she
案例:匹配 QQ 号
要求:
- 必须 5~15 位数字
- 不能以 0 开头
- 必须都是数字
public static void main(String[] args) {
Pattern pattern = Pattern.compile("^[^0]\\d{4,14}$");
Matcher m1 = pattern.matcher("11111");
Matcher m2 = pattern.matcher("012345678912345");
Matcher m3 = pattern.matcher("123456789012345");
if (m1.find()){
System.out.println(m1.group());
}
if (m2.find()){ //fase
System.out.println(m2.group());
}
if (m3.find()){
System.out.println(m3.group());
}
}
前/后向匹配
元字符 | 匹配规则说明 |
---|---|
(?=pattern) | 正向肯定匹配。在任何匹配 pattern 的字符串开始处匹配查找字符串。 |
(?!pattern) | 正向否定匹配。在任何不匹配 pattern 的字符串开始处匹配查找字符串。 |
(?<=pattern) | 反向肯定匹配。与正向肯定匹配类似,只是方向相反。 |
(?<!pattern) | 反向否定匹配。与正向否定匹配类似,只是方向相反。 |
"Windows(?=95|98|NT|2000)" // 能够匹配 Windows2000 中的 Windows,但不能匹配 Windows3.1 中的 Windows
"Windows(?!95|98|NT|2000)" // 不能匹配 Windows2000 中的 Windows,但能匹配 Windows3.1 中的 Windows
"(?<=95|98|NT|2000)Windows" // 能够匹配 2000Windows 中的 Windows,但不能匹配 3.1Windows 中的 Windows
"(?<!95|98|NT|2000)Windows" // 不能匹配 2000Windows 中的 Windows,但能匹配 3.1Windows 中的 Windows
应用
匹配:matches()
案例:编写一个正则表达式匹配手机号
public static void matchPhoneNum(String phone) {
// 只能以 1 开头,第二位为 3、5、8,总长度 11 位
phone.matches("1[358]\\d{9}")? "合法手机号": "非法手机号");
}
案例:匹配固话
public static void matchTelNum(String tel) {
// 区号:首位是 0,长度是 3-5
// 主机号:首位不能是 0,长度是 7-8
System.out.println(tel.matches("0\\d{2,4}-[1-9]\\d{6,7}")? "合法电话号码": "非法电话号码");
}
查找并返回:find()、group()
使用正则查找的步骤:
- 指定为字符串的正则表达式必须首先编译为此类的实例。
- 将得到的正则对象匹配任意的字符串用于创建 Matcher 对象。
- 依照正则表达式,该对象可以与任意字符序列匹配,执行匹配所涉及的所有状态都驻留在元字符中(即所有信息都保留在元字符中),所以多个元字符可以共享同一模式。
经典的调用顺序是:
Pattern p = Pattern.compile("正则"); // Pattern:正则对象
Matcher m = p.matcher("aaaaaab"); // Matcher:元字符对象
元字符要用到的方法:
find()
:通知元字符去匹配字符串,如果查找到符合规则的字符串则返回 true,反之返回 false。group()
:获取符合规则的字符串。
注意:使用 group()、start()、end() 方法时,必须先调用 find() 方法让匹配去查找符合规则的字符串,否则报错。
案例:找出 3 个字母组成的单词
String str = "da jia de jia qi wan bi liao, hai kai xin ma";
// 1. 编写需求的正则表达式。
String reg = "\\b[a-zA-Z]{3}\\b";
// 2. 把字符串的正则编译成Pattern对象。
Pattern p = Pattern.compile(reg);
// 3. 使用正则对象匹配字符串用于创建一个Matcher对象。
Matcher m = p.matcher(str);
// System.out.println("有符合规则的字符串吗?"+m.find()); // 返回true
// System.out.println("获取结果:"+m.group()); // 只返回一个结果
while(m.find()){
System.out.println(m.group()); // 通过循环获取所有结果
}
案例:匹配邮箱
public static void main(String[] args) {
String str = "有事没事请联系:243214@213.com 有事没事请联系:vgvrge@126.net "
+ "有事没事请联系:vgvrge@123.com.cn"
+ "有事没事请联系:dv3_2343@asA.cn";
String reg = "[a-zA-Z1-9]\\w{5,17}@[a-zA-Z0-9]{2,}(\\.(com|cn|net)){1,2}";
// (com|cn|net) 是或者的写法
// (\\.(com|cn|net)){1,2} 表示 \\.(com|cn|net) 这个整体出现 1-2 次
Pattern p = Pattern.compile(reg);
Matcher m = p.matcher(str);
while(m.find()){
System.out.println(m.group());
}
}
切割:split()
案例:按照空格切割
String str = " 不断学习 为了 将来";
String[] arr = str.split(" +"); // 至少出现一次空格
案例:根据重叠次进行切割
如果正则表达式的内容需要被复用,那么需要对正则的内容进行分组
,分组的目的是为了提高正则的复用性,组号从 1 开始。
String str = "大家家明天天玩得得得得开心";
String[] arr = str.split("(.)\\1+"); // 大明玩开心
替换:replaceAll(String regex, String replacement)
- replaceAll(String regex, String replacement):替换全部匹配到的子字符串
- replaceFirst(String regex, String replacement)::替换第一个匹配到的字符串
案例:论坛防止打广告
String str = "如有需要请联系我:13565678964";
String reg = "1[358]\\d{9}";
str.replaceAll(reg, "******");
案例:把重叠词替换成单个单词
String str = "我我要要要做做做项项项项目目";
// 如果需要在 replaceAll 方法正则的外部引用组的内容,那么是使用"$组号"。
str.replaceAll("(.)\\1+", "$1");
**案例:把“我...我我...喜欢欢欢...编编程程程...”转换成“我喜欢编程”
String raw = "我...我我...喜欢欢欢...编编程程程...";
String tmp = raw.replaceAll("\\.", "");
String result = tmp.replaceAll("(.)\\1+", "$1");
System.out.println(result); // 我喜欢编程
案例:反转字母字符
输入任意一个字符串,取出所有字母(顺序不能改变),把大写换成小写,小写换成大写。
String s = "shi1r4FIFI23sdf2VIH";
// 提取所有字母
String letters = s.replaceAll("[^a-zA-Z]+", "");
StringBuilder builder = new StringBuilder();
for (char c:letters.toCharArray()) {
if (c<='z'&&c>='a') {
builder.append((c+"").toUpperCase());
} else {
builder.append((c+"").toLowerCase());
}
}
System.out.println(builder.toString());
匹配开头:lookingAt()
lookingAt() 对前面的字符串进行匹配,只有匹配到的字符串在最前面时才返回 true。
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("22bb23");
m.lookingAt(); // 返回true,因为\d能匹配开头的22
Matcher m2 = p.matcher("bb2223");
m2.lookingAt(); // 返回false,因为\d不能匹配开头的bb
获取匹配索引、分组
获取匹配索引:
Matcher.start():返回匹配到的子字符串在字符串中的索引位置。
Matcher.end():返回匹配到的子字符串的最后一个字符的后一个字符在字符串中的索引位置。
分组:
Matcher.start()、Matcher.end()、Matcher.group() 均有重载方法,它们是 Matcher.start(int i)、Matcher.end(int i)、Matcher.group(int i),专用于分组操作。
Matcher 类还有一个 groupCount() 用于返回有多少组。
注意:
只有当匹配成功时,才可以使用 Matcher.start()、Matcher.end()、Matcher.group() 三个方法,否则会抛出异常,也就是当 matches()、lookingAt()、find() 其中任意一个方法返回 true 时,才可以使用。
示例:
Pattern p = Pattern.comple("([a-z]+)(\\d+)");
Matcher m = p.matcher("aaa2223bb");
m.find(); // 匹配aaa2223
m.groupCount(); // 返回2,因为有两组
m.group(1); // aaa
m.group(2); // 2223
m.start(1); // 返回0,即第一组匹配到的子字符串在字符串中的索引号
m.start(2); // 返回3
m.end(1); // 返回3,即第一组匹配到的子字符串的最后一个字符的后一个字符在字符串中的索引号
m.end(2); // 返回7
示例:将所有的数字提取出来
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher("我的QQ是:123456,我的电话是:654321,我的邮箱是:13312@163.com");
while(m.find()) {
System.out.println(m.group());
System.out.print("start:"+m.start());
System.out.println(" end:"+m.end());
}
执行结果:
123456
start:6 end:12
654321
start:19 end:25
13312
start:32 end:37
163
start:38 end:41
忽略大小写
// 方法一:直接用正则,(?!)表示整体忽略大小写。
// "^d(?!)oc"表示“oc”忽略大小写,"^d((?!)o)c"表示只有“o”忽略大小写
String regex1 = "^(?i)doc$";
System.out.println("doc".matches(regex1)); // true
System.out.println("DoC".matches(regex1)); // true
// 方法二:采用Pattern编译忽略大小写
String regex = "^doc$";
String s = "DoC";
Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
System.out.println(p.matcher(s).find()); // true