正则表达式
符合一定规则的表达式
作用
用于专门操作字符串
特点
用一些特定的符号来表示一些代码操作,这样就简化了书写,所以就是在学习一些特殊符号的使用。
好处
可以简化对字符串的复杂操作
弊端
符号定义越多,正则越长,阅读性越差。
常见符号
字符
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()); } } } }
运行结果