Java 学习之路 之 正则表达式 (三十)

正则表达式是一个强大的字符串处理工具,可以对字符串进行查找、提取、分割、替换等操作。String 类里也提供了如下几个特殊的方法。

boolean matches(String regex):判断该字符串是否匹配指定的正则表达式。

String replaceAll(String regex, String replacement):将该字符串中所有匹配 regex 的子串替换成 replacement。

String[] split(String regex):以 regex 作为分隔符,把该字符串分割成多个子串。

上面这些特殊的方法都依赖于 Java 提供的正则表达式支持,除此之外,Java 还提供了 Pattern 和 Mathcer 两个类专门用于提供正则表达式支持。

很多读者都会觉得正则表达式是一个非常神奇、高级的知识,其实正则表达式是一种非常简单而且非常实用的工具。正则表达式是一个用于匹配字符串的模板。可以说,我们定义的任意字符串都可以当成正则表达式实用,例如“abc”,它也是一个正则表达式,只是它只能匹配“abc”字符串。

如果正则表达式仅能匹配“abc”这样的字符串,那么正则表达式也就不值得我们学习了。

1,创建正则表达式

前面已经介绍了,正则表达式就是一个用于匹配字符串的模板,可以匹配一批字符串,所以创建正则表达式就是创建一个特殊的字符串。

除此之外,正则表达式中有一些特殊字符,这些特殊字符在正则表达式中有其特殊的用途,比如前面介绍的反斜线(\)。如果需要匹配这些特殊字符,就必须首先将这些字符转义,也就是在前面添加一个反斜线(\)。正则表达式中的特殊字符如表7.2 所示。


上面的正则表达式依然只能匹配单个字符,这是因为还未在正则表达式中使用“通配符”,“通配符”是可以匹配多个字符的特殊字符。正则表达式中的“通配符”远远超出了普通通配符的功能,被预定义字符,正则表达式支持如表 7.3 所示的预定义字符。


上面的 7 个预定义字符其实很容易记忆 --- d 是 digit 的意思,代表数字;s 是 space 的意思,代表空白;w 是 word 的意思,代表单词。d、s、w 的大写形式恰好匹配与之相反的字符。

有了上面的预定义字符后,我们就可以创建更强大的正则表达式。例如:

c\wt //可以匹配cat、cbt、cct、b0t、c9t 等一批字符串
\d\d\d-\d\d\d-\d\d\d\d //匹配如 000-000-0000 形式的电话号码
在一些特殊情况下,例如,若只想匹配 a~f 的字母,或者匹配除了 ab 之外的所有小写字符,或者匹配中文字符,上面这些预定义字符就无能为力了,此时就需要使用方括号表达式,方括号表达式有如表 7,4 所示的几种形式。

方括号表达式比前面的预定义字符灵活多了,几乎可以匹配任何字符。例如、若需要匹配所有的中文字符、就可以利用[\\u0041-\\u0056]形式 ---- 因为所有中文字符的 Unicode 值是连续的,只要找出所有中文字符中最小、最大的 Unicode 值,就可以利用上面形式来匹配所有的中文字符。

正则表示还支持圆括号表达式,用于将多个表达式组成一个子表达式,圆括号中可以使用或运算符(|)。例如,正则表达式“(public | protected | private)” 用于匹配 Java 的三个访问控制符其中之一。

除此之外,Java正则表达式还支持如表 7.5 所示的几个边界匹配符。


前面例子中需要建立一个匹配 000-000-0000 形式的电话号码时,我们使用了 \d\d\d-\d\d\d-\d\d\d\d 正则表达式,这看起来比较烦琐。实际上,正则表达式还提供了数量标识符,正则表达式支持的梳理标识符有如下几种模式。

Greedy(贪婪模式):数量表式符默认采用贪婪模式,除非另有表示。贪婪模式的表达式会一直匹配下去,直到无法匹配为止。如果你发现表达式匹配的结果与预期的不符,很有可能是因为 --- 你以为表达式只会匹配前面几个字符,而实际上它是贪婪模式,所以会一直匹配下去。

Reluctant(勉强模式):用问号后缀(?)表示,它只会匹配最少的字符。也称为最小匹配模式。

Possessive(占有模式):用加号后缀(+)表示,目前只有 Java 支持占有模式,通常比较少用。

三种模式的数量表示符如表 7.6 所示。

2,使用正则表达式

一旦在程序中定义了正则表达式,就可以使用 Pattern 和 Matcher 来使用正则表达式。

Pattern 对象是正则表达式编译后再内存中的表示形式,因此,正则表达式字符串必须先被编译为 Pattern 对象,然后再利用该 Pattern 对象创建对应的 Matcher 对象。执行匹配所涉及的状态保留在 Matcher 对象中,多个 Matcher 对象可共享同一个 Pattern 对象。

因此,典型的调用顺序如下:

//将一个字符串编译成 Pattern 对象
Pattern p = Pattern.compile("a*b");
//使用 Pattern 对象创建 Matcher 对象
Matcher m = p.matcher("aaaab");
boolean b = m.matches(); //返回 true

