java正则表达式

本文内容是根据毕晓东老师的视频教程总结而得。

1.特点、概述

符合一定规则的表达式。作用:用于专门操作字符串。

String类中有各种操作字符串的各种方法为什么还要用正则表达式?String类中的方法比较复杂,而正则表达式使用起来会相对简单。

需求:对QQ号码进行校验,要求5~15位,0不能开头,只能是数字。

按照字符串方法进行校验:

package com.vnb.javabase.base.regex;

/**

 * Description:以字符串形式对QQ号码进行校验,要求5~15位,0不能开头,只能是数字。

 * @author

 * @date 20181023

 */

public class RegexDemo {

   public static void main(String[] args) {

      checkQQ("343445643jygf8");

   }

   public static void checkQQ(String qq){

      //判断长度是否在5~15

       if(qq.length()>=5 && qq.length()<=15){

          //判断qq号是否以)开头

          if(!qq.startsWith("0")){

             //判断qq号是否为数字

             boolean flag = true;//一旦发现某个位置不为数字则返回qq号非法

             char[] arr = qq.toCharArray();

             for (int i = 0; i < arr.length; i++) {

                if(!(arr[i]>='0' && arr[i]<='9')){

                   flag = false;

                   break;

                }

            }

             if(flag){

                System.out.println("QQ:"+qq);

             }else{

                System.out.println(qq+".......QQ号非法");

             }

          }else{

             System.out.println("qq号不能以0开头");

          }

       }else{

          System.out.println("qq号长度不在5~15位之间");

       }

   }

}

前面的方式麻烦,可以使用Long进行转化,如果不是数字则会抛出异常出现非法字符(异常捕捉处理方式):

public static void checkQQ_2(String qq){

      //判断长度是否在5~15

       if(qq.length()>=5 && qq.length()<=15){

          //判断qq号是否以)开头

          if(!qq.startsWith("0")){

             //判断qq号是否为数字

             try{

                long qqLong = Long.parseLong(qq);

             }catch(NumberFormatException e){

                System.out.println(qq+".......QQ号非法");

             }

          }else{

             System.out.println("qq号不能以0开头");

          }

       }else{

          System.out.println("qq号长度不在5~15位之间");

       }

   }

结果:343ert4.......QQ号非法

这种方式是使用了String类中的方法进行组合完成了需求,但是代码过于复杂,以下使用正则表达式进行校验,通过字符串方法matchs()方法实现:

public static void checkQQ_3(String qq){

      String regex = "[1-9][0-9]{4,14}";//第一位19;第二位只要是数字即可且第二位出现次数为414

      boolean flag = qq.matches(regex);

      if(flag){

         System.out.println(qq+"......ok");

      }else{

         System.out.println(qq+"......不合法");

      }

   }

结果:343445643jygf8......不合法

特点:用一些自定义的符号来表示一些代码操作,这样就简化书写。所以学习正则表达式就是在学习一些特殊符号的使用。

2.匹配

具体操作的功能:

匹配:String matches()方法,匹配整个字符串,有一个不匹配则结束

[abc]:[ ]判断一个字符串中某一个字符位上出现的字符,要么是a,要么是b,要么是c

[^abc]:除了abc三个字符以外的任意一个字符

package com.vnb.javabase.base.regex;

public class RegexDemo2 {

   public static void main(String[] args) {

      demo();

   }

   public static void demo(){

      String str = "ab";

      String reg = "[abc]";

      boolean b = str.matches(reg);

      System.out.println(b);

   }

}

结果:false

String str = "b";

结果:true

. :表示该字符不做任何限制,任何字符都行

\xOB是男的符号,\f换页符,\w可用于邮箱(字母数字或者_)

X代表某个规则,如\d?表示数字出现一次或没有数字

练习:匹配手机号

public static void checkTel(String tel){

      //手机号段只有13XXX 15XXXX 18XXXX

      String regex = "1[358]\\d{9}";

      boolean flag = tel.matches(regex);

      if(flag){

         System.out.println(tel+"......ok");

      }else{

         System.out.println(tel+"......不合法");

      }

   }

结果:13287879090......ok

16287879090......不合法

3.切割

3.1String类的split()方法:String[] split(String regex)

public static void splitDemo(){

      /*String str = "23r,asdf,23";

      String reg = ",";*/

      String str = "23r  asdf  23";

      String reg = " +";

      String[] arr = str.split(reg);

      for(String s : arr){

         System.out.println("s:"+s);

      }

   }

