正则表达式
符合一定规则的表达式
作用
用于专门操作字符串
特点
用一些特定的符号来表示一些代码操作,这样就简化了书写,所以就是在学习一些特殊符号的使用。
好处
可以简化对字符串的复杂操作
弊端
符号定义越多,正则越长,阅读性越差。
常见符号
字符
x 字符 x
\\ 反斜线字符
\t 制表符('\u0009')
\n 新行(换行)符('\u000A')
\r 回车符('\u000D')
\f 换页符('\u000C')
\a 报警 (bell) 符 ('\u0007')
\e 转义符('\u001B')
字符类
[abc] a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] d、e 或 f(交集)
[a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去)
[a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去)
预定义字符类
. 任何字符(与行结束符可能匹配也可能不匹配)
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[\t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]
边界匹配器
^ 行的开头
$ 行的结尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符(如果有的话)
\z 输入的结尾
Greedy数量词
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次
组和捕获
捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C)))中,存在四个这样的组:
1. ((A)(B(C)))
2. \A
3. (B(C))
4. (C)
组零始终代表整个表达式。在替换中常用$匹配组的内容。
正则表达式具体操作功能
1. 匹配
Boolean matches(String regex) 用规则匹配整个字符串,只要有一出不符合规则匹配就结束,返回false。
代码演示
/*
对正则表达式的匹配功能演示
匹配手机号和QQ号
手机号
13XXXXXXXXX 15XXXXXXXXX 18XXXXXXXXX
QQ号
6~12位的纯数字即可,但开头不能是0
*/
class RegexDemo
{
public static void main(String[] args)
{
//调用验证方法,并传入要验证的数字
phoneNumber("13555555555");
phoneNumber("18555555555");
phoneNumber("1755555555");
QQNumber("123215479615");
QQNumber("0232154");
QQNumber("123");
}
//验证手机号方法,构造函数接收字符串
public static void phoneNumber(String str)
{
//来定义手机号的规则,按题意,13.15.18的号段都可以所以第一位是[1]只是肯定的
//第二位:3,5,8,中的任意一个,所以是[3,5,8]
//第3到9位:任意数字均可,所以是\\d,为什么要多加一个\呢?因为\在java中是转义字符
//会把d转成其他的意思,所以要在前面再加一个\就成了\\d因为剩下的是9位,所以是\\d{9}
String regex = "[1][358]\\d{9}";
//使用matches()方法,匹配传入的字符串,用boolean类型变量记录注返回值
boolean b = str.matches(regex);
//输出返回结果
System.out.println("您传入的手机号"+str+":"+b);
}
//验证QQ号的方法,在构造函数中接收字符串
public static void QQNumber(String str)
{
//定义QQ号的规则,按题意6~12位的纯数字即可,但开头不能是0
//第一位:[1-9] 数字1-9都可以,就是不能是0
//剩下好几位:剩下的是纯数字,有0也可以,所以是\\d。
//除去第一位还剩下5位或者11位,所以是\\d{5,11}任意数字出现至少5次,但不超过11次
String regex = "[1-9]\\d{5,11}";
//使用matches()方法,匹配传入的字符串,用boolean类型变量记录注返回值
boolean b = str.matches(regex);
//输出返回结果
System.out.println("您传入的QQ号"+str+":"+b);
}
}
运行结果
2. 切割
String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。
代码演示
/*
正则表达式切割代码演示
要提的几点
1. 当以 . 分开字符串时,split方法内要传入"\\.",因为 . 在正则中代表了任意字符
我们也要用它切所以要区分开加一个\变为"\.",可是\是转义字符会把 . 转成其他意思
所以再加一个\ 最终变为"\\."
2. 当以 \\ 分开字符串时,split方法内要传入"\\\\"原因同上。
3. 按叠词完成切割,这时就要用到组的概念。因为是叠词,也不知道是什么词叠.
所以就用 (.) 代表有一个任意字符的一个组,用\\n来获取组中的内容,n就是组的编号
这里我们只有一个组,也就是第一组,所以是\\1
把组中的内容拿来用,在后面再加上 + 获取的组中的内容出现了一此或多次
叠词可能叠一个也可能叠好几个,所以需要 +
最终变为"(.)\\1+" 意思就是一个字符出现了一次,然后又出现了一个或多次,所以是叠词
*/
class RegexDemo2
{
public static void main(String[] args)
{
//按 . 切
String[] arr = "123.25.478.sdf.fe.哈哈哈".split("\\.");
//打印切出来的字符串数组
print(arr);
System.out.println("---------我是一条分割线---------");
//按\\切
arr = "sdf\\sdf\\54\\8799\\547\\啦啦啦".split("\\\\");
print(arr);
System.out.println("---------我是一条分割线---------");
//按叠词切
arr = "465555sdfff878988sdffe哇哈哈哈".split("(.)\\1+");
print(arr);
}
//遍历数组方法,方便看效果
public static void print(String[] arrs)
{
for(String str : arrs)
{
System.out.println(str);
}
}
}
运行结果
3. 替换
String replaceAll(String regex , String replacement)
使用给定的replacement替换此字符串中所有匹配给定的正则表达式的子字符串。
代码演示
/*
正则表达式替换代码演示
将叠词替换成一个字符
还是叠词,所以还要使用到组的概念。使用(.)\\1+ 代表叠词,
因为还要用叠词的一个字来替换叠词,所以要用到$
$:拿前面规则中的组
$ 和 \1的区别:\n 获取的是同一个规则里面的组。$ 获取的是别的字符串中的组
*/
class RegexDemo3
{
public static void main(String[] args)
{
//定义字符串
String str = "我我我我我.....一....定定定定.....要要....去....黑.....马!";
//先将.去掉,将 . 替换成空
str = str.replaceAll("\\.+","");
//输出去掉 . 之后的结果
System.out.println(str);
System.out.println("---------我是一条分割线---------");
//定义要被替换的子字符串所要符合的规则,也就是叠词的规则
String r2 = "(.)\\1+";
//用叠词中的一个字来替换叠词,使用$1获取前面规则中的第一组。
str = str.replaceAll(r2 , "$1");
//输出结果
System.out.println(str);
}
}
运行结果
4. 获取
将字符串中符合规则的子串取出。
操作步骤
a. 将正则表达式封装成对象
例:Pattern p = Parrern.compile(regex)
b. 让正则对象和要操作的字符串相关联,并获取正则匹配引擎(匹配器)
获得匹配器对象。通过Pattern中的
Matcher matcher(CharSequence input) 其中Charsequence实现了String类所以可以直接在参数中传入字符串。
例:Matcher m = p.macher(str);
个人理解:把Pattern和要操作的字符关联上,取得Matcher对象Matcher对象调用的所有方法都是在用规则去匹配字符串。
规则和字符串都被封装在Matcher对象内部了,所以Matcher才叫匹配器。(方便记忆)
c. 通过引擎对符合规则的子串进行操作。
代码演示
注:当同一个匹配器同时调用matches()和find()方法时,就产生索引位的改变,不会从第一个字符开始匹配,
可能造成数据丢失,因为同一个匹配器只有一个指针
/*
正则表达式获取代码演示
*/
import java.util.regex.*;
class RegexDemo4
{
public static void main(String[] args)
{
//调用获取方法
get();
}
//获取方法
public static void get()
{
//定义要获取的字符串
String str = "jin tian xue xi de tai xin ku le , bu guo wo hui jia you de";
//打印字符串
System.out.println(str);
//定义规则,获取3个字母的单词 \\b为单词边界
String regex = "\\b[a-z]{3}\\b";
//将规则封装成对象。
Pattern p = Pattern.compile(regex);
//让正则对象和要作用的字符串相关联。获取匹配器对象。
Matcher m = p.matcher(str);
//System.out.println(m.matches());
//其实String类中的matches方法。用的就是Pattern和Matcher对象来完成的。
//只不过被String的方法封装后,用起来较为简单。但是功能却单一。
//将规则作用到字符串上,并进行符合规则的子串查找。一个一个找
while(m.find())
{
//使用group方法获取匹配后的结果。
System.out.println(m.group());
//start()和end()方法的返回值分别表示匹配后的结果在字符串上的角标位置。
System.out.println(m.start()+","+m.end());
}
}
}
运行结果
正则表达式有四种功能,在需求下具体使用哪一种功能呢?或者哪几个功能呢?
思路方式
1. 如果只想知道该字符串是对是错,使用匹配。
2. 想要将已有的字符串变为另一个字符串,用替换
3. 想要按照指定的方式将字符串变成多个字符串,用切割(获取规则以外的字符串)
4. 想拿到符合要求的字符串的子串,用获取(获取符合规则的子串)
正则表达式练习
练习一
/*
正则表达式练习
练习一
将IP地址排序 192.68.1.3 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.30
思路
1. 首先先将IP地址的每一段都至少补到3位,因为如10和2 本来是10大,可是先比的是1和2
所以就变成了2大,这就不对了,所以要补全3位,但有的段已经是3位了,不管,先补
2. 将所有段都只保留后三位
3. 按自然顺序给IP地址排序,可以使用把IP地址存入TreeSet集合中的方法让他们自然排序
4. 将每一段前面的有0的去掉。
*/
import java.util.*;
class RegexTest1
{
public static void main(String[] args)
{
//定义要被排序的IP地址
String ip = "192.68.1.3 102.49.23.013 10.10.10.10 2.2.2.2 8.109.90.30";
//定义要被替换的规则,(\\d+) 数字段
String regex = "(\\d+)";
//将规则替换为00$1 在前面补两个0,后面数字不变
ip = ip.replaceAll(regex , "00$1");
//输出补完0的字符串
System.out.println("第一步:"+ip);
//定义要被替换的规则0*(\\d{3}) 前面有零个或多个0 后面是三个整数
regex = "0*(\\d{3})";
//将规则替换为 $1 只保留后三位数
ip = ip.replaceAll(regex , "$1");
System.out.println("第二步"+ip);
//将字符串通过空格切割
String[] arrs = ip.split(" ");
//定义一个TreeSet集合
TreeSet<String> ts = new TreeSet<String>();
//把切割得来的字符数组加入集合中,让它们按自然顺序排序
for(String str : arrs)
{
ts.add(str);
}
System.out.println("第三步");
//定义循环,循环集合中的元素
for(String s : ts)
{
//将多余的0去除,并打印结果
System.out.println(s.replaceAll("0*(\\d+)","$1"));
}
}
}
运行结果
练习二
/*
正则表达式练习二
需求
对邮箱进行检验
*/
class RegexTest2
{
public static void main(String[] args)
{
//定义邮箱地址
String mail = "1235468@qq.com";
//较为精确的匹配。
String reg = "[a-zA-Z0-9]+@[a-zA-Z0-9]+(\\.[a-zA-Z]+)+";
//相对来说不太精确的匹配
reg = "\\w+@\\w+(\\.\\w+)+";
//返回结果
System.out.println(mail.matches(reg));
}
}
练习三
/*
正则表达式练习三
需求
网络爬虫,获取一个网页上的所有邮箱
思路
1. 将网址封装成URL对象
2. 连接远程服务器
3. 建立字符输入流,用于读取网页中的字符信息
4. 将正则规则封装成对象
5. 按行读取网页中的信息,将Pattern对象和读取的信息相关联
6. 将规则作用在字符串上,并获取匹配后的结果
*/
import java.io.*;
import java.util.regex.*;
import java.net.*;
class RegexTest3
{
public static void main(String[] args) throws Exception
{
//调取获得邮箱的方法
getMails();
}
//获得邮箱方法
public static void getMails()throws Exception
{
//封装网页地址
URL url = new URL("http://tieba.baidu.com/p/3552256898");
//获取远程服务器
URLConnection conn = url.openConnection();
//创建读取流,读取网页上的字符内容
BufferedReader bufIn =
new BufferedReader(new InputStreamReader(conn.getInputStream()));
//定义邮箱规则
String mailreg = "\\w+@\\w+(\\.\\w+)+";
//将规则封装成对象
Pattern p = Pattern.compile(mailreg);
//定义String类型变量
String line = null;
//读取网页上内容,进行匹配
while((line=bufIn.readLine())!=null)
{
//让正则对象和要操作的字符串相关联,获取匹配器对象
Matcher m = p.matcher(line);
//寻找符合规则的字符串
while(m.find())
{
//获取匹配后的结果,并打印
System.out.println(m.group());
}
}
}
}
运行结果