正则表达式
正则表达式对字符串的常见操作有:字符串的匹配、切割、替换、获取
正则表达式的构造摘要:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*
正则表达式的构造摘要
构造 匹配
字符
x 字符 x
\\ 反斜线字符
\0n 带有八进制值 0 的字符 n (0 <= n <= 7)
\0nn 带有八进制值 0 的字符 nn (0 <= n <= 7)
\0mnn 带有八进制值 0 的字符 mnn(0 <= m <= 3、0 <= n <= 7)
\xhh 带有十六进制值 0x 的字符 hh
\uhhhh 带有十六进制值 0x 的字符 hhhh
\t 制表符 ('\u0009')
\n 新行(换行)符 ('\u000A')
\r 回车符 ('\u000D')
\f 换页符 ('\u000C')
\a 报警 (bell) 符 ('\u0007')
\e 转义符 ('\u001B')
\cx 对应于 x 的控制符
字符类
[abc] a、b 或 c(简单类)
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] d、e 或 f(交集)
[a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去)
[a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去)
预定义字符类
. 任何字符(与行结束符可能匹配也可能不匹配)
\d 数字:[0-9]
\D 非数字: [^0-9]
\s 空白字符:[ \t\n\x0B\f\r]
\S 非空白字符:[^\s]
\w 单词字符:[a-zA-Z_0-9]
\W 非单词字符:[^\w]
POSIX 字符类(仅 US-ASCII)
\p{Lower} 小写字母字符:[a-z]
\p{Upper} 大写字母字符:[A-Z]
\p{ASCII} 所有 ASCII:[\x00-\x7F]
\p{Alpha} 字母字符:[\p{Lower}\p{Upper}]
\p{Digit} 十进制数字:[0-9]
\p{Alnum} 字母数字字符:[\p{Alpha}\p{Digit}]
\p{Punct} 标点符号:!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
\p{Graph} 可见字符:[\p{Alnum}\p{Punct}]
\p{Print} 可打印字符:[\p{Graph}\x20]
\p{Blank} 空格或制表符:[ \t]
\p{Cntrl} 控制字符:[\x00-\x1F\x7F]
\p{XDigit} 十六进制数字:[0-9a-fA-F]
\p{Space} 空白字符:[ \t\n\x0B\f\r]
java.lang.Character 类(简单的 java 字符类型)
\p{javaLowerCase} 等效于 java.lang.Character.isLowerCase()
\p{javaUpperCase} 等效于 java.lang.Character.isUpperCase()
\p{javaWhitespace} 等效于 java.lang.Character.isWhitespace()
\p{javaMirrored} 等效于 java.lang.Character.isMirrored()
Unicode 块和类别的类
\p{InGreek} Greek 块(简单块)中的字符
\p{Lu} 大写字母(简单类别)
\p{Sc} 货币符号
\P{InGreek} 所有字符,Greek 块中的除外(否定)
[\p{L}&&[^\p{Lu}]] 所有字母,大写字母除外(减去)
边界匹配器
^ 行的开头
$ 行的结尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符(如果有的话)
\z 输入的结尾
Greedy 数量词
X? X, 一次或一次也没有
X* X, 零次或多次
X+ X, 一次或多次
X{n} X, 恰好 n 次
X{n,} X, 至少 n 次
X{n,m} X, 至少 n 次,但是不超过 m 次
Reluctant 数量词
X?? X, 一次或一次也没有
X*? X, 零次或多次
X+? X, 一次或多次
X{n}? X, 恰好 n 次
X{n,}? X, 至少 n 次
X{n,m}? X, 至少 n 次,但是不超过 m 次
Possessive 数量词
X?+ X, 一次或一次也没有
X*+ X, 零次或多次
X++ X, 一次或多次
X{n}+ X, 恰好 n 次
X{n,}+ X, 至少 n 次
X{n,m}+ X, 至少 n 次,但是不超过 m 次
Logical 运算符
XY X 后跟 Y
X|Y X 或 Y
(X)X,作为捕获组
Back 引用
\n 任何匹配的 nth 捕获组
引用
\ Nothing, 但是引用以下字符
\Q Nothing,但是引用所有字符,直到 \E
\E Nothing,但是结束从 \Q 开始的引用
特殊构造(非捕获)
(?:X) X, 作为非捕获组
(?idmsux-idmsux) Nothing,但是将匹配标志由 on 转为 off
(?idmsux-idmsux:X) X,作为带有给定标志 on - off 的非捕获组
(?=X) X, 通过零宽度的正 lookahead
(?!X) X, 通过零宽度的负 lookahead
(?<=X) X, 通过零宽度的正 lookbehind
(?<!X) X, 通过零宽度的负 lookbehind
(?>X) X, 作为独立的非捕获组
--------------------------------------------------------------------------------
反斜线、转义和引用
反斜线字符 ('\') 用于引用转义构造,如上表所定义的,同时还用于引用其他将被解释为非转义构造的字符。因此,表达式 \\ 与单个反斜线匹配,而 \{ 与左括号匹配。
在不表示转义构造的任何字母字符前使用反斜线都是错误的;它们是为将来扩展正则表达式语言保留的。可以在非字母字符前使用反斜线,不管该字符是否非转义构造的一部分。
根据 Java Language Specification 的要求,Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义。因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释。例如,当解释为正则表达式时,字符串字面值 "\b" 与单个退格字符匹配,而 "\\b" 与单词边界匹配。字符串字面值 "hello" 是非法的,将导致编译时错误;要与字符串 (hello) 匹配,必须使用字符串字面值 "\\(hello\\)"。
字符类
字符类可以出现在其他字符类中,并且可以包含并集运算符(隐式)和交集运算符 (&&)。并集运算符表示至少包含其某个操作数类中所有字符的类。交集运算符表示包含同时位于其两个操作数类中所有字符的类。
字符类运算符的优先级如下所示,按从最高到最低的顺序排列:
1 字面值转义 \x
2 分组 [...]
3 范围 a-z
4 并集 [a-e][i-u]
5 交集 [a-z&&[aeiou]]
注意,元字符的不同集合实际上位于字符类的内部,而非字符类的外部。例如,正则表达式 . 在字符类内部就失去了其特殊意义,而表达式 - 变成了形成元字符的范围。
行结束符
行结束符 是一个或两个字符的序列,标记输入字符序列的行结尾。以下代码被识别为行结束符:
新行(换行)符 ('\n')、
后面紧跟新行符的回车符 ("\r\n")、
单独的回车符 ('\r')、
下一行字符 ('\u0085')、
行分隔符 ('\u2028') 或
段落分隔符 ('\u2029)。
如果激活 UNIX_LINES 模式,则新行符是惟一识别的行结束符。
如果未指定 DOTALL 标志,则正则表达式 . 可以与任何字符(行结束符除外)匹配。
默认情况下,正则表达式 ^ 和 $ 忽略行结束符,仅分别与整个输入序列的开头和结尾匹配。如果激活 MULTILINE 模式,则 ^ 在输入的开头和行结束符之后(输入的结尾)才发生匹配。处于 MULTILINE 模式中时,$ 仅在行结束符之前或输入序列的结尾处匹配。
组和捕获
捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C))) 中,存在四个这样的组:
1 ((A)(B(C)))
2 \A
3 (B(C))
4 (C)
组零始终代表整个表达式。
之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过 Back 引用在表达式中使用,也可以在匹配操作完成后从匹配器检索。
与组关联的捕获输入始终是与组最近匹配的子序列。如果由于量化的缘故再次计算了组,则在第二次计算失败时将保留其以前捕获的值(如果有的话)例如,将字符串 "aba" 与表达式 (a(b)?)+ 相匹配,会将第二组设置为 "b"。在每个匹配的开头,所有捕获的输入都会被丢弃。
以 (?) 开头的组是纯的非捕获 组,它不捕获文本,也不针对组合计进行计数。
*/
1、字符串的匹配matches():
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package Content;
/*
字符类
[abc] a,b或者c(简单类) --(对于只有一个字符时,[]可以省略,[1]<=>1)
[^abc] 任何字符,除了a,b,c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
[a-d[m-p]] a 到 d 或 m 到 p:[a-dm-p](并集)
[a-z&&[def]] d、e 或 f(交集)
[a-z&&[^bc]] a 到 z,除了 b 和 c:[ad-z](减去)
[a-z&&[^m-p]] a 到 z,而非 m 到 p:[a-lq-z](减去)
PS:[0-9]<=>\d,举例:"1[378][0-9]{9}" = "1[378]\\d{9}"。(在java中\需要用\来转义,所以写为\\d而不是\d)。
*/
import java.util.*;
public class RegularExpression_matches {
//匹配QQ号(长度为5-10位,纯数字组成,且不能以0开头)
static boolean match(String string) {
//定义匹配规则
//第一位不能是0的纯数字,所以我们用[1-9]来表示第一位的规则;
//第二位:我们用[0-9]来表示,后面是纯数字即[0-9],
//qq的长度只能是5~10,而规则里我们用{}来表示范围,即[0-9]{4,9}结合起来就表示:4~9个纯数字。
String regex = "[1-9][0-9]{4,9}";
//判断是否符合规则
return string.matches(regex);
}
public static void main(String[] args) {
String qq1 = "920783243";
String qq2 = "020783243";
String qq3 = "120783243";
System.out.println(match(qq1));
System.out.println(match(qq2));
System.out.println(match(qq3));
}
}
2、字符串の切割:split()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*
***Greedy(贪婪) 数量词***
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次
***Reluctant(勉强) 数量词***
X?? X, 一次或零次
X*? X, 零次或多次
X+? X,一次或多次
X{n}? X, 恰好n次
X{n,}? X, 至少n次
X{n, m}? X,至少n次, 但是不超过m次
***Possessive(占有)数量词 ***
X?+ X, 一次或零次
X*+ X, 零次或多次
X++ X,一次或多次
X{n}+ X, 恰好n次
X{n,}+ X,至少n次
X{n,m}+ X,至少n次, 但是不超过m次
*/
import java.util.*;
public class RegularExpression_split {
static String[] strings;
static void split(String string) {
String regex = "@+";
strings = string.split(regex);
}
public static void main(String[] args) {
//切割字符串"Amy@@@Mike@@Alice@Johe".
String string ="Amy@@@Mike@@Alice@Johe";
strings = new String[10];
split(string);
for(String e:strings) {
System.out.println(e);
}
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package Content;
/*
1、预定义字符类 :
. 任何字符(与行结束符可能匹配也可能不匹配)
2、组和捕获
捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C))) 中,存在四个这样的组:
<1>((A)(B(C)))
<2>\A
<3>(B(C))
<4>(C)
组零始终代表整个表达式。
之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。
捕获的子序列稍后可以通过 Back 引用在表达式中使用,也可以在匹配操作完成后从匹配器检索。
与组关联的捕获输入始终是与组最近匹配的子序列。
如果由于量化的缘故再次计算了组,则在第二次计算失败时将保留其以前捕获的值(如果有的话)
例如,将字符串 "aba" 与表达式 (a(b)?)+ 相匹配,会将第二组设置为 "b"。在每个匹配的开头,所有捕获的输入都会被丢弃。
以 (?) 开头的组是纯的非捕获组,它不捕获文本,也不针对组合计进行计数。
*/
//PS:“haha.lisi.nihao”用"."来切割:这个小点可以代表任何字符,所以我们需要用转义字符\来将“.”转义为普通的点,所以只要把regex = "\\."即可。
public class RegularExpression_split2 {
static String[] strings;
static void split(String string) {
//()来表示组,对组中的数据进行引用:
//regex = "(.)\\1"就表示:
//某一字符出现了两次((.)来表示任意字符,而\\1是对组(.)中的字符进行复用,合起来就是:两个相同的字符)
//使用+号来表示出现多次,最终叠词就表示为:regex = "(.)\\1+"。
String regex = "(.)\\1+";
strings = string.split(regex);
}
public static void main(String[] args) {
//"Amy@@Alice¥¥MikezzzNancy"按叠词切割.
String string = "Amy@@Alice¥¥MikezzzNancy";
split(string);
for(String e:strings) {
System.out.println(e);
}
}
}
3、字符串の替换:replaceAll()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package Content;
public class RegularExpression_replaceAll {
public static void main(String[] args) {
//将字符串“Amy@@Alice¥¥MikezzzNancy”中的叠词替换为:“、”。
String string = "Amy@@Alice¥¥MikezzzNancy";
String regex = "(.)\\1+";
String newString = string.replaceAll(regex, "、");
System.out.println(newString);
}
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package Content;
public class RegularExpression_replaceAll2 {
public static void main(String[] args) {
//将字符串“Amy@@Alice¥¥MikezzzNancy”中的叠词替换为:“Amy@Alice¥MikezNancy”。
String string = "Amy@@Alice¥¥MikezzzNancy";
String regex = "(.)\\1+";
//使用$1来复用组中第1组的值(即叠词的字符)
String newString = string.replaceAll(regex, "$1");
System.out.println(newString);
}
}
4、字符串的获取:
正则表达式其实是封装成了Pattern类,所以字符串的匹配、切割、替换都是调用了Pattern类中的方法。所以如果我们需要获取指定字符串中的子串,首先同样的我们需要进行字符串匹配,然后判断指定字符串中是否有匹配的子串,有就获取,没有就获取不到。
获取子串的步骤:
1、描述要获取的子串:匹配子串
2、使用正则表达式的封装类Pattern来获取匹配器
3、使用匹配器中的方法group()获取字符串的匹配的子串
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package Content;
//获取字符串"Hello! I am Nancy,Nice to meet you!"中为5个字母的单词。即Hello、Nancy。
/*
第一步,我们要对子串进行匹配,即两个字母的单词,字母可以用[a-zA-Z]来表示,范围是两个,所以regex = "[a-zA-Z]{2}"。
但这样不够准确,我们需要的是单词,而不是三个字母,所以要用到“边界匹配器”
边界匹配器
^ 行的开头
$ 行的结尾
\b 单词边界
\B 非单词边界
\A 输入的开头
\G 上一个匹配的结尾
\Z 输入的结尾,仅用于最后的结束符(如果有的话)
\z 输入的结尾
*/
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegularExpression_get {
public static void main(String[] args) {
String string = "Hello! I am Nancy,Nice to meet you!";
// 1、匹配子串
String regex = "\\b[a-zA-Z]{5}\\b";
// 2、获取匹配器
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(string);
// 3、使用匹配器的group()方法来获取:(find方法是判断是否具有匹配子串)
// start():返回当前匹配的子串的第一个字符在原目标字符串中的索引位置 。
// end():返回当前匹配的子串的最后一个字符在原目标字符串中的索引位置 。
// reset() 方法会重置Matcher 内部的 匹配状态。当find() 方法开始匹配时,Matcher 内部会记录截至当前查找的距离。调用reset() 会重新从文本开头查找。
// 也可以调用 reset(CharSequence) 方法. 这个方法重置Matcher,同时把一个新的字符串作为参数传入,用于代替创建 Matcher的原始字符串。
while (matcher.find()) {
System.out.println(matcher.start() + " " + matcher.end());
System.out.println(matcher.group());
}
String string1 = "Amy and Amy or Amy and andAmy";
String regex1 = "Amy";
pattern = pattern.compile(regex1);
matcher = pattern.matcher(string1);
System.out.println(matcher.lookingAt()); //lookingAt():检测目标字符串是否以匹配的子串起始。
// System.out.println(matcher.groupCount());
while (matcher.find()) {
System.out.println(matcher.group());
}
}
}