结果:在由多个空格的情况下,一般的切割无法匹配所有的空格,所以使用“ +”即可匹配一个或多个空格。

s:23r

s:asdf

s:23

3.2正则表达式中的"."

注意:"."是正则表达式的一个特殊符号表示任意字符,直接用"."切割是不行的

public static void splitDemo(){

      /*String str = "23r,asdf,23";

      String reg = ",";*/

      /*String str = "23r  asdf  23";

      String reg = " +";*/

      //注意:"."是正则表达式的一个特殊符号表示任意字符,直接用"."切割是不行的

      String str = "23r.asdf.23";

      String reg = ".";

      String[] arr = str.split(reg);

      for(String s : arr){

         System.out.println("s:"+s);

      }

   }

结果:没有打印任何结果

一定要使用“.”进行切割,要转义,\.表示正则表达式的”.”,要转义”.”使用\\.

String reg = "\\.";

结果:

s:23r

s:asdf

s:23

3.3出现叠词进行切割

利用正则表达式的组:当想要对一个规则的结果进行重用时,可以将这个规则进行封装成组,组里面的结果就可以再次被使用。即(),通过\进行往回引用即(.)\1表示第一位的内容在第二位出现(.)\\1

按照叠词进行切割:叠词即后一个和前一个一致,前一个是任意字符,后一个和前一个字符相同。即"."(第一位的字符位“.”任意字符,第二位字符位是第一位的复用)。注意,没有写(),叫做默认第0组,所以不写()没有意义。

   public static void splitDemo1(){

      String str = "asdfzzgwerdda4rfdseeasgresf";

      String reg = "(.)\\1";

      String[] arr = str.split(reg);

      for(String s : arr){

         System.out.println("s:"+s);

      }

   }

结果:

s:asdf

s:gwer

s:a4rfds

s:asgresf

(.)\\1如果第一位是z,第二位也是z才匹配

如果想匹配一次或多次叠字(.)\\1+

为了可以让规则的结果被重用,可以将规则封装成一个组,用()实现,组的出现都有编号。从1开始,想要使用已有的组可以通过\n的形式来获取(n就是组的编号)

多个组:书写方便但是阅读性差

正则表达式弊端:符号定义越多,正则越长,阅读性越差

4.替换

String replaceAll(String regex,String replacement):根据正则进行替换

public static void main(String[] args) {

      //要求:将字符串中的数字\d替换成#,连续超过5个数字就替换成#

      String str = "asdfas1382398575784y12345678asdff";

      //将叠词替换成#

      String str1 = "erf3ee23rsfddddsdfsdf";

       //将字符串中所有的叠词替换成#

//replaceAllDemo(str,"(.)\\1+","#");

      replaceAllDemo(str,"\\d{5,}","#");

      replaceAllDemo(str1,"(.)\\1+","&");

      //将重叠的字母替换成单个字母:取完叠词后将新的字符设置成和组里面的字符一样(通过$1获取组里面的字符)

      replaceAllDemo(str1,"(.)\\1+","$1");//$1表示获取前一个规则的第一个组,然后使用第一个符合规则的字符进行替换

   }

public static void replaceAllDemo(String str,String reg,String newStr){

       str = str.replaceAll(reg, newStr);

      System.out.println("str:"+str);

   }

结果:

str:asdfas#y#asdff

str:erf3&23rsf&sdfsdf

str:erf3e23rsfdsdfsdf

通过多个组的形式替换某一部分:

5.获取

将字符串中符合指定的规则的子串取出来

操作步骤:

  • 将正则表达式封装成对象(Pattern、Matcher)
  • 让正则表达式对象和要操作的字符串相关联(compile() find())
  • 关联后,获取正则匹配引擎(group())
  • 通过引擎对符合规则的子串进行操作,比如取出

Pattern类:正则表达式的编译表示形式,即描述封装的正则表达式(将正则表达式封装成对象),封装成对象了并没有对字符串进行操作,要对字符串进行操作,必须使用Matcher匹配器对象操作已有字符串

