正则表达式(Regular Expression)是一个强大的字符串处理工具,可以对字符串进行查找、提取、分割、替换等操作。
正则表达式是一个非常实用的工具,用于匹配字符串的模版。
Java还提供了Pattern和Matcher两个类专门用于提供正则表达式的支持。
String类里提供了如下几个特殊方法:
boolean matches(String regex):判断该字符串是否匹配指定正则表达式。
String replaceAll(String regex, String replacement):返回该字符串中所有匹配正则表达式的子串替换成replacement后的新字符串。
String[] split(String regex):根据给定正则表达式拆分该字符串后得到的字符串数组。
上面这些特殊的方法都依赖于Java提供的正则表达式支持。
下面先列出一些常用的正则表达式的构造摘要。
构造 匹配
--------------------------------------------
1)字符
x 字符x
\\ 反斜线字符
2)字符类
[abc] a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
3)预定义字符类
. 任何字符(与行结束符可能匹配也可能不匹配)
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]
4)边界匹配器
\b 单词边界
\B 非单词边界
\B 非单词边界
5)数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次
1.匹配
使用String类的方法matches(String regex)。
public class RegexDemo {
public static void main(String[] args) {
//字符串是否匹配邮箱格式
String mail = "123yp@sina.com";
String regex = "\\w+@[a-zA-Z]+(\\.[a-zA-Z]+){1,2}";
System.out.println("该字符串是否符合邮箱格式:" + mail.matches(regex));
}
}
2.切割
使用String类的方法split(String regex)。
下面程序将一个绝对路径切割成几个分路径。
public class RegexDemo {
public static void main(String[] args) {
//将下面路径切割成分路径
String path = "c:\\java\\day1\\Hello.java";
//切割成分路径,即将反斜线作为分隔符切割成几部分
//因为是双反斜线,每个反斜线都要用一个反斜线转义,所以正则表达式是"\\\\"
String regex = "\\\\";
String[] paths = path.split(regex);
for(String p : paths){
System.out.println(p);
}
}
}
下面程序将ip地址分成4段
public class RegexDemo {
public static void main(String[] args) {
//将下面ip地址分段
String ip = "192.168.10.10";
//ip地址分段,即将点作为分隔符切割成几部分
//因为在正则表达式中,点代表任意符号,所以要将点转义,而每个反斜线都要用一个反斜线转义,所以正则表达式是"\\."
String regex = "\\.";
String[] ips = ip.split(regex);
for(String i : ips){
System.out.println(i);
}
}
}
下面程序将一个字符串以叠词为分隔符来进行切割
1)叠词就是多个连续的相同字母。
2)这里要用到正则表达式中组的概念,组用一对小括号表示,“()”,小括号中是每个组的规则。每个组都有编号。
获取组的个数与每个组其自身编号的技巧:出现多少个左括号“(”就有几个组,并且左括号出现的次数即为该组的编号,如:((())())中有4组。
3)在同一个正则表达式字符串中,使用“\\n”来表示第n个组的引用(“\n代表引用第n组,但是反斜线也要转义”)。而在不同表达式中,使用美元符号“&n”来表示引用前一个字符串中第n个组。
所以叠词的正则表达式写法:([a-zA-Z])\\1+。
[a-zA-Z]代表任意一个字符的引用;([a-zA-Z])代表将这个字符作为一个规则,变成一个组;\\1代表引用第1组的规则,即和前一个字符相同的字符;+代表出现一次或多次。
public class RegexDemo {
public static void main(String[] args) {
//将下面字符串使用叠词作为分隔符来切割
String str = "abcdekkabcdqqfakcls";
//叠词的正则表达式
String regex = "([a-zA-Z])\\1+";
String[] strs = str.split(regex);
for(String s : strs){
System.out.println(s);
}
}
}
3.替换
使用String类的方法replaceAll(String regex, String replacement)。
public class RegexDemo {
public static void main(String[] args) {
//将下面语句变成“我要学编程”
String str = "我我..要要.....学...学学学..编编编...编编...编编程...";
//先将多余的省略号去掉
str = str.replaceAll("\\.+", "");
//将重复的字去掉
//这里用到组的概念。每个任意的字为一个组
//多个连续重复的字的正则表达式为"(.)\\1+"
//替换成一个单一的字,即要引用前一个字符串中的组,用"$1"
str = str.replaceAll("(.)\\1+", "$1");
System.out.println(str);
}
}
4.获取
Java提供了Pattern和Matcher类来使用正则表达式。
Pattern对象是正则表达式编译后在内容中的表示形式,而Matcher对象代表执行匹配所涉及的状态保留在其中。
使用步骤:
1)将正则表达式封装成对象。
2)将正则对象和要匹配的字符串相关联。
3)关联后,获取匹配器。
4)通过匹配器对符合规则的子串进行操作。
Pattern类没有构造器。
方法:
static Pattern compile(String regex):返回Pattern对象,将给定的正则表达式编译到模式中。
Matcher matcher(CharSequence input):返回一个Matcher对象,将给定的正则表达式编译到具有给定标志的模式中。这个方法将正则表达式和字符串关联。
Matcher类,一般通过Pattern对象的matcher方法返回一个Matcher对象。
方法:
boolean matches():尝试将整个区域与模式匹配。即返回整个目标字符串与正则表达式是否匹配。
boolean find():尝试查找与该模式匹配的输入序列的下一个子序列。
String group():返回由以前匹配操作所匹配的输入子序列。与find方法一起使用。
int start():返回以前匹配的初始索引。
int end();返回最后匹配字符之后的偏移量。
public class RegexDemo {
public static void main(String[] args) {
//查找字符串中是3个字母组成的单词
String str = "ming tian jiu yao fang jia la";
//正则表达式,\b代表单词边界
String regex = "\\b[a-zA-Z]{3}\\b";
//将正则表达式封装成对象
Pattern p = Pattern.compile(regex);
//将正则表达式和字符串关联并获取匹配器
Matcher m = p.matcher(str);
//查找
while(m.find()){
System.out.println(m.group());
}
}
}
练习1. 将字符串中的ip地址按分组顺序排序
public class RegexDemo {
public static void main(String[] args) {
//按字符串的比较来判断
String ip = "192.168.0.1 10.10.10.10 8.30.5.7 102.49.7.89 2.2.2.2";
//在每个ip地址段前补两个零,使得每段地址至少为3位
ip = ip.replaceAll("(\\d+)", "00$1");
System.out.println(ip);
//将每段地址多余的0去掉,使得每段地址只有3位
ip = ip.replaceAll("0+(\\d{3})", "$1");
System.out.println(ip);
//将ip地址切割成为一个单独的ip地址
String[] ips = ip.split(" ");
//遍历数组
//将数组元素按照字符串的自然顺序将其存到TreeSet中,这样也就进行了按自然顺序的排序
TreeSet<String> set = new TreeSet<String>();
for(String i : ips){
set.add(i);
}
//将每段地址前面多余的0去掉并打印
for(String i : set){
System.out.println(i.replaceAll("0+(\\d+)", "$1"));
}
}
}
练习2 网页爬虫:获取某网页所有邮箱地址。
public class RegexDemo {
public static void main(String[] args) throws Exception{
//获取网页链接
URL url = new URL("http://book.douban.com/subject/3072831/discussion/1295004/");
URLConnection conn = url.openConnection();
//输入流
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
//邮箱地址正则表达式
String regex = "\\w+@[a-zA-Z]+(\\.[a-zA-Z]+){1,2}";
//将正则表达式封装成对象
Pattern p = Pattern.compile(regex);
String line = null;
while((line = in.readLine()) != null){
//将正则表达式与字符串相关联
Matcher m = p.matcher(line);
//查找
while(m.find()){
System.out.println(m.group());
}
}
}
}