正则表达式(RegularExpressions)
正则表达式是一个特殊的字符序列,一个字符串是否与我们所设定的这样的字符序列相匹配。
用途:
字符串匹配(字符匹配)
字符串查找
字符串替换
例如:
IP地址是否正确
从网页提取email地址
从网页提取链接等
类:
java.lang.String
java.util.regex.Pattern:作用在于编译正则表达式后创建一个匹配模式
java.util.regex.Matcher:使用Pattern实例提供的模式信息对正则表达式进行匹配
一、正则表达式引出
先看一个小问题:如果判断一个字符串是否全部由数字组成
解法一:
public class TestDemo {
public static void main(String[] args) {
String string = "1234929w042210";
if (isNumber(string)) {
System.out.println("字符串由数字组成");
}
else {
System.out.println("字符串不是全部由数字组成");
}
}
public static boolean isNumber(String str){
char[] arr = str.toCharArray();
for (int i = 0; i < arr.length; i++) {
if (arr[i] < '0' || arr[i] > '9') {
return false;
}
}
return true;
}
}
解法二:
public class TestDemo {
public static void main(String[] args) {
String string = "123492w9042210";
if (isNumber(string)) {
System.out.println("字符串由数字组成");
}
else {
System.out.println("字符串不是全部由数字组成");
}
}
public static boolean isNumber(String str){
if (str.matches("\\d+")) {
return true;
}
else {
return false;
}
}
}
相比较解法一,解法二是不是更加简单呢?解法二就是用到了正则表达式的概念。
先用一个小程序初步认识一下正则表达式的概念
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
//简单认识正则表达式的概念
public static void main(String[] args) {
p("abc".matches("..."));//true
p("a8729a".replaceAll("\\d","-"));//a----a
Pattern pattern = Pattern.compile("[a-z]{3}");
Matcher matcher = pattern.matcher("fgs");
p(matcher.matches());//true
//可以用一句话写出
p("fgs".matches("[a-z]{3}"));//true
//或者
p(Pattern.matches("[a-z]{3}", "fgs"));//true
}
public static void p(Object o) {
System.out.println(o);
}
}
二、Pattern类
常用正则标记
1.字符:匹配单个字符
(1)字符x
:表示匹配字符x;
(2)\\
:匹配转义字符“\”;
(3)\t
:匹配转义字符“\t”;
(4)\n
:匹配转义字符“\n”;
栗子:
public class TestDemo {
public static void main(String[] args) throws Exception{
String string = "d";
System.out.println(string.matches("d"));//true
System.out.println(string.matches("D"));//false
System.out.println(string.matches("e"));//false
}
}
结果:
true
false
false
2.字符集(字符类)
(1)[abc]
:表示可能是字符a,可能是字符b或者是字符c中的任意一个;
(2)[^abc]
:表示不是字母a、字母b、字母c的任意一个(否定);
(3)[a-z]
:表示全部小写字母中的任意一个(范围);
(4)[a-zA-Z] [a-z[A-Z]] [a-z]|[A-Z]
:表示全部字母中的任意一个(范围);
(5)[0-9]
:表示全部数字的任意一个(范围);
(6)[a-z&&[def]]
:d、e 或f(交集);
(7)[a-z&&[^bc]]
:a到z,除了b和c(减去);
(8)[a-z&&[^m-p]]
:a到z,而非m到p(减去);
栗子:
public class TestDemo {
public static void main(String[] args) throws Exception {
String str1 = "a";
System.out.println(str1.matches("[abc]"));//匹配
System.out.println(str1.matches("[^abc]"));//不匹配
System.out.println(str1.matches("[a-z]"));//匹配
System.out.println(str1.matches("[a-zA-Z]"));//匹配
String str2 = "1";
System.out.println(str2.matches("[0-9]"));//匹配
}
}
结果:
true
false
true
true
true
3.简写表达式(预定义字符类)
(1).
:表示任意的一位字符;
(2)\d
:表示任意的一位数字,等价于“[0-9]”
;
(3)\D
:表示任意的一位非数字,等价于“[^0-9]
”;
(4)\w
:表示任意的一位字母、数字、_,等价于“[a-zA-Z0-9_]
”;
(5)\W
:表示任意的一位非字母、数字、_,等价于“[^a-zA-Z0-9_]
”;
(6)\s
:表示任意的一位空格,例如:\n
、\t
等;
(7)\S
:表示任意的一位非空格;
栗子1:
public class TestDemo {
public static void main(String[] args) throws Exception {
String str1 = "a";
System.out.println(str1.matches("."));//匹配
System.out.println(str1.matches("\\w"));//匹配
System.out.println(str1.matches("\\W"));//不匹配
System.out.println(str1.matches("\\s"));//不匹配
System.out.println(str1.matches("\\S"));//匹配
String str2 = "1";
System.out.println(str2.matches("\\d"));//匹配
System.out.println(str2.matches("\\D"));//不匹配
System.out.println(str2.matches("\\w"));//匹配
System.out.println(str2.matches("\\W"));//不匹配
}
}
结果1:
true
true
false
false
true
true
false
true
false
栗子2:
public class TestDemo {
public static void main(String[] args) throws Exception{
System.out.println(" \n\r\t".matches("\\s{4}"));//true
System.out.println(" ".matches("\\S"));//false
System.out.println("a_4".matches("\\w{3}"));//true
System.out.println("abc888&^%".matches("[a-z]{1,3}\\d+[&^#%]+"));//true
System.out.println("\\".matches("\\\\"));//true
}
}
结果2:
true
false
true
true
true
这里尤其要注意最后一个
System.out.println("\\".matches("\\\\"));//true
4.数量表示
之前的所有的正则都只是表示一位,如果要想表示多位,就要用数量来表示
(1)正则表达式?
:此正则出现0次或1次;
(2)正则表达式*
:此正则出现0次、1次或多次;
(3)正则表达式+
:此正则出现1次或多次;
(4)正则表达式{n}
:此正则出现正好n次;
(5)正则表达式{n,}
:此正则出现n次及其以上;
(6)正则表达式{n,m}
:此正则出现n ~ m次(包括n,m)
栗子:
public class TestDemo {
public static void main(String[] args) throws Exception {
String str1 = "a";
System.out.println(str1.matches("[a-z]?"));//匹配
System.out.println(str1.matches("[a-z]+"));//匹配
System.out.println(str1.matches("[a-z]{1}"));//匹配
System.out.println(str1.matches("[a-z]{2}"));//不匹配
String str2 = "abc";
System.out.println(str2.matches("[a-z]*"));//匹配
System.out.println(str2.matches("[a-z]+"));//匹配
System.out.println(str2.matches("[a-z]{3}"));//匹配
System.out.println(str2.matches("[a-z]{3,}"));//匹配
String str3 = "abcde";
System.out.println(str3.matches("[a-z]{5}"));//匹配
System.out.println(str3.matches("[a-z]{5,}"));//匹配
System.out.println(str3.matches("[a-z]{1,10}"));//匹配
}
}
结果:
true
true
true
false
true
true
true
true
true
true
true
5.逻辑运算符
(1)正则表达式A正则表达式B
:表达式A之后紧跟着表达式B;
(2)正则表达式A|正则表达式B
:表示表达式A或者是表达式B,二者任一满足即可;
(3)(正则表达式)
:将多个子表达式合成一个表示,作为一组出现。
6.边界匹配器
(1)^:行的开头;
(2)$
:行的结尾;
(3)\b
:单词边界;
(4)\B
:非单词边界;
(5)\A
:输入的开头;
(6)\G
:上一个匹配的结尾
栗子:
public class TestDemo {
public static void main(String[] args) throws Exception{
System.out.println("hello sir".matches("^h.*"));//true
System.out.println("hello sir".matches(".*ir$"));//true
System.out.println("hello sir".matches("^h.[a-z]{2}o\\b.*"));//true
System.out.println("hellosir".matches("^h.[a-z]{1,3}o\\b.*"));//false
System.out.println(" \n".matches("^[\\s&&[^\\n]]*\\n$"));//true
}
}
结果:
true
true
true
false
true
7.Greedy Reluctant Possessive的区别
这三者看似差不多,但是实际上有什么区别呢?
1.greedy(默认)
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestDe {
//greedy
public static void main(String[] args) {
Pattern pattern = Pattern.compile(".{3,10}[0-9]");
Matcher matcher = pattern.matcher("aaaa3nnnn8");
if (matcher.find()) {
System.out.println(matcher.start()+"-"+matcher.end());
}
else {
System.out.println("not match!");
}
}
}
结果:
0-10
分析:greedy贪婪的,是因为匹配器被强制要求第一次尝试匹配时读入整个输入串,如果第一次尝试匹配失败,则从后往前逐个字符地回退并尝试再次匹配,知道匹配成功或没有字符可回退。
2.reluctant:不情愿的,勉强的。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestDe {
//reluctant
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(.{3,10}?)[0-9]");
Matcher matcher = pattern.matcher("aaaa3nnnn8");
if (matcher.find()) {
System.out.println(matcher.start()+"-"+matcher.end());
}
else {
System.out.println("not match!");
}
}
}
结果:
0-5
差不多的例子:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestDe {
//reluctant
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(.{3,10}?)[0-9]");
Matcher matcher = pattern.matcher("aaaa3nnnn8");
while (matcher.find()) {
System.out.println(matcher.start()+"-"+matcher.end());
}
}
}
结果:
0-5
5-10
分析:reluctant,勉强的,所以特别不情愿。它与greedy相反,它从输入串的第一个位置开始,在尝试匹配查找中只勉强地读一个字符,直到尝试完整个字符
3.possessive:独占的
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestDe {
//possessive
public static void main(String[] args) {
Pattern pattern = Pattern.compile("(.{3,10}+)[0-9]");
Matcher matcher = pattern.matcher("aaaa3nnnn8");
if (matcher.find()) {
System.out.println(matcher.start()+"-"+matcher.end());
}
else {
System.out.println("not match!");
}
}
}
结果:
not match!
分析:possessive总是读入整个输入串,尝试一次(仅且一次)匹配成功,从不回退,即便这样做也坑内使整体匹配成功。
三、Matcher类
http://tool.oschina.net/apidocs/apidoc?api=jdk-zh
常用方法:
- boolean matches() 最常用方法:尝试对整个目标字符展开匹配检测,也就是只有整个目标字符串完全匹配时才返回真值.
- boolean find() 对字符串进行匹配,匹配到的字符串可以在任何位置
- boolean lookingAt() 对前面的字符串进行匹配,只有匹配到的字符串在最前面才会返回true
- int start() 返回当前匹配到的字符串在原目标字符串中的位置
- int end() 返回当前匹配的字符串的最后一个字符在原目标字符串中的索引位置.
- String group() 返回匹配到的子字符串
- reset()给当前的matcher对象配上新的目标,目标就是该方法的参数,如果不给参数,reset就会把matcher设到当前字符串的开始处。
package rexExp;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestDemo {
public static void main(String[] args) throws Exception{
Pattern pattern = Pattern.compile("\\d{3,5}");
String string = "123-54-2324-92";
Matcher matcher = pattern.matcher(string);
System.out.println(matcher.matches());//false,匹配失败
//虽然匹配失败了,但是由于string里的"123"和pattern是匹配的,所以下次的匹配从7开始
//测试匹配位置
System.out.println(matcher.find());//true
System.out.println(matcher.group());//2324
System.out.println(matcher.start()+"-"+matcher.end());//7-11
//使用reset方法重置匹配位置
matcher.reset();
//第一次find匹配以及匹配的目标和匹配的起始位置
System.out.println(matcher.find());//true
System.out.println(matcher.group());//123
System.out.println(matcher.start()+"-"+matcher.end());//0-3
//第二次find匹配以及匹配的目标和匹配的起始位置
System.out.println(matcher.find());//true
System.out.println(matcher.group());//2324
System.out.println(matcher.start()+"-"+matcher.end());//7-11
//没有匹配成功
System.out.println(matcher.find());//false
//第一次lookingAt匹配以及匹配的目标和匹配的起始位置
System.out.println(matcher.lookingAt());//true
System.out.println(matcher.group());//123
System.out.println(matcher.start()+"-"+matcher.end());//0-3
//第二次lookingAt匹配以及匹配的目标和匹配的起始位置
System.out.println(matcher.lookingAt());//true
System.out.println(matcher.group());//123
System.out.println(matcher.start()+"-"+matcher.end());//0-3
//两次匹配的都是"123",说明总是从第一个字符进行匹配
System.out.println(matcher.find());//true???思考一下这里为什么是true,因为前面是lookingAt
System.out.println(matcher.group());//2324
}
}
false
true
2324
7-11
true
123
0-3
true
2324
7-11
false
true
123
0-3
true
123
0-3
true
2324
matches(),find(),lookingAt()三个方法的区别:
matches():整个匹配,只有整个字符序列完全匹配成功,才返回true,否则返回false。但是如果前部分匹配成功,将移动下次匹配的位置。
lookingAt:部分匹配,总是从第一个字符进行匹配,匹配成功了不再继续匹配,匹配失败了,也不继续匹配。
find:部分匹配,从当前位置开始匹配,找到一个匹配的子串,将移动下次匹配的位置。
分组
四、String类对正则的支持
在jdk1.4之后,String类对正则有了直接的方法支持
(1)与指定正则匹配:
public boolean matches(String regex)
(2)替换满足指定正则的全部内容:
public String replaceAll(String regex, String replacement)
(3)替换满足指定正则的首个内容:
public String replaceFirst(String regex, String replacement)
(4)按照指定正则全拆分:
public String[] split(String regex)
(5)按照指定的正则拆分为指定个数:
public String[] split(String regex, int limit)
Pattern类之中存在的方法:
(1)字符串全拆分:public String[] split(CharSequence input)
;
(2)字符串部分拆分:public String[] split(CharSequence input, int limit);
Matterh类之中存在的方法:
(1)字符串匹配:public boolean matches()
;
(2)字符串全替换:public String replaceAll(String replacement)
;
(3)字符串替换首个:public String replaceFirst(String replacement)
。
实例1:字符串拆分(遇到字符拆)
public class TestDemo {
public static void main(String[] args) throws Exception {
String str = "a1bb2ccc3dddd4eeeee5fffffff6ggggggg7";
String regex = "[a-zA-Z]+";// 字母出现1次或多次
System.out.println(str.replaceAll(regex, ""));
System.out.println(str.replaceFirst(regex, ""));
}
}
运行结果:
1234567
1bb2ccc3dddd4eeeee5fffffff6ggggggg7
实例2:字符串拆分(遇到数字拆)
public class TestDemo {
public static void main(String[] args) throws Exception {
String str = "a1bb2ccc3dddd4eeeee5fffffff6ggggggg7";
String regex = "\\d";// 数字出现1次,[0-9]
String result[] = str.split(regex);
for (int x = 0; x < result.length; x++) {
System.out.print(result[x] + "、");
}
}
}
运行结果:
a、bb、ccc、dddd、eeeee、fffffff、ggggggg、
有了正则,对于字符串的种种操作就会变的相当方便。而正则在使用的过程之中最为重要的部分就是验证部分,因为一些字符串必须满足于指定的格式才可以操作。
实例3:做一个简单的验证,要求字符串格式“aaaaab”,在b之前至少1个a。
public class TestDemo {
public static void main(String[] args) throws Exception {
String str = "aaaaaab";
String regex = "a+b";
System.out.println(str.matches(regex));
}
}
运行结果:
true
实例4:验证字符串是否是小数,如果是则将其变为double型数据
public class TestDemo {
public static void main(String[] args) throws Exception {
String str = "123.1";
String regex = "\\d+(\\.\\d+)?";
if (str.matches(regex)) { // 符合于验证要求
double data = Double.parseDouble(str); // 字符串变为double型数据
System.out.println(data);
} else {
System.out.println("字符串不是数字所组成!");
}
}
}
运行结果:
123.1
实例5:一个用户名只能由字母、数字、_所组成,其长度只能是6~15位。
public class TestDemo {
public static void main(String[] args) throws Exception {
String str = "343234FDSF_";
String regex = "\\w{6,15}";
if (str.matches(regex)) { // 符合于验证要求
System.out.println("用户名合法。");
} else {
System.out.println("用户名非法!");
}
}
}
运行结果:
用户名合法。
实例6:replacement的应用
1.将所有的"java"串,不管大小写都转换成大写
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestDemo {
public static void main(String[] args) {
//CASE_INSENSITIVE表示不分大小写
Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher("java Java JAVa JaVa IloveJAVA you hate Java");
//1.将"java",不管大小写,都转换成大写
//在前面用上CASE_INSENSITIVE
//先找出所有符合的子串
// while (matcher.find()) {
// System.out.println(matcher.group());
// }
String string = matcher.replaceAll("JAVA");
System.out.println(string);
}
}
JAVA JAVA JAVA JAVA IloveJAVA you hate JAVA
2.将找到的第单数串转成大写,第双数串转成小写
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestDemo {
public static void main(String[] args) {
//CASE_INSENSITIVE表示不分大小写
Pattern pattern = Pattern.compile("java", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher("java Java JAVa JaVa IloveJAVA you hate Java dddge4");
//2.将找到的第单数串转成大写,第双数串转成小写
int i = 0;//用来判断奇偶数
StringBuffer buffer = new StringBuffer();
while(matcher.find()){
i++;
if (i%2 == 0) {
//双数,就转成小写
matcher.appendReplacement(buffer, "java");
}
else {
matcher.appendReplacement(buffer, "JAVA");
//单数,就转成大写
}
}
matcher.appendTail(buffer);//为什么要加这一句,如果不加的话,后面" dddge4"就不出来了
System.out.println(buffer);
}
}
JAVA java JAVA java IloveJAVA you hate java dddge4
注意:
public Matcher appendReplacement(StringBuffer sb, String replacement)
是将当前匹配子串替换为指定字符串,并且将替换后的子串以及之前到上次匹配子串之后的字符串段添加到一个StringBuffer对象里。此方法设计用于循环以及appendTail和find方法中。
public StringBuffer appendTail(StringBuffer sb)
是将最后一次匹配工作后剩余的字符串添加到StringBuffer对象中。
在上面栗子中,两者是结合使用的。
五、实际应用
1.抓取网页上的email地址
先在论坛上找到:比如:点击打开链接,并保存在本地
package rexExp;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EmailSpider {
public static void main(String[] args) {
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader("D:\\123.html"));//一行一行的杜
String line = "";
while ((line=bufferedReader.readLine()) != null) {
parse(line);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static void parse(String line) {
Pattern pattern = Pattern.compile("[\\w[.-]]+@[\\w[.-]]+");
Matcher matcher = pattern .matcher(line);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
}
}
结果截图:
2.代码统计小程序