没有构造函数,只有静态本类对象如,

  • static Pattern compile(String regex):eg,Pattern p = Pattern.compile(regex)
  • 让正则对象和要作用的字符串相关联matcher(CharSequence cs),char值得可读序列,String是其子类对象,返回此模式的新匹配器通过解释PatternCharcter sequence执行匹配操作的引擎
  • 获取匹配器对象,有了匹配器后,就有了多种方式操作字符串封装到了匹配器里
  • matches():其实String类的matches方法用的就是封装后的Pattern中的matches()方法,用起来简单,但是功能单一。eg,Matcher m = p.matcher(str)
  • find():尝试查找与模式匹配的输入序列的下一个子序列
  • group():用于获取匹配后的结果,需要先执行find()方法才能执行group方法否则会抛出异常No match found

package com.vnb.javabase.base.regex;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

public class RegexDemo3 {

   public static void main(String[] args) {

      getDemo();

   }

   public static void getDemo(){

      //将连续三个字母的单词取出来(三个三个的取,取出来的是字母就获取,不是就不获取)

      String str = "ming tian jiu yao fang jia le,da jia.";

      String reg = "\\b[a-z]{3}\\b";

      //将规则封装成对象

      Pattern p = Pattern.compile(reg);

      //让正则对象和要作用的字符串相关联matcher(CharSequence cs),char值得可读序列,String是其子类对象,返回此模式的新匹配器通过解释PatternCharcter sequence执行匹配操作的引擎

      //获取匹配器对象,有了匹配器后,就有了多种方式操作字符串封装到了匹配器里

      Matcher m = p.matcher(str);

      //将规则作用到字符串上,并进行符合规则的子串查找

      boolean b = m.find();

      //System.out.println(b);

       System.out.println(str);

      while(m.find()){

         //发现取出来的并不是英文单词,所以可以使用英文边界匹配器(\b单词边界)

         System.out.println(m.group());

System.out.println(m.start()+"........."+m.end());

      }

   }

}

结果:

ming tian jiu yao fang jia le,da jia.

yao

14.........17

jia

23.........26

jia

33.........36

end()返回最后匹配的偏移量

start()

包头不包尾找取出来的数组角标。

先去进行匹配再进行查找,发现匹配返回false,获取到的结果,发现没有获取到:

matches作用于整个字符串,规则是连续4个字母,当查找到ming时,发现字符串还有东西,所以matches就不匹配了,但是此时匹配器已经走到tian的t了,前面不匹配了,所以索引位改变了,再使用m.find()时,就会从t开始进行匹配,即同一个匹配器使用的是同一个指针:

package com.vnb.javabase.base.regex;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

public class RegexDemo3 {

   public static void main(String[] args) {

      getDemo();

   }

   public static void getDemo(){

      //将连续三个字母的单词取出来(三个三个的取,取出来的是字母就获取,不是就不获取)

      String str = "ming tian jiu yao fang jia le,da jia.";

      String reg = "\\b[a-z]{4}\\b";

      //将规则封装成对象

      Pattern p = Pattern.compile(reg);

      //让正则对象和要作用的字符串相关联matcher(CharSequence cs),char值得可读序列,String是其子类对象,返回此模式的新匹配器通过解释PatternCharcter sequence执行匹配操作的引擎

      //获取匹配器对象,有了匹配器后,就有了多种方式操作字符串封装到了匹配器里

      Matcher m = p.matcher(str);

      //将规则作用到字符串上,并进行符合规则的子串查找

      boolean b = m.find();

      //System.out.println(b);

      System.out.println(str);

      System.out.println("matches:"+m.matches());

      while(m.find()){

         //发现取出来的并不是英文单词,所以可以使用英文边界匹配器(\b单词边界)

         System.out.println(m.group());

         System.out.println(m.start()+"........."+m.end());

      }

   }

}

结果:

ming tian jiu yao fang jia le,da jia.

matches:false

tian

5.........9

fang

18.........22

6.练习1

需求:将下列字符串转成:我要学编程

到底使用四种功能的哪一个?或者哪几个呢?

字符串:我我....我我..我要…我要…要要…学学学…学学….编编…编程

思路:

  • 如果只想知道该字符串是否对或错,使用匹配
  • 想要将已有的字符串变成李另一个字符串,使用替换
  • 想要按照自定义的方式将字符串变成多个字符串,使用切割。获取规则以外的子串
  • 想要拿到(获取)符合需求的字符串子串,使用获取。获取符合规则的子串。

此处是将已有字符串变成另一个字符串,使用替换功能。

步骤:

先将“.”去掉

再将多个重复的内容变成单个内容

package com.vnb.javabase.base.regex;

public class RegexTest {

