文章目录
  1. 1. 什么是正则表达式
    1. 1.1. 基础表达式
    2. 1.2. * ? + 的使用
    3. 1.3. 范围匹配
    4. 1.4. 两个重要的类Pattern和Matcher
    5. 1.5. 替换
    6. 1.6. 分组和查找
      1. 1.6.1. 贪婪和非贪婪模式

什么是正则表达式

编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

基础表达式

  • .所有字符
  • \d:所有数字
  • \D:所有非数字
  • \s:空白字符
  • \S:非空白字符
  • \w:[a-zA-Z_0-9]
  • \W:非\w

    //.表示任意字符
    System.out.println("a".matches("."));
    System.out.println("aa".matches(".a"));
    System.out.println("\\d");
    //\\d表示是否是数字
    System.out.println("123".matches("\\d\\d\\d"));
    System.out.println("1d32e".matches("\\d\\D\\d\\d\\D"));
    //\\s表示是否是空白字符
    System.out.println("1  2        d".matches("\\d\\s\\s\\d\\s\\sd"));
    //\\w表示常用输入字符:a-z,A-Z,0-9,_
    System.out.println("aa b1 22".matches("\\w\\w\\s\\w\\w\\s\\w\\w"));
    //[abcd]表示是否是abcd这是个字符中的某一个
    System.out.println("a".matches("[abcd]"));
    //[a-z]表示是否是a-z之间的字符
    System.out.println("D".matches("[a-zA-D]"));
    //[^a-z]表示不在a-z之间
    System.out.println("h".matches("[^a-z]"));
    //也支持&&和||
    System.out.println("a".matches("[a-z&&[def]]"));
    System.out.println("H".matches("[a-z]||[A-D]"));
    

* ? + 的使用

  • X? X,表示0个或1个

    //?表示0个或者1个
    System.out.println("a".matches("a?"));
    System.out.println("aa".matches("a?"));
    System.out.println("".matches("a?"));
    
  • X* X, 表示0个或多个

    System.out.println("aaaa".matches("a*"));
    //为false因为*表示的多个
    System.out.println("abcd".matches("a*"));
    System.out.println("abcdlskdjff".matches("a[a-z]*"));
    //为true
    System.out.println("".matches("a*"));
    
  • X+ X,表示1个或多个

    //+表示1个或者多个
    System.out.println("aa".matches("a+"));
    System.out.println("a".matches("a+"));
    //false,+表示一个或者多个
    System.out.println("".matches("a+"));
    

范围匹配

  • X{n} X, 表示出现n次

    System.out.println("26666".matches("\\d{5}"));
    
  • X{n,} X, 表示至少出现n次

    System.out.println("2666645333".matches("\\d{5,}"));
    
  • X{n,m} X, 表示至少出现n次最多出现m次

    //{n,m}表示至少出现n次最多出现m次
    System.out.println("2kk3-12-22".matches("\\d{4}-\\d{1,2}-\\d{1,2}"));
    
  • [abc] a, b, or c (simple class)

几个常用的例子:

//第一个:检测一个字符串是否是数字
System.out.println("2334.99".matches("\\d+\\.?\\d+"));
System.out.println("38".matches("\\d{1}||[12]{1}\\d{1}|3{1}[0-5]{1}"));
//第二个:检测一个字符串是否是一个电话号码0870-2233445-01
System.out.println("0870-2233445-01".matches("\\d{3,4}-\\d{7}-\\d{2,5}"));
System.out.println("0870-2233445".matches("\\d{3,4}-\\d{7}-*\\d*"));
//第三个:匹配一个IP地址 111.22.33.22
System.out.println("192.168.0.33".matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"));
System.out.println("92".matches("[1-2]?\\d{0,2}"));
System.out.println("192.234.22.33".matches("[1-2]?\\d{0,2}\\.[1-2]?\\d{0,2}\\.[1-2]?\\d{0,2}\\.[1-2]?\\d{0,2}"));
//第四:匹配一个身份证号
System.out.println("53210119761209005X".matches("\\d{15}||\\d{18}||\\d{17}[X]"));
//匹配一个电子邮件
System.out.println("ynkonghao@gmail-pun.com.ddfdfdf".matches("[\\w-\\.]*\\w+@[\\w\\.-]*\\w+\\.\\w{2,6}"));

^这个元字符有两个用法:

  1. ^不在[]中就表示以xx为开头
  2. 在[]中就表示非

$表示以xx为结尾

^和$的结合用法:比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$。

两个重要的类Pattern和Matcher

可以先将一个正则表达式编译成为一个Pattern对象,可以提高效率。通过Pattern可以获取一个Matcher对象,通过Matcher对象可以获取大量的有用信息。

Pattern p = Pattern.compile("\\d{4}");
Matcher m = p.matcher("23338888-3233-1111");

下面介绍一些常用的Matcher的方法:

判断是否匹配:m.matches(),返回一个boolean类型的返回值

将查找的指针重置:m.reset()

顺序匹配相应的字符串:m.find(),返回一个boolean类型的返回值

每进行一次find,就可以将字符串通过group获取:m.group(),返回获取到的字符串

注意:必须在find之后才能执行group,不然会报错!

m.start()和m.end()可以获取匹配字符串的开始位置和结束位置

while(m.find()) {
        System.out.println(m.group()+"["+m.start()+","+m.end()+"]");
    }

替换

String str = "234445sdsff3444sdss";
//第一个参数是正则表达式,第二参数是要替换的值
System.out.println(str.replaceAll("\\d", "*"));
System.out.println(str.replaceAll("\\d+", "*"));
System.out.println("13222331111".replaceAll("\\d{4}$", "****"));

“\d{4}$”指的是替换后四位!

分组和查找

使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。

  • 分组0对应整个正则表达式
  • 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配--因此所有命名组的组号都大于未命名的组号
  • 你可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.

    String str = "532101197612210039,532122199802120034,534501198212110029,532101780203009";
    //使用括号进行分组
    Pattern p = Pattern.compile("((\\d{6})(\\d{8}))\\d{4}");
    Matcher m = p.matcher(str);
    while(m.find()) {
        System.out.println(m.group());
        System.out.println("生源地:"+m.group(2)+"出生日期:"+m.group(3));
    }
    

贪婪和非贪婪模式

贪婪模式:指的是.*会匹配所有的信息,此处会找整个信息。

String h = "<table><td>你好</td><td>大家好</td><td>每个人都好</td></table>";
p = Pattern.compile("<td>(.*)</td>");
m = p.matcher(h);
while(m.find()) {
    System.out.println(m.group(1));
    System.out.println(m.start()+","+m.end());
}

找到的结果:

你好</td><td>大家好</td><td>每个人都好
7,44

非贪婪模式:仅仅只是匹配第一个结尾,特别注意:?接在*+之后就表示使用了非贪婪模式.

String h = "<table><td>你好</td><td>大家好</td><td>每个人都好</td></table>";
p = Pattern.compile("<td>(.*?)</td>");
m = p.matcher(h);
while(m.find()) {
    System.out.println(m.group(1));
    System.out.println(m.start()+","+m.end());
}

找到的结果:

你好
7,18
大家好
18,30
每个人都好
30,44

接下去会写一点网络爬虫的内容,也算是正则表达式的应用吧。

获取邮箱:

    public static List<String> getEmail(String str) {
    List<String> es = new ArrayList<String>();
    Pattern p = Pattern.compile("[\\w\\.-]*\\w+@[\\w\\.-]*\\w+\\.\\w{2,5}");
    Matcher m = p.matcher(str);
    while(m.find()) {
        es.add(m.group());
    }
    return es;
}

获取URL:

public static List<String> getUrl(String str) {
    List<String> es = new ArrayList<String>();
    Pattern p = Pattern.compile("<a.*?\\s+href=['\"]([^\"'>]*?)['\"].*?>(.*?)</a>");
    Matcher m = p.matcher(str);
    while(m.find()) {
        es.add(m.group(1));
    }
    return es;

附录:(来自于网络 正则表达式30分钟入门教程

表1.常用的元字符
代码说明
.匹配除换行符以外的任意字符
\w匹配字母或数字或下划线或汉字
\s匹配任意的空白符
\d匹配数字
\b匹配单词的开始或结束
^匹配字符串的开始
$匹配字符串的结束

表2.常用的限定符
代码/语法说明
*重复零次或更多次
+重复一次或更多次
?重复零次或一次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n到m次

表3.常用的反义代码
代码/语法说明
\W匹配任意不是字母,数字,下划线,汉字的字符
\S匹配任意不是空白符的字符
\D匹配任意非数字的字符
\B匹配不是单词开头或结束的位置
[^x]匹配除了x以外的任意字符
[^aeiou]匹配除了aeiou这几个字母以外的任意字符
表4.常用分组语法
分类代码/语法说明
捕获(exp)匹配exp,并捕获文本到自动命名的组里
(?<name>exp)匹配exp,并捕获文本到名称为name的组里,也可以写成(?’name’exp)
(?:exp)匹配exp,不捕获匹配的文本,也不给此分组分配组号
零宽断言(?=exp)匹配exp前面的位置
(?<=exp)匹配exp后面的位置
(?!exp)匹配后面跟的不是exp的位置
(?<!exp)匹配前面不是exp的位置
注释(?#comment)这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读
文章目录
  1. 1. 什么是正则表达式
    1. 1.1. 基础表达式
    2. 1.2. * ? + 的使用
    3. 1.3. 范围匹配
    4. 1.4. 两个重要的类Pattern和Matcher
    5. 1.5. 替换
    6. 1.6. 分组和查找
      1. 1.6.1. 贪婪和非贪婪模式