上面定义的 Pattern 对象可以多次重复使用。如果某个正则表达式仅需一次使用,则可直接使用 Pattern 类的静态 matches 方法,此方法自动把指定字符串编译成匿名的 Pattern 对象,并执行匹配,如下所示。

boolean b = Pattern.matches("a*b", "aaaab");  //返回 true

上面语句等效于前面的三条语句。但采用这种语句每次都需要重新编译新的 Pattern 对象,不能重复利用已编译的 Pattern 对象,所以效率不高。

Pattern 是不可变类,可供多个并发线程安全使用。

Matcher 类提供了如下几个常用方法。

find():返回目标字符串中是否包含与 Pattern 匹配的子串。

group():返回上一次与 Pattern 匹配的子串。

start():返回上一次与 Pattern 匹配的子串在目标字符串中的开始位置。

end():返回上一次与 Pattern 匹配的子串在目标字符串中的结束位置加 1.

lookingAt():返回目标字符串前面部分与 Pattern 是否匹配。

matches():返回整个目标字符串与 Patter 是否匹配。

reset():将现有的 Matcher 对象应用于一个新的字符序列。

在 Pattern、Matcher 类的介绍中经常会看到一个 CharSequence 接口,该接口代表一个字符序列,其中 CharBuffer、String、StringBuffer、StringBuilder 都是它的实现类。简单地说,CharSequence 代表一个各种表示形式的字符串。

通过 Matcher 类的 find() 和 group() 方法可以从目标字符串中依次去除特定子串(匹配正则表达式的子串),下面的例子程序示范了这种用途。

package com.demo;

import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class FindGroup {
	public static void main(String[] args) {
		//创建一个 Pattern 对象,并用它建立一个 Matcher 对象
		Matcher m = Pattern.compile("\\w+").matcher("Java is very easy!");
		while(m.find()){
			System.out.println(m.group());
		}
		int i = 0;
		while(m.find(i)){
			System.out.println(m.group() + "\t");
			i++;
		}
	}
}
运行上面程序,看到如下运行结果:
Java
is
very
easy
Java	
ava	
va	
a	
is	
is	
s	
very	
very	
ery	
ry	
y	
easy	
easy	
asy	
sy	
y	
从上面运行结果可以看出,find() 方法依次查找字符串中与 Pattern 匹配的子串,一旦找到对应的子串,下次调用 find() 方法时将接着向下查找。除此之外,find() 方法还可以传入一个 int 类型的参数,带 int 参数的 find() 方法从该 int 索引处向下搜索。

start() 和 end() 方法主要用于确定子串在目标字符串中的位置,如下程序所示。

package com.demo;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StartEnd {
	public static void main(String[] args) {
		//创建一个 Pattern 对象,并用它建立一个 Matcher 对象
		String regStr = "Java is very easy";
		System.out.println("目标字符串是:" + regStr);
		Matcher m = Pattern.compile("\\w+").matcher(regStr);
		while(m.find()){
			System.out.println(m.group() + "子串的起始位置:" + m.start() +",其结束位置:" + m.end());
		}
	}
}
上面程序使用 find()、group() 方法逐项取出目标字符串中与指定正则表达式匹配的子串,并使用 start()、end() 方法返回子串在目标字符串中的位置。运行上面程序,看到如下运行结果:

目标字符串是:Java is very easy
Java子串的起始位置:0,其结束位置:4
is子串的起始位置:5,其结束位置:7
very子串的起始位置:8,其结束位置:12
easy子串的起始位置:13,其结束位置:17
matches() 和 lookingAt() 方法有点相似,只是 matches() 方法要求整个字符串和 Pattern 完全匹配时才返回 true,而 lookingAt() 只要字符串以 Pattern 开头就会返回 true。reset() 方法可将现有的 Matcher 对象应用于新的字符序列。看如下的例子程序。

package com.demo;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MatchesTest {
	public static void main(String[] args) {
		String[] mails = {
				"kongyeeku@163.com",
				"kongyeeku@gmail.com",
				"ligang@crazyit.org",
				"wawa@abc.xx"
		};
		String mailRegEx = "\\w{3,20}@\\w+\\.{com|org|cn|net|gov}";
		Pattern mailPattern = Pattern.compile(mailRegEx);
		Matcher matcher = null;
		for(String mail : mails){
			if(matcher == null){
				matcher = mailPattern.matcher(mail);
			}else{
				matcher.reset(mail);
			}
			String result = mail + (matcher.matches() ? "是":"不是" + "一个有效的邮件地址!");
			System.out.println(result);
		}
	}
}
上面程序创建了一个邮件地址的 Pattern,接着用这个 Pattern 与多个邮件地址进行匹配。当程序中的 Matcher 为 null 时,程序调用 matcher() 方法来创建一个 Matcher 对象,一旦 Matcher 对象被创建,程序就调用 Matcher 的 reset() 方法将该 Matcher 应用于新的字符序列。

事实上,String 类里也提供了 matches() 方法,该方法返回该字符串是否匹配指定的正则表达式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值