   public static void main(String[] args) {

      String str = "我我....我我..我要...要要....学学学....学学....编编....编程";

      reverseStr(str);

   }

   public static void reverseStr(String str){

      //先将.去掉

      str = str.replaceAll("\\.+", "");

      System.out.println("str:"+str);

      //再去除重复,如果出现重复使用第一组替换

      str = str.replaceAll("(.)\\1+", "$1");

      System.out.println("str:"+str);

   }

}

结果:

str:我我我我我要要要学学学学学编编编程

str:我要学编程

7.练习2

需求:将IP地址进行按顺序排序

192.168.1.254  102.49.23.13  10.10.10.10  2.2.2.2  8.109.90.30

取出空格,再排序

思路:

  • 因为如果位数不同,有的1位有的2位有的3位就会不好比较,所以使用补0方式将1和2位的补全为3位再比较
  • 每一段的位数都不一样,那么要补多少0?可以每一段都补2个0保证至少有3位
  • 将每一段只保留3位,这样所有的IP地址都是每一段2位

package com.vnb.javabase.base.regex;

import java.util.TreeSet;

public class RegexTest2 {

   //IP地址进行按顺序排序192.168.1.254  102.49.23.13  10.10.10.10  2.2.2.2  8.109.90.30

   public static void main(String[] args) {

      sortIp();

   }

   public static void sortIp(){

      String ip = "192.168.1.254  102.49.23.13  10.10.10.10  2.2.2.2  8.109.90.30";

      //因为每个IP地址的位数不同,所以强行将每个IP都补20,这样就可以保证每个IP段上至少有3

      ip = ip.replaceAll("(\\d+)", "00$1");//将所有连续数字(一个或多个)头部加上200

      //ip:00192.00168.001.00254  00102.0049.0023.0013  0010.0010.0010.0010  002.002.002.002  008.00109.0090.0030

      System.out.println("ip:"+ip);

      //因为原本就有大于1位的IP段在加上两个0后,位数超过了3位,所以要将超过3位的0去掉

      ip = ip.replaceAll("0*(\\d{3})", "$1");//先匹配前面0个或多个0,再匹配数字,如果数字已经有3位就只取数字

      System.out.println("ip:"+ip);//ip:192.168.001.254  102.049.023.013  010.010.010.010  002.002.002.002  008.109.090.030

      //通过" "空格切割字符串成Ip数组

      String[] arr = ip.split(" +");//空格可能会有1个或多个

      //遍历数组

      System.out.println("通过空格切割后的字符串数组:");

      //建立TreeSet集合,通过TreeSet集合进行排序

      TreeSet<String> ipSet = new TreeSet<String>();

      for(String s : arr){

         System.out.println(s);

         //将字符串添加进集合中,添加时去掉每个ip端中前面的0

         ipSet.add(s.replaceAll("0*(\\d+)", "$1"));

      }

      //遍历TreeSet集合后,ip已经排好序

      System.out.println("遍历且排序好的Ip集合:");

      for(String s : ipSet){

         System.out.println(s);

      }

   }

}

结果:需要在遍历集合的时候在去除0才能正确比较,否则只会按照字符串顺序进行排序

ip:00192.00168.001.00254  00102.0049.0023.0013  0010.0010.0010.0010  002.002.002.002  008.00109.0090.0030

ip:192.168.001.254  102.049.023.013  010.010.010.010  002.002.002.002  008.109.090.030

通过空格切割后的字符串数组:

192.168.001.254

102.049.023.013

010.010.010.010

002.002.002.002

008.109.090.030

遍历且排序好的Ip集合:

2.2.2.2

8.109.90.30

10.10.10.10

102.49.23.13

192.168.1.254

8.练习3

需求:对邮件地址进行校验

精确的匹配:

  • @前6到12位,保证尽量不重复;字母(大小写)、数字、下划线;[a-zA-Z_0-9]{6,12}
  • @固定
  • @后面字母或数字一次或多次
  • .固定
  • .后字母(大小写)一次或多次
  • 但是有可能.com和.cn有可能都有,有可能只有其中一个所以使用组(最多3次重复):(\\.[a-zA-Z]+)+

public static void checkMail(){

      String mail = "adssdff@sina.com.cn";

      //String regex = "[a-zA-Z_0-9]{6,12}@[a-zA-Z0-9]{2,12}(\\.[a-zA-Z]+)+";

      String regex = "\\w{6,12}@[a-zA-Z0-9]{2,12}(\\.[a-zA-Z]+)+";

      boolean b = mail.matches(regex);

      System.out.println("邮箱输入是否正确:"+b);

   }

