一文教会你如何在Java中使用正则表达式(三)

简介: 正则表达式一般用于字符串匹配, 字符串查找和字符串替换. 别小看它的作用, 在工作学习中灵活运用正则表达式处理字符串能够大幅度提高效率, 编程的快乐来得就是这么简单。 一下子给出一堆匹配的规则可能会让人恐惧, 下面将由浅入深讲解正则表达式的使用。

分组

先从一个问题引入, 看下面这段代码

public static void main(String[] args) {
    Pattern p = Pattern.compile("\\d{3,5}[a-z]{2}");
    String s = "123aa-5423zx-642oi-00";
    Matcher m = p.matcher(s);
    while(m.find()){
        p(m.group());
    }
}
//输出结果
123aa
5423zx
642oi

其中正则表达式"\\d{3,5}[a-z]{2}"表示3~5个数字跟上两个字母, 然后打印出每个匹配到的字符串

如果想要打印每个匹配串中的数字, 如何操作呢.

首先你可能想到把匹配到的字符串再进行匹配, 但是这样太麻烦了, 分组机制可以帮助我们在正则表达式中进行分组.

规定使用()进行分组, 这里我们把字母和数字各分为一组"(\\d{3,5})([a-z]{2})"

然后在调用m.group(int group)方法时传入组号即可

注意, 组号从0开始, 0组代表整个正则表达式, 从0之后, 就是在正则表达式中从左到右每一个左括号对应一个组. 在这个表达式中第1组是数字, 第2组是字母.

public static void main(String[] args) {
    Pattern p = Pattern.compile("(\\d{3,5})([a-z]{2})");//正则表达式为3~5个数字跟上两个字母
    String s = "123aa-5423zx-642oi-00";
    Matcher m = p.matcher(s);
    while(m.find()){
        p(m.group(1));
    }
}
//输出结果
123
5423
642

实战1: 抓取网页中的email地址(爬虫)

假设我们手头上有一些优质的资源, 打算分享给网友, 于是便到贴吧上发出一个留邮箱发资源的帖子. 没想到网友热情高涨, 留下了近百个邮箱. 但逐个复制发送太累了, 我们考虑用程序实现.

这里不展开讲发邮件部分, 重点应用已经学到的正则表达式从网页中截取所有的邮箱地址.

首先获取一个帖子的html代码随便找了一个, 点击跳转[1], 在浏览器中点击右键保存html文件

接下来看代码:

public class Demo12 {
    public static void main(String[] args) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader("C:\\emailTest.html"));
            String line = "";
            while((line = br.readLine()) != null){//读取文件的每一行
                parse(line);//解析其中的email地址
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(br != null){
                try {
                    br.close();
                    br = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    private static void parse(String line){
        Pattern p = Pattern.compile("[\\w[.-]]+@[\\w[.-]]+\\.[\\w]+");
        Matcher m = p.matcher(line);
        while(m.find()){
            System.out.println(m.group());
        }
    }
}
//输出结果
2819531636@qq.com
2819531636@qq.com
2405059759@qq.com
2405059759@qq.com
1013376804@qq.com
...

实战2: 代码统计小程序

最后的一个实战案例: 统计一个项目中一共有多少行代码, 多少行注释, 多少个空白行. 不妨对自己做过的项目进行统计, 发现不知不觉中也是个写过成千上万行代码的人了...

我在github上挑选了一个项目, 是纯java写的小项目, 方便统计. 点击跳转[2]

下面是具体的代码, 除了判断空行用了正则表达式外, 判断代码行和注释行用了String类的api

public class Demo13 {
    private static long codeLines = 0;
    private static long commentLines = 0;
    private static long whiteLines = 0;
    private static String filePath = "C:\\TankOnline";
    public static void main(String[] args) {
        process(filePath);
        System.out.println("codeLines : " + codeLines);
        System.out.println("commentLines : " + commentLines);
        System.out.println("whiteLines : " + whiteLines);
    }
    /**
     * 递归查找文件
     * @param pathStr
     */
    public static void process(String pathStr){
        File file = new File(pathStr);
        if(file.isDirectory()){//是文件夹则递归查找
            File[] fileList = file.listFiles();
            for(File f : fileList){
                String fPath = f.getAbsolutePath();
                process(fPath);
            }
        }else if(file.isFile()){//是文件则判断是否是.java文件
            if(file.getName().matches(".*\\.java$")){
                parse(file);
            }
        }
    }
    private static void parse(File file) {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(file));
            String line = "";
            while((line = br.readLine()) != null){
                line = line.trim();//清空每行首尾的空格
                if(line.matches("^[\\s&&[^\\n]]*$")){//注意不是以\n结尾, 因为在br.readLine()会去掉\n
                    whiteLines++;
                }else if(line.startsWith("/*") || line.startsWith("*") || line.endsWith("*/")){
                    commentLines++;
                }else if(line.startsWith("//") || line.contains("//")){
                    commentLines++;
                }else{
                    if(line.startsWith("import") || line.startsWith("package")){//导包不算
                        continue;
                    }
                    codeLines++;
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(null != br){
                try {
                    br.close();
                    br = null;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
//输出结果
codeLines : 1139
commentLines : 124
whiteLines : 172

贪婪模式与非贪婪模式

经过两个实战后, 相信大家已经掌握了正则表达式的基本使用了, 下面介绍贪婪模式与非贪婪模式.

通过查看官方api我们发现Pattern类中有如下定义:

Greedy quantifiers 贪婪模式
X?  X, once or not at all
X*  X, zero or more times
X+  X, one or more times
X{n}  X, exactly n times
X{n,} X, at least n times
X{n,m}  X, at least n but not more than m times
Reluctant quantifiers 非贪婪模式(勉强的, 不情愿的)
X?? X, once or not at all
X*? X, zero or more times
X+? X, one or more times
X{n}? X, exactly n times
X{n,}?  X, at least n times
X{n,m}? X, at least n but not more than m times
Possessive quantifiers  独占模式
X?+ X, once or not at all
X*+ X, zero or more times
X++ X, one or more times
X{n}+ X, exactly n times
X{n,}+  X, at least n times
X{n,m}+ X, at least n but not more than m times

这三种模式表达的意思是一样的, 在前面的讲解中我们全部使用的是贪婪模式. 那么其他两种模式的写法有什么区别呢? 通过下面的代码示例进行讲解.

public static void main(String[] args) {
    Pattern p = Pattern.compile(".{3,10}[0-9]");
    String s = "aaaa5bbbb6";//10个字符
    Matcher m = p.matcher(s);
    if(m.find()){
        System.out.println(m.start() + " - " + m.end());
    }else {
        System.out.println("not match!");
    }
}
//输出结果
0 - 10

正则表达式的意思是3~10个字符加一个数字. 在贪婪模式下匹配时, 系统会先吞掉10个字符, 这时检查最后一个是否时数字, 发现已经没有字符了, 于是吐出来一个字符, 再次匹配数字, 匹配成功, 得到0-10.

下面是非贪婪模式演示(勉强的, 不情愿的)

public static void main(String[] args) {
    Pattern p = Pattern.compile(".{3,10}?[0-9]");//添加了一个?
    String s = "aaaa5bbbb6";
    Matcher m = p.matcher(s);
    if(m.find()){
        System.out.println(m.start() + " - " + m.end());
    }else {
        System.out.println("not match!");
    }
}
//输出结果
0 - 5

在非贪婪模式下, 首先只会吞掉3个(最少3个), 然后判断后面一个是否是数字, 结果不是, 在往后吞一个字符, 继续判断后面的是否数字, 结果是, 输出0-5

最后演示独占模式, 通常只在追求效率的情况下这么做, 用得比较少

public static void main(String[] args) {
    Pattern p = Pattern.compile(".{3,10}+[0-9]");//多了个+
    String s = "aaaa5bbbb6";
    Matcher m = p.matcher(s);
    if(m.find()){
        System.out.println(m.start() + " - " + m.end());
    }else {
        System.out.println("not match!");
    }
}
//输出结果
not match!

独占模式会一下吞进10个字符, 然后判断后一个是否是数字, 不管是否匹配成功它都不会继续吞或者吐出一个字符.

相关文章
|
2月前
|
Java 程序员
Java 异常处理与正则表达式详解,实例演练及最佳实践
在 Java 代码执行期间,可能会发生各种错误,包括程序员编码错误、用户输入错误以及其他不可预料的状况。 当错误发生时,Java 通常会停止并生成错误消息,这个过程称为抛出异常。 try...catch 语句 try 语句允许您定义一段代码块,并在其中测试是否发生错误。 catch 语句允许您定义一段代码块,当 try 块中发生错误时执行该代码块。 try 和 catch 关键字成对使用,语法如下:
44 0
|
5月前
|
数据采集 Java 数据格式
【Java】正则表达式,校验数据格式的合法性。
【Java】正则表达式,校验数据格式的合法性。
59 0
|
22天前
|
XML 数据可视化 前端开发
java正则表达式
java正则表达式
C4.
|
2月前
|
Java 程序员
Java的正则表达式
Java的正则表达式
C4.
14 0
|
2月前
|
数据采集 Java
JAVA正则表达式
JAVA正则表达式
14 1
|
2月前
|
Java
Java的正则表达式结构
Java的正则表达式结构
9 0
|
2月前
|
机器学习/深度学习 Java 索引
39、一篇文章弄懂 Java 正则表达式中的量词、贪婪、勉强、独占和 String 的 matches 方法的底层【个人感觉非常值得学习】
39、一篇文章弄懂 Java 正则表达式中的量词、贪婪、勉强、独占和 String 的 matches 方法的底层【个人感觉非常值得学习】
30 0
|
2月前
|
Java
38、Java 中的正则表达式(单字符匹配和预定义字符)
38、Java 中的正则表达式(单字符匹配和预定义字符)
18 0
|
5月前
|
Java 计算机视觉
【Java 正则表达式】简单用法,注意点,我学不会正则
【Java 正则表达式】简单用法,注意点,我学不会正则
|
5月前
|
Java
Java正则表达式【详解】
Java正则表达式【详解】
40 1