文章目录
1 正则表达式解析原理
正则引擎大体上可分为不同的两类:DFA(确定型有穷自动机)和NFA(非确定型有穷自动机),
而NFA又基本上可以分为传统型NFA和POSIX NFA。DFA引擎因为不需要回溯,所以匹配快速,但
不支持捕获组,所以也就不支持反向引用和$number这种引用方式,目前使用DFA引擎的语言和工
具主要有awk、egrep 和 lex。POSIX NFA主要指符合POSIX标准的NFA引擎,主要是提供在找
到最左侧最长匹配之前,它将继续回溯。原理在此暂不深究。
Java源代码的字符串中的反斜线被解释为Unicode 转义或其他字符转义。
因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释。
2 普通字符和11个元字符(匹配量词使用 )
3 常用预定义匹配符
// 反斜杠
/t 间隔 ('/u0009')
/n 换行 ('/u000A')
/r 回车 ('/u000D')
/d 数字 等价于[0-9]
/D 非数字 等价于[^0-9]
/s 空白符号 [/t/n/x0B/f/r]
/S 非空白符号 [^/t/n/x0B/f/r]
/w 单独字符 [a-zA-Z_0-9]
/W 非单独字符 [^a-zA-Z_0-9]
/f 换页符
/e Escape
/b 一个单词的边界
/B 一个非单词的边界
/G 前一个匹配的结束
^为限制开头
$为限制结尾
. 条件限制除/n以外任意一个单独字符
4 捕获组(正则表达式中的组)
正则表达式中每个"()"内的部分算作一个捕获组,每个捕获组都有一个编号,从1,2...,
编号0代表整个匹配到的内容。可对组进行引用。
捕获组应用示例:
/** 捕获组 */
@Test
public void test() {
Pattern pattern = Pattern.compile("(\\d{5})((\\w{2})(\\d{3}))");
Matcher matcher = pattern.matcher("55555HH999");
while (matcher.find()) {
/** 获取捕获组内容*/
System.out.println(matcher.group());
System.out.println(matcher.group(0));
System.out.println(matcher.group(1));
System.out.println(matcher.group(2));
System.out.println(matcher.group(3));
System.out.println(matcher.group(4));
}
System.out.println("===============");
String str = "55555HH999";
String regex = "(\\d{5})(\\w{2})(\\d{3})";
//replaceAll(String regex, String replacement)
//java.util.regex.Pattern.compile(regex).matcher(str).replaceAll(repl)
/**使用捕获组引用*/
System.out.println(str.replaceAll(regex, "$0啦啦"));
System.out.println(str.replaceAll(regex, "$1哈哈"));
System.out.println(str.replaceAll(regex, "$2丫丫"));
System.out.println(str.replaceAll(regex, "$3嘎嘎"));
}
/** 结果 */
// 55555HH999
// 55555HH999
// 55555
// HH999
// HH
// 999
// ===============
// 55555HH999啦啦
// 55555哈哈
// HH丫丫
// 999嘎嘎
5 正则表达式中的特定限制条件
加入特定限制条件「[]」。
[a-z] 条件限制在小写a to z范围中一个字符
[A-Z] 条件限制在大写A to Z范围中一个字符
[a-zA-Z] 条件限制在小写a to z或大写A to Z范围中一个字符
[0-9] 条件限制在小写0 to 9范围中一个字符
[0-9a-z] 条件限制在小写0 to 9或a to z范围中一个字符
[0-9[a-z]] 条件限制在小写0 to 9或a to z范围中一个字符(交集)
[]中加入后加再次限制条件「[]」.
[^a-z] 条件限制在非小写a to z范围中一个字符
[^A-Z] 条件限制在非大写A to Z范围中一个字符
[^a-zA-Z] 条件限制在非小写a to z或大写A to Z范围中一个字符
[^0-9] 条件限制在非小写0 to 9范围中一个字符
[^0-9a-z] 条件限制在非小写0 to 9或a to z范围中一个字符
[^0-9[a-z]] 条件限制在非小写0 to 9或a to z范围中一个字符(交集)
6 贪婪匹配与非贪婪匹配
**介绍**:
(1)贪婪模式在整个表达式匹配成功的前提下,尽可能多的匹配;
(2)非贪婪模式在整个表达式匹配成功的前提下,尽可能少的匹配;
非贪婪模式只被部分NFA引擎所支持。
**具体使用**:
(1)属于贪婪模式的量词,也叫做匹配优先量词,包括:“{m,n}”、“{m,}”、“?”、“*”和“+”。
(2)在一些使用NFA引擎的语言中,在匹配优先量词后加上“?”,即变成属于非贪婪模式的量词,
也叫做忽略优先量词。包括:“{m,n}?”、“{m,}?”、“??”、“*?”和“+?”。
应用示例:
/** 正则贪婪与非贪婪模式 */
@Test
public void test() {
/** 贪婪匹配 */
Pattern pattern1 = Pattern.compile("\\d{3,5}");
Matcher matcher1 = pattern1.matcher("55577HH999");
while (matcher1.find()) {
System.out.println(matcher1.group());
}
System.out.println("------");
/** 非贪婪匹配 */
Pattern pattern2 = Pattern.compile("\\d{3,5}?");
Matcher matcher2 = pattern2.matcher("55577HH999");
while (matcher2.find()) {
System.out.println(matcher2.group());
}
}
/**结果*/
// 55577
// 999
// ------
// 555
// 999
7 正则使用建议
1. 使用正确的边界匹配器(^、$、\b、\B等),限定搜索字符串位置;
2. 尽量不适用通配符".";字符使用具体的元字符、字符类(\d、\w、\s等);
3. 使用正确的量词(+、*、?、{n,m}),如果能够限定长度,匹配最佳;
4. 使用非捕获组、原子组,减少没有必要的捕获,减少内存;
8 使用示例
(1)非打印不可见字符替换为空格
"非打印不可见字符"实属于ASCII 码中的控制字符,它们是(十进制表示)0到31、以及127。
分别代表什么可查询ASCII码表。Java用的是Unicode编码,8位的ASCII码包含在Unicode中,是从0~127的。
/**
* 替换掉文本的不可见字符
* 1 换行符,回车符不替换
* 2 其它不可见字符,替换为空格
*
* 0x0A 换行符
* 0x0D 回车符
* 0x20 空格符
* @param content
* @return
*/
private String filterFlightStr(String content) {
if (content != null && content.length() > 0) {
char[] charArr = content.toCharArray();
for (int i = 0; i < charArr.length; i++) {
if ((charArr[i] < 0x20) || charArr[i] == 0x7F) {
if (charArr[i] != 0x0A && charArr[i] != 0x0D) {
//其它不可见字符,替换为空格符号
charArr[i] = 0x20;
}
}
}
return new String(charArr);
}
return "";
}
(2)字符串开头验证
//查找以Java开头,任意结尾的字符串
Pattern pattern = Pattern.compile("^Java.*");
Matcher matcher = pattern.matcher("Java是最好的语言.");
boolean b= matcher.matches();
//当条件满足时,将返回true,否则返回false
System.out.println(b);
(3)以多条件分割字符串
Pattern pattern = Pattern.compile("[-,\\s,:]+");
String[] strs = pattern.split("Java Hello-World Java,Hello,,World|Sun");
//ArrayList<String> arrayList = Lists.newArrayList(str.split("[-,\\s,:]+"));
for (int i=0;i<strs.length;i++) {
System.out.println(strs[i]);
}
(4)文字替换(首次出现字符)
Pattern pattern = Pattern.compile("正则表达式");
Matcher matcher = pattern.matcher("正则表达式 Hello World,正则表达式 Hello World");
//替换第一个符合正则的数据
System.out.println(matcher.replaceFirst("Java"));
//输出:Java Hello World,正则表达式 Hello World
(5)文字替换(全部)
Pattern pattern = Pattern.compile("正则表达式");
Matcher matcher = pattern.matcher("正则表达式 Hello World,正则表达式 Hello World");
//替换第一个符合正则的数据
System.out.println(matcher.replaceAll("Java"));
//输出:Java Hello World,Java Hello World
(6)文字替换(置换字符)
Pattern pattern = Pattern.compile("正则表达式");
Matcher matcher = pattern.matcher("正则表达式 Hello World,正则表达式 Hello World.");
StringBuffer strBuffer = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(strBuffer, "Java");
}
matcher.appendTail(strBuffer);
System.out.println(strBuffer.toString());
//输出:Java Hello World,Java Hello World.