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