“语法是个难点,但它确实是一种简洁、动态的语言。它提供了一种完全通用的方式,能解决各种字符串处理相关问题:匹配、选择、编辑以及验证。”
1 基础
1.1 String 类自带方法
1.1.1 matches(String reg)
检查字符串是否匹配所诉正则表达式。
1.1.2 split(String reg)、split(String reg, int limit)
将字符串从正则表达式匹配的地方切开;
limit 限制字符串分割的次数;
1.1.3 替换
replaceFirst(String reg, String str): 只替换正则表达式第一个匹配的子串;
replaceAll(String reg, String str): 替换所有匹配的地方;
2.创建正则表达式
符号 | 含义 |
\xhh | 十六进制为oxhh的字符 |
\uhhhh | 十六进制表示为oxhhhh的Unicode字符 |
\t | 制表符Tab |
\n | 换行符 |
\r | 回车 |
\f | 换页 |
\e | 转义(Escape) |
表 字符
符号 | 含义 |
. | 任意字符 |
[abc] | 包含a、b、c的任何字符(和a|b|c作用相同) |
[a-zA-Z] | 从a到z或A到Z到任何字符 |
[abc[hij]] | 任意a、b、c、h、i和j字符(合并) |
[a-z&&[hij]] | 任意h、i或j(交) |
\s | 空白符(空格、tab、换行、换页和回车) |
\S | 非空白字符[^\s] |
\d | 数字[1-9] |
\D | 非数字[^0-9] |
\w | 词字符[a-zA-Z0-9] |
\W | 非词字符 |
表 字符类
符号 | 含义 |
XY | Y跟在X后面 |
X|Y | X或Y |
(X) | 捕获组。可以在表达式中用\i引用第i个捕获组 |
表 逻辑操作符
符号 | 含义 |
^ | 一行到起始 |
$ | 一行到结束 |
\b | 词到边界 |
\B | 非词到边界 |
\G | 前一个匹配结束 |
表 边界匹配符
2.1 捕获组
把正则表达式中子表达式匹配的内容保存的内存中,以数字编号或显示命名。这种引用既可以在正则表达式内部,也可以在正则表达式外部。
捕获组有两种形式:
普通捕获组:(Expression);
命名捕获组: (?<name>Expression);
2.1.1 捕获组编号规则
编号为0的捕获组指的是正则表达式整体。
1)普通捕获组编号规则:按照“(”出现的顺序,从左到右,从1开始进行编号的;
图 普通捕获组编号
2)命名捕获组编号规则:与上述规则一样;
正则表达式:(?<year>\d{4})-(?<date>\d{2}-(?<day>\d\d))
3)普通捕获组与命名捕获组混合编号规则:首先按照普通捕获组中“(”出现的先后顺序,从左到右,从1开始进行编号,当普通捕获组编号完成后,再按命名捕获组中“(”出现的先后顺序,从左到右,接着普通捕获组的编号值继续进行编号。
图 普通捕获组与命名捕获组混合编号
2.1.2 捕获组的引用
在正则表达式内部进行引用是反向引用,其作用通常是用来查找或限定重复,限定指定标识配对出现。
普通捕获组反向引用:\k<number>,通常简写为\number
命名捕获组反向引用:\k<name>或者\k'name'
2.2 \b、\B、\G
正则中的位置分为“字符的占位”和“字符的间隙”。 字符的占位是显示位置,肉眼可见的字母、符号、空格都是可以占位的字符,字符的间隙是隐式的位置,即显示位置之间的位置。
边界是指占位的字符左右的间缝位置。
正则表达式的单词\w是指[0-9a-zA-Z_]
\b: 单词边界。匹配的是一个间缝位置,它的前一个字符和后一个字符必须是\w (字母数字)和 \W (非字母数字),即匹配必须出现在 \w (字母数字)和 \W (非字母数字)字符之间的边界上。
\B: 非单词边界。左右占位的字符,都必须是 \w。
\G:前一个匹配的结束。匹配必须在上一个匹配结束的位置进行;如果以前没有匹配项,则从开始进行匹配的字符串中的位置进行。
模式 \G\(\d\) 匹配 "(1)(3)(5)[7](9)" 中的 "(1)"、 "(3)" 和 "(5)"。
3 量词
3.1 类型
贪婪型:贪婪表达式会为所有可能的模式发现尽可能多的匹配;
勉强型:有问好来指定,这个量词匹配满足模式所需的最少字符数;
占有型:只要在Java语言中才可用,通过在表达式后加上 + 号来执行此模式。
在正则表达式被应用于字符串时,它会产生相当多的状态,以便在匹配失败时可以回溯。而“占有的”量词并不保存这些中间状态,因此它们可以防止回溯。它们常常用来防止正则表达式失控,可以使正则表达式执行起来更有效;
static void testQuantifier() {
String str = "article is art at ad";
System.out.println("old str:" + str);
//贪婪型
System.out.println(str.replaceAll("a.*t","***"));
//勉强型
System.out.println(str.replaceAll("a.*?t", "***"));
//占有型
System.out.println(str.replaceAll("a.*+t", "***"));
}
运行结果:
old str:article is art at ad
*** ad
***icle is *** *** ad
article is art at ad
4 Pattern 和 Matcher
4.1 Macher 上的方法
appendReplacement(StringBuffer sb, String replacement):
执行渐进式替换,将当前匹配子串替换为指定字符串,以及将替换后的子串以及其之前到上次匹配子串之后的字符串添加到StringBuffer对象里。
appendTail(StringBuffer stuf)
将上次匹配工作后剩余的字符串添加到StringBuffer里,在执行了一次或多次appendReplacement之后,调用此方法可以将输入字符串余下的部分复制到StringBuffer中。
static void testAppend() {
String str = " 1 is One, 2 is Two";
Pattern pattern = Pattern.compile("\\d");
Matcher matcher = pattern.matcher(str);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb,"Num");
System.out.println("sb:" + sb);
}
matcher.appendTail(sb);
System.out.println("final sb:" + sb);
}
运行结果:
sb: Num
sb: Num is One, Num
final sb: Num is One, Num is Two
group(): 返回与上一个匹配项匹配到输入子序列,group(int i) 返回在前一次匹配操作期间制定的组号。
reset(String newStr),将现有的Matcher对象应用于一个新的字符串。 使用不带参数的reset()方法,可以将Matcher对象重新设置到当前字符序列的起始位置。
4.2 模式标记
编译标记 | 效果 |
Pattern.CANON_EQ | 规范等效,正则表达式 \u003f 会匹配字符 ?, 因为 ?的 unicode 编码就是 \u003f。 |
Pattern.CASE_INSENSITIVE(?i) | 允许匹配模式不必考虑大小写。 |
Pattern.COMMENTS(?x) | 空格符将被忽略,并且以#开始直到行末的注释也会被忽略掉。 |
Pattern.DOTALL(?s) | 表达式”. ”匹配所有字符,包括行终结符。默认情况下,”.”表达式不匹配行终结符。 |
Pattern.MULTILINE(?m) | 在多行模式下,表达式^和$分别匹配一行的开始和结束。^还匹配输入字符串的开始,而$还匹配输入字符串的结尾。默认情况下,这些表达式仅匹配输入的完整字符串的开始和结束。 |
Pattern.UNICODE_CASE(?u) | 当指定这个标记,并且开启Pattern.CASE_INSENSITIVE时,大小写不敏感的匹配将按照与Unicode标准相一致的方式进行。默认情况下,大小写不敏感的匹配假定只能在US- ASCII字符集中的字符才能进行。 |
Pattern.UNIX_LINES(?d) | 在”.”,”^”和”$”中,只识别行终结符\n |
表 Java 模式标记
可以通过“或”(|)操作符组合多个标记的功能:
Pattern.compile(“^java”,Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
同时设置了忽略大小写模式及多行模式。
5 扫描输入
5.1 BufferReader
从缓冲区中读取内容,所有的输入字节数据都将放在缓冲区中。
方法 | 类型 | 描述 |
new BufferReader(Reader in) | 构造 | 接收一个Reader 类实例 |
String readLine() throws IOException | 方法 | 一次性从缓冲区中将内容读取 |
表BufferReader 方法说明
5.2 Scanner
扫描输入文本。Scanner构造器可以接受任何类型的输入对象,报告File对象、InputStream、String 及 Readable对象。
图 temp.text 内容
static void testScannerDelimiter() throws FileNotFoundException {
File file = new File("/Users/xxx/Desktop/temp.txt");
Scanner scanner = new Scanner(file);
scanner.useDelimiter("\\n");
while (scanner.hasNext()) {
System.out.println(scanner.next());
}
}
运行结果:
Hello Java!
This is a temp text.
The password is 123456
What is your name?
static void testScannerPattern() throws FileNotFoundException {
String str = " DESCRIPTION DESCRIPTION 'Header for scroller'";
Scanner scanner = new Scanner(str);
String pattern = "'?[a-zA-Z]+";
while (scanner.hasNext(pattern)) {
scanner.next(pattern);
MatchResult match = scanner.match();
System.out.println(match.group());
}
}
运行结果:
DESCRIPTION
DESCRIPTION
'Header
for