Java 正则表达式
描述
定义:用于描述文本/字符串的一组规则
作用:使用一些自定义的规则来批量处理文本,提取信息
优点:使用很少的代码完成复杂的文本提取工作,提高工作效率
缺点:难学,难懂。在 java 对于正则表达式的支持不太友好,频繁使用会有性能问题。
常见规则
元字符
| ^ | 开始位置 |
|---|---|
| $ | 结束位置 |
| . | 单个任意字符 |
| \w | 单个”word”字符 字母/数字/下划线/汉字 |
| \s | 单个空白字符 |
| \d | 单个数字字符 |
| \b | 单词的开始或结束 |
重复
| * | 0次或多次 |
|---|---|
| + | 1次或多次 |
| ? | 0次或1次 |
| {n} | n次 |
| {n,} | >=n次 |
| {n,m} | n到m次 |
选择
| [aeiou] | 单个的a/e/i/o/u字符之一 |
|---|---|
| [0-9] | 单个数字字符 |
| [A-Z] | 单个大写字母 |
| [A-Z0-9_] | 大写字母或者数字或者下划线 |
| Hi|hi等价于[Hh]i | Hi或者hi |
Java 世界中的正则表达式
Java 中的正则表达式是比较「昂贵」的
正则表达式需要解析
- Java 需要将正则表达式字符串,转换成自己内部的数据结构,这个转换过程代价较大
- 在写代码中可以将正则表达式预编译好,需要的时候再调用就行了,减少了多次编译的开销。例:
1
private static final Pattern phoneNumber = Pattern.compile("0\\d{2}-[1-9]\\d{7}|0\\d{3}-[1-9]\\d{6}");
匹配过程非常「昂贵」
- Java 的匹配算法并不高效,用到的是回溯算法。就像机器人走迷宫一般,一直尝试,遇到墙就换一条路线继续试,直到走出迷宫
Java 中使用到正则表达式的方法:String 类中的 split,replaceAll,replacefirst,matches,以及 Matches 中的方法。可以查看相关的 demo 链接:判断是不是合法的固定电话号码,移除文件中的时间戳
分组与捕获
前面的正则表达式用法,都是用来判断字符串是否满足条件。然而,实际工作中用到较多的都是从一堆字符串中提取所需的信息,那么 Java 又是怎么做的呢?
想要将所有符合正则表达式的⽂本抓出来处理,需要先了解如下规则:
- 使⽤括号来指定⼀个被捕获的分组
- 分组的编号从1开始
- 分组的编号计算只看左括号
- (?:)不捕获和分配编号,括号只⽤于分组或标记优先
- 分组编号为0表示整个匹配的字符串
Java 中捕获数据
在 Java 中可以使用 Pattern 类的 matcher() 方法生成 Matcher 对象 ,然后我们可以对 Matcher对象进行操作。
假设有如下数据,我们需要将时间以及消费金额提取出来。
1 | |
我们可以先将该数据读取出来,保存成一个 List
1 | |
然后我们可以在类中声明 Pattern,把对匹配时间和事件的正则表达式预编译。经过在线正则表达式测试,我们可以编写如下正则 \d{4}-\d{2}-\d{2}(.*)?\d 这个可以匹配时间和金额。我们要提取,就需要给时间部分加上括号,金额部分也加上括号。正则表达式变成 (\d{4}-\d{2}-\d{2})(.*)?(\d)。
1 | |
接着对所有字符串进行匹配,匹配过程中会生成 Matcher。调用 Matcher 的 find 方法,判断是否匹配成功。因为分组编号为 0 代表匹配的全部信息,即日期+金额,group(1) 代表匹配的第一组信息,即时间,group(2)就代表金额。
1 | |
一个更为复杂的 GC 日志文件信息提取操作可查看 Demo