正则表达式(JAVA版)
前言
在编程中,处理字符时,简单的直接使用match、find等方法即可。稍微复杂点的,就需要使用正则表达式进行处理了。正则表达式虽然在各语言中有所差异,但总体是差不多的,而且根据二八法则,只需要学习常见的20%的语法就可以解决工作中大部分的字符处理问题。
如果想作深入学习,有本书推荐给大家《精通正则表达式*第三版》。
本文只对java中常见的正则表达式进行简单记录,之所以用java,是因为左耳老师一直说java是必学语言,java非我工作的常用语言,要学好非熟用不可得也。
一、正则基础
常见匹配符回顾:
表达式 | 含义 | 备注 |
---|---|---|
^ | 行或字符串的起始 | |
$ | 行或字符串的结束 | |
. | 任意字符(不包括行结束符) | |
[...] | 匹配“字符集” | [0-9]、[a-z] |
[^...] | 不匹配“字符集” | |
(...) | 捕获分组 | |
(?:...) | 括号不捕获分组 | |
| | 或 | |
* | 零个或多个 | 相当于{0,} |
+ | 一个或多个 | 相当于{1,} |
? | 零个或一个 | 相当于{0,1} |
{m,n} | 至少m个但不多于n个匹配 | |
{m} | m个匹配,实际上多于m个也可以匹配,但若是捕获只会捕获m个 | |
{m,} | m个以上匹配 | |
{,n} | 最多n个匹配 | |
? | 非贪婪模式,即最小匹配即可,缺省是贪婪模式 | |
\w | 任意字母字符 | |
\W | 非单词字母字符 | |
\d | 数字字符 | |
\D | 非数字字符 | |
\s | 空格、制表符等 | 由java.lang.Character.isWhitespace()决定 |
\S | 非空格字符 |
当然,还有一些正则正反向位置匹配符没有列出来,虽然不常用,但偶尔也会用到。
二、使用方法
java中主要使用java.util.regex包中的Pattern、Matcher两个包进行处理,方法也比较多而全,详细方法可以直接检查api文档。
就个人而言,对于一项技术,除非本身非常简短才会通览其所有的使用方法;否则,只会从实际应用的场景触发,先了解目前应该用到的方法,随着以后应用场景的不同再做深入学习。毕竟人的精力和大脑的记忆容量是有限的。
常见应用场景有:
- 判断字符串是否匹配某个条件
//判断某IP地址是否符合IPv4格式(简单判断,不作范围判断):
String ip = "127.0.0.1";
if (Pattern.matches("\\d+\\.\\d+\\.\\d+\\.\\d+", ip)) {
System.out.println(ip + ":格式正确");
}
// 不区分大小写的匹配
String str = "Abc";
Pattern p = Pattern.compile("abc",Pattern.CASE_INSENSITIVE);
if(p.matcher(str).lookingAt()) {
System.out.println("match!");
}
// 不区分大小写的匹配,方法二
String str = "Abc";
if(Pattern.matches("(?i)abc",str)) {
System.out.println("match!");
}
在这里,需要注意以下matches和lookingAt方法的区别,两者都是尝试匹配正则,但matches要求整个字符串都匹配,则lookingAt不需要(字符串内部分匹配即可),但lookingAt需要从第一个字符开始匹配。
相比之前,感觉还是find更实用,matches相当于缺省加了^regex$,lookingAt相当于缺省加了^regex。
- 若字符串匹配某个条件,则获取其具体匹配的值
//查询根目录的剩余空间和使用率
String lines = "$ df -k\n" +
"文件系统 1K-块 已用 可用 已用% 挂载点\n" +
"/dev/sda1 26206076 24555196 319680 99% /\n" +
"/dev/sda2 31684544 30006348 68676 100% /data";
Pattern r = Pattern.compile("\\d+\\s+\\d+\\s+(\\d+)\\s+(\\d+%)\\s+/$");
for (String line : lines.split("\n")) {
Matcher m = r.matcher(line);
if (m.find()) {
System.out.println("根目录剩余空间:" + m.group(1) + "k,当前利用率:" + m.group(2));
}
}
//获取字符串中所有匹配的值
String nums = "123 111 478 999 666";
Pattern p = Pattern.compile("(\\d+)");
Matcher m = p.matcher(nums);
int count = 0;
while (m.find()) {
count++;
System.out.println("匹配到的第" + count + "个数字为:" + m.group(1));
}
- 替换字符、切分字符
切分字符使用String.split(regex)即可实现切分。
替换字符使用String.replace(regex)、replaceFirst、replaceAll实现替换。
// 将dog替换成cat
String str = "The dog says 666.";
Matcher m = Pattern.compile("dog").matcher(str);
str = m.replaceFirst("cat");
System.out.println(str);
- 反向引用
反向引用主要用在替换场景中,在替换的字符中引用了前文匹配到的字符内容。
// 将and前后的内容置换
String str = "dog and cat";
Matcher m = Pattern.compile("(\\S+)\\s+and\\s+(\\S+)").matcher(str);
str = m.replaceFirst("$2 and $1");
System.out.println(str);
- 动态正则
动态正则主要是指正则表达式非源码定义,而是引用外部规则,比如db中,使用也是比较简单,主要需要注意一下判断是否存在编译异常。
总结
1.java中使用正则表达式还是比较简单的,跟perl5比较类似,差别不大。
2.本文只是简单介绍,抛砖引玉,在实际应用中,还是需要考虑周全,正则不止需要达到匹配要求,同样需要考虑各种边界情况。此外,还需要考虑正则的解析效率问题。