结果:

邮箱输入是否正确:true

相对不太精确的匹配:

\w代表:[a-zA-Z_0-9]

所以使用\\w+@\\w+(\\.\\w+)+也可以粗略匹配,但是这样1@1.1也能过

只要找到邮件地址的特征,@存在即可

9.网页爬虫(蜘蛛)

通过爬虫获取到网页上所有的邮箱等

数据源:txt文件,源文件里有很多邮件地址

需求:将数据源里的邮箱地址爬取出来

思路:

获取指定文档中的邮件地址。使用获取功能,Pattern  Matcher

mail.txt

asdfasdfanhasv weasdf@sffj.com asdfasdfanhasv

afasfs 873rasdfwr2435aSfsafbdvc lmf@qq.com.cn  sdfasf

fsafjiaosdjfasdf sdwes238924859jidf@dsifh.com afasdf

asdfasdfh9jsf43r93@oasifjodif.com asfasdf

89247weiuafhs@asfjaso.comads.cn

asdfa98weuasf asf938ufies asf lisi@160.com asdfasf

package com.vnb.javabase.base.regex;

import java.io.BufferedReader;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStreamReader;

import java.util.regex.Matcher;

import java.util.regex.Pattern;

public class RegexTest3 {

   public static void main(String[] args) {

      touchMail1();

   }

   public static void touchMail1(){

      //字符流 文本 需要缓冲

      try {

         //通过字符流独缺到

         BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("mail.txt")));

         String line = null;

         //System.out.println("txt文件所有内容:");

         try {

            while((line=br.readLine())!=null){

                //System.out.println(line);

                //获取到文件所有内容后,通过正则获取功能进行处理

                String regex = "\\w{6,12}@[a-zA-Z0-9]{2,12}(\\.[a-zA-Z]+)+";

                Pattern p = Pattern.compile(regex);

                Matcher m = p.matcher(line);

                while(m.find()){

                   System.out.println(m.group());

                }

            }

         } catch (IOException e) {

            e.printStackTrace();

         }

      } catch (FileNotFoundException e) {

         e.printStackTrace();

      }

   }

}

结果:

weasdf@sffj.com

38924859jidf@dsifh.com

dfh9jsf43r93@oasifjodif.com

9247weiuafhs@asfjaso.comads.cn

如果爬取网页上的邮箱地址:

自己造一个网页出来,有邮箱相关地址,再从该网页爬取邮箱地址

url.openConnection()获取到网页连接器,在通过其conn.getInputStream()获得数据

<%@ page language="java" contentType="text/html; charset=utf-8"

    pageEncoding="utf-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta charset="utf-8">

<title>网页</title>

</head>

<body>

asdfasdfanhasv weasdf@sffj.com asdfasdfanhasv

afasfs 873rasdfwr2435aSfsafbdvc lmf@qq.com.cn  sdfasf

fsafjiaosdjfasdf sdwes238924859jidf@dsifh.com afasdf

asdfasdfh9jsf43r93@oasifjodif.com asfasdf

89247weiuafhs@asfjaso.comads.cn

asdfa98weuasf asf938ufies asf lisi@160.com asdfasf

</body>

</html>

public static void touchMail2(){

      try {

         URL url = new URL("http://localhost:8080/SpringmvcQuartz/print");

         try {

            URLConnection conn = url.openConnection();

            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

            String line = null;

            //System.out.println("txt文件所有内容:");

            try {

                while((line=br.readLine())!=null){

                   //System.out.println(line);

                   //获取到文件所有内容后,通过正则获取功能进行处理

                   String regex = "\\w{6,12}@[a-zA-Z0-9]{2,12}(\\.[a-zA-Z]+)+";

                   Pattern p = Pattern.compile(regex);

                   Matcher m = p.matcher(line);

                   while(m.find()){

                      System.out.println(m.group());

                   }

                }

            } catch (IOException e) {

                e.printStackTrace();

            }

         } catch (IOException e) {

            e.printStackTrace();

         }

      } catch (MalformedURLException e) {

         e.printStackTrace();

      }

   }

结果:

touchMail2

weasdf@sffj.com

38924859jidf@dsifh.com

dfh9jsf43r93@oasifjodif.com

9247weiuafhs@asfjaso.comads.cn

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值