admin管理员组

文章数量:1579410

文章目录

  • 1. 正则表达式:
    • 1.1 简单匹配
    • 1.2 字符,字符类、预定义字符
    • 1.3 数量词
    • 1.4 边界条件
    • 1.5 `split` 分割
    • 1.6 逻辑运算
    • 1.7 使用正则表达式控制输入
    • 1.8 正则表达式的简化写法(语法糖)
    • 1.9 校验数字的表达式
    • 1.10 校验字符的表达式
    • 1.11 特殊需求表达式
  • 2. 正则表达式基本用法
    • 2.1 基本匹配
    • 2.2 元字符
      • 2.2.1 英文句号
      • 2.2.2 字符集
        • 2.2.2.1 否定字符集
      • 2.2.3 重复
        • 2.2.3.1 星号
        • 2.2.3.2 加号
        • 2.2.3.3 问号
      • 2.2.4 花括号
      • 2.2.5 字符组
      • 2.2.6 分支结构
      • 2.2.7 转义特殊字符
      • 2.2.8 定位符
        • 2.2.8.1 插入符号
        • 2.2.8.2 美元符号
    • 2.3 简写字符集
    • 2.4 断言
      • 2.4.1 正向先行断言
      • 2.4.2 负向先行断言
      • 2.4.3 正向后行断言
      • 2.4.4 负向后行断言
    • 2.5 标记
      • 2.5.1 不区分大小写
      • 2.5.2 全局搜索
      • 2.5.3 多行匹配
    • 2.6 常用正则表达式
  • 3. 正则表达式进阶
    • 3.1 `POSIX` 字符类
    • 3.2 `java.lang.Character` 类
    • 3.3 `Unicode` 块和类别的类
    • 3.4 边界匹配器
    • 3.5 `Back` 引用
    • 3.6 模式匹配
    • 3.7 `split` 结果阈值 `limit`

1. 正则表达式:

正则表达式是一种被用于从文本中检索符合某些特定模式的文本。

1.1 简单匹配

\d 数字:[0-9] 
String input = "123a";
String regex = "\\d{4}"; // 匹配4个数字
System.out.println(input.matches(regex) == false);
input = "1234";
System.out.println(input.matches(regex) == true);

1.2 字符,字符类、预定义字符

	字符 
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] 

	String input = "123a";
	String  regex = "a";
	System.out.println("a".matches(regex) == true);
	System.out.println("b".matches(regex) == false);
	regex = "\\\\";
	// System.out.println(regex);
	System.out.println("\\"); // \
	System.out.println("\\".matches(regex) == true);
	
	regex = "\\x61"; // 十六进制的字符 hh
	System.out.println("a".matches(regex) == true);
	
	regex = "\\u9999"; // 十六进制的字符 hhhh
	System.out.println("香".matches(regex) == true);
	
	// regex = "\\c97";
	// System.out.println(regex);
	
	regex = "\\."; // 代表一个点
	System.out.println(".".matches(regex) == true);
	System.out.println("a".matches(regex) == false);
	
	regex = "[123]";
	System.out.println("123".matches(regex) == false);
	System.out.println("1".matches(regex) == true);
	System.out.println("2".matches(regex) == true);
	System.out.println("3".matches(regex) == true);
	System.out.println("12".matches(regex) == false);
	System.out.println("[".matches(regex) == false);
	
	regex = "[^123]"; // 不是123的任意一个字符
	System.out.println("123".matches(regex) == false);
	System.out.println("1".matches(regex) == false);
	System.out.println("2".matches(regex) == false);
	System.out.println("3".matches(regex) == false);
	System.out.println("4".matches(regex) == true);
	System.out.println("a".matches(regex) == true);
	System.out.println("".matches(regex) == false);
	
	regex = "[a-z]"; // 所有的英文小写字母
	System.out.println("abc".matches(regex) == false);
	System.out.println("a".matches(regex) == true);
	System.out.println("z".matches(regex) == true);
	System.out.println("1".matches(regex) == false);
	System.out.println("-".matches(regex) == false);
	System.out.println(" ".matches(regex) == false);
	
	regex = "[a-zA-Z]"; // 所有英文字母
	System.out.println("a".matches(regex) == true);
	System.out.println("A".matches(regex) == true);
	System.out.println("5".matches(regex) == false);
	
	regex = "[a-zA-Z0-9]"; // 所有英文字母
	System.out.println("a".matches(regex) == true);
	System.out.println("A".matches(regex) == true);
	System.out.println("5".matches(regex) == true);
	
	regex = "[a-d[m-p]]"; // a 到 d 或 m 到 p:[a-dm-p] (并集)
	regex = "[a-z&&[def]]"; // d、e、f (交集)
	regex = "[a-z&&[^bc]]"; // 不是bc的小写字母
	regex = "[a-z&&[^m-p]]"; // 不是m-p的小写字母
	
	// 预定义字符
	regex = "\\d"; // [0-9]
	regex = "\\D"; // [^0-9]
	
	regex = "\\w"; // 单词字符:[a-zA-Z_0-9] 注意还有一个下划线
	regex = "\\W"; // 非单词字符:[^a-zA-Z_0-9] 
	
	regex = "\\s"; // 空白字符:[ \t\n\x0B\f\r] 注意还有空格
	regex = "\\S"; // 非空白字符:[^ \t\n\x0B\f\r] 
	regex = "."; // 任何字符(与行结束符可能匹配也可能不匹配)
	
	regex = "[\\u4E00-\\u9FA5]"; // 汉字(简易派别)
	System.out.println("A".matches(regex) == false);
	System.out.println("汉".matches(regex) == true);

1.3 数量词

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 次 
String regex = "a{3}"; // 3个
System.out.println("aaa".matches(regex) == true);
System.out.println("aa".matches(regex) == false);
System.out.println("aaaa".matches(regex) == false);
System.out.println("aaab".matches(regex) == false);
System.out.println("aabab".matches(regex) == false);

regex = "a{0,3}"; // 0-3
System.out.println("aa".matches(regex) == true);
System.out.println("".matches(regex) == true);
System.out.println("a".matches(regex) == true);
System.out.println("aaa".matches(regex) == true);
System.out.println("aaaa".matches(regex) == false);

regex = "a{1,}"; // 1个或多个
System.out.println("".matches(regex) == false);
System.out.println("a".matches(regex) == true);
System.out.println("b".matches(regex) == false);
System.out.println("aaaaaaaaaaaaaaaaaaaa".matches(regex) == true);

regex = "a{0,4}"; // 4个字符以下
System.out.println("".matches(regex) == true);
System.out.println("a".matches(regex) == true);
System.out.println("aaa".matches(regex) == true);
System.out.println("aaaa".matches(regex) == true);
System.out.println("aaaaa".matches(regex) == false);

regex = "a*"; // 0个或多个{0,}
System.out.println("".matches(regex) == true);
System.out.println("a".matches(regex) == true);
System.out.println("aaa".matches(regex) == true);

// 0个或者1个{0,1}
regex = "a?"; // 0个或1个
System.out.println("".matches(regex) == true);
System.out.println("a".matches(regex) == true);
System.out.println("aaa".matches(regex) == false);

// 1个或者多个{1,}
regex = "a+"; // 1个或多个
System.out.println("".matches(regex) == false);
System.out.println("a".matches(regex) == true);
System.out.println("aaa".matches(regex) == true);

1.4 边界条件

边界匹配器 
^ 行的开头 
$ 行的结尾 
\b 单词边界 
\B 非单词边界 
\A 输入的开头 
\G 上一个匹配的结尾 
\Z 输入的结尾,仅用于最后的结束符(如果有的话) 
\z 输入的结尾 
	String regex = "a";
	System.out.println("abcabc".replaceAll(regex, "*").equals("*bc*bc"));
	regex = "^a"; // 不在方括号里,表示一行字符串的开头
	System.out.println("abcabc".replaceAll(regex, "*").equals("*bcabc"));
	regex = "a$"; // 不在方括号里,表示一行字符串的结尾
	System.out.println("abcabc".replaceAll(regex, "*").equals("abcabc"));
	System.out.println("abcabca".replaceAll(regex, "*").equals("abcabc*"));
	
	regex = "\\ba"; // 不在方括号里,单词边界
	System.out.println("abcabca abcabca".replaceAll(regex, "*").equals("*bcabca *bcabca"));
	
	regex = "a\\b"; // 不在方括号里,单词边界
	System.out.println("abcabca abcabca".replaceAll(regex, "*").equals("abcabc* abcabc*"));
	
	regex = "a\\B"; // 不在方括号里,非单词边界
	System.out.println("abcabca abcabca".replaceAll(regex, "*").equals("*bc*bca *bc*bca"));
	
	regex = "\\Aa"; // 不在方括号里,输入的开头
	System.out.println("abcabca abcabca".replaceAll(regex, "*").equals("*bcabca abcabca"));
	
	System.out.println("abcabca\r\nabcabca".replaceAll("^a", "*").equals("*bcabca\r\nabcabca"));
	System.out.println("abcabca\r\nabcabca".replaceAll("\\Aa", "*").equals("*bcabca\r\nabcabca"));
	
//	Pattern compile = Patternpile("^a",Pattern.MULTILINE);
//	Matcher matcher = compile.matcher("abcabca\r\nabcabca");
//	System.out.println(matcher.replaceAll("*").equals("*bcabca\r\n*bcabca")); 
	
//	Pattern compile = Patternpile("\\Aa",Pattern.MULTILINE);
//	Matcher matcher = compile.matcher("abcabca\r\nabcabca");
//	System.out.println(matcher.replaceAll("*").equals("*bcabca\r\nabcabca")); 
	
//	Pattern compile = Patternpile("a\\z",Pattern.MULTILINE);
//	Matcher matcher = compile.matcher("abcabca\r\nabcabca");
//	System.out.println(matcher.replaceAll("*").equals("abcabca\r\nabcabc*"));
	
	Pattern compile = Pattern.compile("a\\z",Pattern.MULTILINE);
	Matcher matcher = compile.matcher("abcabca\r\na" + Character.valueOf((char) 0) + "bacbaca");
	System.out.println(matcher.replaceAll("*"));
	/**
	* abcabca
	* a bacbac*
	**/

1.5 split 分割

String input = "苹果,草莓,香蕉";
String[] array = input.split("[,,]");
System.out.println(Arrays.toString(array)); // [苹果, 草莓, 香蕉]

1.6 逻辑运算

System.out.println("ab".matches("\\Sb") == true); // \S 非空白字符
System.out.println("ba".matches("\\Sb") == false);

System.out.println("ba".matches("a|b") == false); // a或者b
System.out.println("b".matches("a|b") == true);
System.out.println("a".matches("a|b") == true);

1.7 使用正则表达式控制输入

String tip = "请输入您要注册的学生编号,姓名,出生日期,用逗号分开"
		+ "0:返回上级";
//	String input= "1001,李四,1999-10-06";
//	String input= "0";
String input= "1001,李四,1999-10-06";
String regex = "0|\\d{1,9}[,,][\\u4E00-\\u9FA5]{2,10}[,,]\\d{4}-\\d{2}-\\d{2}";
System.out.println(input.matches(regex) == true);
System.out.println("1001,李四,1999-10-06".matches(regex) == true);
System.out.println("1001李四,1999-10-06".matches(regex) == false);
System.out.println("0".matches(regex) == true);

1.8 正则表达式的简化写法(语法糖)

private static void test8() {
//	正则表达式的简化写法(语法糖)
//	捕获的语法糖
	String tip = "1.注册学生"
			+ "2.删除"
			+ "3.修改"
			+ "4.显示学生信息"
			+ "0.退出系统";
	print(tip);
	/**
	1.注册学生
	2.删除
	3.修改
	4.显示学生信息
	0.退出系统
	**/
}
private static void print(String tip) {
//	String regex = "(\\d\\.)(\\S*)"; // 捕获组
	String regex = "(\\B\\d\\.)"; // 捕获组
//	System.out.println(tip.replaceAll(regex, "--$0-----$1-------$2"));
	System.out.println(tip.replaceAll(regex, "\n$1"));
}

1.9 校验数字的表达式

  • 数字:^[0-9]\*$
  • n位的数字:^\d{n}$
  • 至少n位的数字:^\d{n,}$
  • m-n位的数字:^\d{m,n}$
  • 零和非零开头的数字:^(0|[1-9][0-9]\*)$
  • 非零开头的最多带两位小数的数字:^([1-9][0-9]\*)+(\.[0-9]{1,2})?$
  • 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})$
  • 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$
  • 有两位小数的正实数:^[0-9]+(\.[0-9]{2})?$
  • 有1~3位小数的正实数:^[0-9]+(\.[0-9]{1,3})?$
  • 非零的正整数:^[1-9]\d\*$^([1-9][0-9]\*){1,3}$^\+?[1-9][0-9]\*$
  • 非零的负整数:^\-[1-9][]0-9"\*$^-[1-9]\d\*$
  • 非负整数:^\d+$^[1-9]\d\*|0$
  • 非正整数:^-[1-9]\d\*|0$^((-\d+)|(0+))$
  • 非负浮点数:^\d+(\.\d+)?$^[1-9]\d\*\.\d\*|0\.\d\*[1-9]\d\*|0?\.0+|0$
  • 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$^(-([1-9]\d\*\.\d\*|0\.\d\*[1-9]\d\*))|0?\.0+|0$
  • 正浮点数:^[1-9]\d\*\.\d\*|0\.\d\*[1-9]\d\*$^(([0-9]+\.[0-9]\*[1-9][0-9]\*)|([0-9]\*[1-9][0-9]\*\.[0-9]+)|([0-9]\*[1-9][0-9]\*))$
  • 负浮点数:^-([1-9]\d\*\.\d\*|0\.\d\*[1-9]\d\*)$^(-(([0-9]+\.[0-9]\*[1-9][0-9]\*)|([0-9]\*[1-9][0-9]\*\.[0-9]+)|([0-9]\*[1-9][0-9]\*)))$
  • 浮点数:^(-?\d+)(\.\d+)?$^-?([1-9]\d\*\.\d\*|0\.\d\*[1-9]\d\*|0?\.0+|0)$

1.10 校验字符的表达式

  • 汉字:^[\u4e00-\u9fa5]{0,}$
  • 英文和数字:^[A-Za-z0-9]+$^[A-Za-z0-9]{4,40}$
  • 长度为3-20的所有字符:^.{3,20}$
  • 由26个英文字母组成的字符串:^[A-Za-z]+$
  • 由26个大写英文字母组成的字符串:^[A-Z]+$
  • 由26个小写英文字母组成的字符串:^[a-z]+$
  • 由数字和26个英文字母组成的字符串:^[A-Za-z0-9]+$
  • 由数字、26个英文字母或者下划线组成的字符串:^\w+$^\w{3,20}$
  • 中文、英文、数字包括下划线:^[\u4E00-\u9FA5A-Za-z0-9_]+$
  • 中文、英文、数字但不包括下划线等符号:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
  • 可以输入含有^%&’,;=?KaTeX parse error: Can't use function '\"' in math mode at position 1: \̲"̲等字符:`[^%&',;=?\x22]+`
  • 禁止输入含有~的字符:[^~\x22]+

1.11 特殊需求表达式

  • Email地址:^\w+([-+.]\w+)\*@\w+([-.]\w+)\*\.\w+([-.]\w+)\*$
  • 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?
  • InternetURL:[a-zA-z]+://[^\s]\*^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]\*)?$
  • 手机号码:^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
  • 电话号码(“XXX-XXXXXXX”、“XXXX-XXXXXXXX”、“XXX-XXXXXXX”、“XXX-XXXXXXXX”、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
  • 国内电话号码(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
  • 电话号码正则表达式(支持手机号码,3-4位区号,7-8位直播号码,1-4位分机号): ((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)
  • 身份证号(15位、18位数字),最后一位是校验位,可能为数字或字符X:(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)
  • 帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
  • 密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线):^[a-zA-Z]\w{5,17}$
  • 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在 8-10 之间):^(?=.\*\d)(?=.\*[a-z])(?=.\*[A-Z])[a-zA-Z0-9]{8,10}$
  • 强密码(必须包含大小写字母和数字的组合,可以使用特殊字符,长度在8-10之间):^(?=.\*\d)(?=.\*[a-z])(?=.\*[A-Z]).{8,10}$
  • 日期格式:^\d{4}-\d{1,2}-\d{1,2}
  • 一年的12个月(01~09和1~12):^(0?[1-9]|1[0-2])$
  • 一个月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
  • 钱的输入格式:
    1. 有四种钱的表示形式我们可以接受:“10000.00” 和 “10,000.00”, 和没有 “分” 的 “10000” 和 “10,000”:^[1-9][0-9]\*$
    2. 这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式:^(0|[1-9][0-9]\*)$
    3. 一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号:^(0|-?[1-9][0-9]\*)$
    4. 这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧。下面我们要加的是说明可能的小数部分:^[0-9]+(.[0-9]+)?$
    5. 必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 “10” 和 “10.2” 是通过的:^[0-9]+(.[0-9]{2})?$
    6. 这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样:^[0-9]+(.[0-9]{1,2})?$
    7. 这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样:^[0-9]{1,3}(,[0-9]{3})\*(.[0-9]{1,2})?$
    8. 1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须:^([0-9]+|[0-9]{1,3}(,[0-9]{3})\*)(.[0-9]{1,2})?$
    9. 备注:这就是最终结果了,别忘了"+“可以用”*"替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里
  • xml文件:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
  • 中文字符的正则表达式:[\u4e00-\u9fa5]
  • 双字节字符:[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
  • 空白行的正则表达式:\n\s\*\r (可以用来删除空白行)
  • HTML标记的正则表达式:<(\S\*?)[^>]\*>.\*?|<.\*? /> ( 首尾空白字符的正则表达式:^\s\*|\s\*$(^\s\*)|(\s\*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
  • 腾讯QQ号:[1-9][0-9]{4,} (腾讯QQ号从10000开始)
  • 中国邮政编码:[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
  • IPv4地址:((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}

2. 正则表达式基本用法

2.1 基本匹配

正则表达式只是我们用于在文本中检索字母和数字的模式。例如正则表达式 cat,表示: 字母 c 后面跟着一个字母 a,再后面跟着一个字母 t

"cat" => The cat sat on the mat

正则表达式 123 会匹配字符串 “123”。通过将正则表达式中的每个字符逐个与要匹配的字符串中的每个字符进行比较,来完成正则匹配。 正则表达式通常区分大小写,因此正则表达式 Cat 与字符串 “cat” 不匹配。

"Cat" => The cat sat on the Cat

2.2 元字符

元字符是正则表达式的基本组成元素。元字符在这里跟它通常表达的意思不一样,而是以某种特殊的含义去解释。有些元字符写在方括号内的时候有特殊含义。 元字符如下:

元字符描述
.匹配除换行符以外的任意字符。
[ ]字符类,匹配方括号中包含的任意字符。
[^ ]否定字符类。匹配方括号中不包含的任意字符
*匹配前面的子表达式零次或多次
+匹配前面的子表达式一次或多次
?匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。
{n,m}花括号,匹配前面字符至少 n 次,但是不超过 m 次。
(xyz)字符组,按照确切的顺序匹配字符xyz。
|分支结构,匹配符号之前的字符或后面的字符。
\转义符,它可以还原元字符原来的含义,允许你匹配保留字符 [ ] ( ) { } . * + ? ^ $ \ |
^匹配行的开始
$匹配行的结束

2.2.1 英文句号

英文句号 . 是元字符的最简单的例子。元字符 . 可以匹配任意单个字符。它不会匹配换行符和新行的字符。例如正则表达式 .ar,表示: 任意字符后面跟着一个字母 a, 再后面跟着一个字母 r

".ar" => The car parked in the garage.

2.2.2 字符集

字符集也称为字符类。方括号被用于指定字符集。使用字符集内的连字符来指定字符范围。方括号内的字符范围的顺序并不重要。 例如正则表达式 [Tt]he,表示: 大写 T 或小写 t ,后跟字母 h,再后跟字母 e

"[Tt]he" => The car parked in the garage.

然而,字符集中的英文句号表示它字面的含义。正则表达式 ar[.],表示小写字母 a,后面跟着一个字母 r,再后面跟着一个英文句号 . 字符。

"ar[.]" => A garage is a good place to park a car.
2.2.2.1 否定字符集

一般来说插入字符 ^ 表示一个字符串的开始,但是当它在方括号内出现时,它会取消字符集。例如正则表达式 [^c]ar,表示: 除了字母 c 以外的任意字符,后面跟着字符 a, 再后面跟着一个字母 r

"[^c]ar" => The car parked in the garage.

2.2.3 重复

以下元字符 +*? 用于指定子模式可以出现多少次。这些元字符在不同情况下的作用不同。

2.2.3.1 星号

该符号 * 表示匹配上一个匹配规则的零次或多次。正则表达式 a* 表示小写字母 a 可以重复零次或者多次。但是它如果出现在字符集或者字符类之后,它表示整个字符集的重复。 例如正则表达式 [a-z]*,表示: 一行中可以包含任意数量的小写字母。

"[a-z]*" => The car parked in the garage #21.

* 符号可以与元符号 . 用在一起,用来匹配任意字符串 .*。该 * 符号可以与空格符 \s 一起使用,用来匹配一串空格字符。 例如正则表达式 \s*cat\s*,表示: 零个或多个空格,后面跟小写字母 c,再后面跟小写字母 a,再再后面跟小写字母 t,后面再跟零个或多个空格。

"\s*cat\s*" => The fat cat sat on the cat.
2.2.3.2 加号

该符号 + 匹配上一个字符的一次或多次。例如正则表达式 c.+t,表示: 一个小写字母 c,后跟任意数量的字符,后跟小写字母 t

"c.+t" => The fat cat sat on the mat.
2.2.3.3 问号

在正则表达式中,元字符 ? 用来表示前一个字符是可选的。该符号匹配前一个字符的零次或一次。 例如正则表达式 [T]?he,表示: 可选的大写字母 T,后面跟小写字母 h,后跟小写字母 e

"[T]he" => The car is parked in the garage.
"[T]?he" => The car is parked in the garage.

2.2.4 花括号

在正则表达式中花括号(也被称为量词 ?)用于指定字符或一组字符可以重复的次数。例如正则表达式 [0-9]{2,3},表示: 匹配至少2位数字但不超过3位(0到9范围内的字符)。

"[0-9]{2,3}" => The number was 9.9997 but we rounded it off to 10.0.

我们可以省略第二个数字。例如正则表达式 [0-9]{2,},表示: 匹配2个或更多个数字。如果我们也删除逗号,则正则表达式 [0-9]{2},表示: 匹配正好为2位数的数字。

"[0-9]{2,}" => The number was 9.9997 but we rounded it off to 10.0.
"[0-9]{2}" => The number was 9.9997 but we rounded it off to 10.0.

2.2.5 字符组

字符组是一组写在圆括号内的子模式 (...)。正如我们在正则表达式中讨论的那样,如果我们把一个量词放在一个字符之后,它会重复前一个字符。 但是,如果我们把量词放在一个字符组之后,它会重复整个字符组。 例如正则表达式 (ab)* 表示匹配零个或多个的字符串 “ab”。我们还可以在字符组中使用元字符 |。例如正则表达式 (c|g|p)ar,表示: 小写字母 cgp 后面跟字母 a,后跟字母 r

"(c|g|p)ar" => The car is parked in the garage.

2.2.6 分支结构

在正则表达式中垂直条 | 用来定义分支结构,分支结构就像多个表达式之间的条件。现在你可能认为这个字符集和分支机构的工作方式一样。 但是字符集和分支结构巨大的区别是字符集只在字符级别上有作用,然而分支结构在表达式级别上依然可以使用。 例如正则表达式 (T|t)he|car,表示: 大写字母 T 或小写字母 t,后面跟小写字母 h,后跟小写字母 e 或小写字母 c,后跟小写字母 a,后跟小写字母 r

"(T|t)he|car" => The car is parked in the garage.

2.2.7 转义特殊字符

正则表达式中使用反斜杠 \ 来转义下一个字符。这将允许你使用保留字符来作为匹配字符 { } [ ] / \ + * . $ ^ | ?。在特殊字符前面加 \,就可以使用它来做匹配字符。 例如正则表达式 . 是用来匹配除了换行符以外的任意字符。现在要在输入字符串中匹配 . 字符,正则表达式 (f|c|m)at\.?,表示: 小写字母 fc 或者 m 后跟小写字母 a,后跟小写字母 t,后跟可选的 . 字符。

"(f|c|m)at\.?" => The fat cat sat on the mat.

2.2.8 定位符

在正则表达式中,为了检查匹配符号是否是起始符号或结尾符号,我们使用定位符。 定位符有两种类型: 第一种类型是 ^ 检查匹配字符是否是起始字符,第二种类型是 $,它检查匹配字符是否是输入字符串的最后一个字符。

2.2.8.1 插入符号

插入符号 ^ 符号用于检查匹配字符是否是输入字符串的第一个字符。如果我们使用正则表达式 ^a (如果a是起始符号)匹配字符串 abc,它会匹配到 a。 但是如果我们使用正则表达式 ^b,它是匹配不到任何东西的,因为在字符串 abc 中 “b” 不是起始字符。 让我们来看看另一个正则表达式 ^(T|t)he,这表示: 大写字母 T 或小写字母 t 是输入字符串的起始符号,后面跟着小写字母 h,后跟小写字母 e

"(T|t)he" => The car is parked in the garage.
"^(T|t)he" => The car is parked in the garage.
2.2.8.2 美元符号

美元 $ 符号用于检查匹配字符是否是输入字符串的最后一个字符。例如正则表达式 (at\.)$,表示: 小写字母 a,后跟小写字母 t,后跟一个 . 字符,且这个匹配器必须是字符串的结尾。

"(at\.)" => The fat cat. sat. on the mat.
"(at\.)$" => The fat cat sat on the mat.

2.3 简写字符集

正则表达式为常用的字符集和常用的正则表达式提供了简写。简写字符集如下:

简写描述
.匹配除换行符以外的任意字符
\w匹配所有字母和数字的字符: [a-zA-Z0-9_]
\W匹配非字母和数字的字符: [^\w]
\d匹配数字: [0-9]
\D匹配非数字: [^\d]
\s匹配空格符: [\t\n\f\r\p{Z}]
\S匹配非空格符: [^\s]

2.4 断言

后行断言和先行断言有时候被称为断言,它们是特殊类型的 非捕获组 (用于匹配模式,但不包括在匹配列表中)。当我们在一种特定模式之前或者之后有这种模式时,会优先使用断言。 例如我们想获取输入字符串 $4.44 and $10.88$ 字符之前的所有数字。我们可以使用这个正则表达式 (?<=\$)[0-9\.]*,表示: 获取 $ 字符之前的所有的数字包含 . 字符。 以下是正则表达式中使用的断言:

符号描述
?=正向先行断言
?!负向先行断言
?<=正向后行断言
?<!负向后行断言

2.4.1 正向先行断言

正向先行断言认为第一部分的表达式必须是先行断言表达式。返回的匹配结果仅包含与第一部分表达式匹配的文本。 要在一个括号内定义一个正向先行断言,在括号中问号和等号是这样使用的 (?=...)。先行断言表达式写在括号中的等号后面。 例如正则表达式 (T|t)he(?=\sfat),表示: 匹配大写字母 T 或小写字母 t,后面跟字母 h,后跟字母 e。 在括号中,我们定义了正向先行断言,它会引导正则表达式引擎匹配 Thethe 后面跟着 fat

"(T|t)he(?=\sfat)" => The fat cat sat on the mat.

2.4.2 负向先行断言

当我们需要从输入字符串中获取不匹配表达式的内容时,使用负向先行断言。负向先行断言的定义跟我们定义的正向先行断言一样, 唯一的区别是不是等号 =,我们使用否定符号 !,例如 (?!...)。 我们来看看下面的正则表达式 (T|t)he(?!\sfat),表示: 从输入字符串中获取全部 The 或者 the 且不匹配 fat 前面加上一个空格字符。

"(T|t)he(?!\sfat)" => The fat cat sat on the mat.

2.4.3 正向后行断言

正向后行断言是用于获取在特定模式之前的所有匹配内容。正向后行断言表示为 (?<=...)。例如正则表达式 (?<=(T|t)he\s)(fat|mat),表示: 从输入字符串中获取在单词 Thethe 之后的所有 fatmat 单词。

"(?<=(T|t)he\s)(fat|mat)" => The fat cat sat on the mat.

2.4.4 负向后行断言

负向后行断言是用于获取不在特定模式之前的所有匹配的内容。负向后行断言表示为 (?<!...)。例如正则表达式 (?<!(T|t)he\s)(cat),表示: 在输入字符中获取所有不在 Thethe 之后的所有单词 cat

"(?<!(T|t)he\s)(cat)" => The cat sat on cat.

2.5 标记

标记也称为修饰符,因为它会修改正则表达式的输出。这些标志可以以任意顺序或组合使用,并且是正则表达式的一部分。

标记描述
i不区分大小写: 将匹配设置为不区分大小写。
g全局搜索: 搜索整个输入字符串中的所有匹配。
m多行匹配: 会匹配输入字符串每一行。

2.5.1 不区分大小写

i 修饰符用于执行不区分大小写匹配。例如正则表达式 /The/gi,表示: 大写字母 T,后跟小写字母 h,后跟字母 e。 但是在正则匹配结束时 i 标记会告诉正则表达式引擎忽略这种情况。正如你所看到的,我们还使用了 g 标记,因为我们要在整个输入字符串中搜索匹配。

"The" => The fat cat sat on the mat.
"/The/gi" => The fat cat sat on the mat.

2.5.2 全局搜索

g 修饰符用于执行全局匹配 (会查找所有匹配,不会在查找到第一个匹配时就停止)。 例如正则表达式 /.(at)/g,表示: 除换行符之外的任意字符,后跟小写字母 a,后跟小写字母 t。 因为我们在正则表达式的末尾使用了 g 标记,它会从整个输入字符串中找到每个匹配项。

".(at)" => The fat cat sat on the mat.
"/.(at)/g" => The fat cat sat on the mat.

2.5.3 多行匹配

m 修饰符被用来执行多行的匹配。正如我们前面讨论过的 (^, $),使用定位符来检查匹配字符是输入字符串开始或者结束。但是我们希望每一行都使用定位符,所以我们就使用 m 修饰符。 例如正则表达式 /at(.)?$/gm,表示: 小写字母 a,后跟小写字母 t,匹配除了换行符以外任意字符零次或一次。而且因为 m 标记,现在正则表达式引擎匹配字符串中每一行的末尾。

"/.at(.)?$/" => The fat
                cat sat
                on the mat.
"/.at(.)?$/gm" => The fat
                  cat sat
                  on the mat.

2.6 常用正则表达式

  • 正整数: ^\d+$
  • 负整数: ^-\d+$
  • 电话号码: ^+?[\d\s]{3,}$
  • 电话代码: ^+?[\d\s]+(?[\d\s]{10,}$
  • 整数: ^-?\d+$
  • 用户名: ^[\w\d_.]{4,16}$
  • 字母数字字符: ^[a-zA-Z0-9]*$
  • 带空格的字母数字字符: ^[a-zA-Z0-9 ]*$
  • 密码: ^(?=^.{6,}$)((?=.*[A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z]))^.*$
  • 电子邮件: ^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})*$
  • IPv4 地址: ^((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))*$
  • 小写字母: ^([a-z])*$
  • 大写字母: ^([A-Z])*$
  • 网址: ^(((http|https|ftp):\/\/)?([[a-zA-Z0-9]\-\.])+(\.)([[a-zA-Z0-9]]){2,4}([[a-zA-Z0-9]\/+=%&_\.~?\-]*))*$
  • VISA 信用卡号码: ^(4[0-9]{12}(?:[0-9]{3})?)*$
  • 日期 (MM/DD/YYYY): ^(0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])[- /.](19|20)?[0-9]{2}$
  • 日期 (YYYY/MM/DD): ^(19|20)?[0-9]{2}[- /.](0?[1-9]|1[012])[- /.](0?[1-9]|[12][0-9]|3[01])$
  • 万事达信用卡号码: ^(5[1-5][0-9]{14})*$

3. 正则表达式进阶

3.1 POSIX 字符类

POSIX 字符类
(Portable Operating System Interface)可移植操作系统接口

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] 
String regex = "\\p{Punct}"; // 英文标点符号
System.out.println(",".matches(regex) == true);
System.out.println("$".matches(regex) == true);
System.out.println(",".matches(regex) == false);

3.2 java.lang.Character

\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 规范是否对称。
String regex = "\\p{javaLowerCase}";
System.out.println("a".matches(regex) == true);
System.out.println("A".matches(regex) == false);
regex = "\\p{javaMirrored}";
System.out.println("A".matches(regex) == false);
System.out.println("(".matches(regex));
System.out.println(")".matches(regex));
System.out.println("[".matches(regex));
System.out.println("\"".matches(regex) == false);

3.3 Unicode 块和类别的类

\p{InGreek} Greek 块(简单块)中的字符 
\p{Lu} 大写字母(简单类别) 
\p{Sc} 货币符号 
\P{InGreek} 所有字符,Greek 块中的除外(否定) 
[\p{L}&&[^\p{Lu}]]  所有字母,大写字母除外(减去)
String regex = "\\$";
System.out.println("$".matches(regex) == true);
regex = "\\p{Sc}"; // 货币符号
System.out.println("$".matches(regex) == true);
System.out.println("¥".matches(regex) == true);
System.out.println("€".matches(regex) == true);
System.out.println("a".matches(regex) == false);
regex = "\\p{InGreek}"; // 匹配希腊字母
System.out.println("a".matches(regex) == false);
System.out.println("α".matches(regex) == true);
System.out.println("β".matches(regex) == true);
System.out.println("γ".matches(regex) == true);
System.out.println("Δ".matches(regex) == true);
System.out.println("θ".matches(regex) == true);
System.out.println("ρ".matches(regex) == true);

3.4 边界匹配器

^ 行的开头 
$ 行的结尾 
\b 单词边界 
\B 非单词边界 
\A 输入的开头 
\G 上一个匹配的结尾 
\Z 输入的结尾,仅用于最后的结束符(如果有的话) 
\z 输入的结尾 
// 匹配模式
String regex = "(<b>(如梦令)</b>)";
System.out.println("<b>如梦令</b>".replaceAll(regex, "$1").equals("<b>如梦令</b>"));
System.out.println("<b>如梦令</b>".replaceAll(regex, "$2").equals("如梦令"));

// 最大匹配 (Greedy,贪婪模式)
regex = "(<b>(\\S+)</b>)"; 
System.out.println("<b>如梦令</b>".replaceAll(regex, "$2").equals("如梦令"));
System.out.println("<b>如梦令</b><b>满江红</b>".replaceAll(regex, "$2").equals("如梦令</b><b>满江红"));
// 最小匹配 (Reluctant,勉强模式)
regex = "(<b>(\\S+?)</b>)";
System.out.println("<b>如梦令</b><b>满江红</b>".replaceAll(regex, "$2").equals("如梦令满江红"));
// 独占模式 (Possessive )
regex = "(<b>(\\S++)</b>)";
System.out.println("<b>如梦令</b><b>满江红</b>".replaceAll(regex, "$2").equals("<b>如梦令</b><b>满江红</b>"));

regex = "\\S+b";
System.out.println("aaaab".matches(regex) == true);

regex = "\\S++b"; // 独占模式不会回溯
System.out.println("aaaab".matches(regex) == false);

3.5 Back 引用

\n 任何匹配的 nth 捕获组 
//		反向引用
String regex = "(\\w{3})\\1";
System.out.println("123456".matches(regex) == false);
System.out.println("123123".matches(regex) == true);
System.out.println("456456".matches(regex) == true);
System.out.println("abcabc".matches(regex) == true);

regex = "(\\w{2})\\1+";
System.out.println("12121212".matches(regex) == true);
System.out.println("abab".matches(regex) == true);

3.6 模式匹配

	Pattern pattern = Pattern.compile("<b>(\\S+?)</b>");
//	String input = "<b>如梦令</b><b>满江红</b>";
	String input = "aaa<b>如梦令</b><b>满江红</b>vvv";
	Matcher matcher = pattern.matcher(input);
//	System.out.println(matcher.find() == true);
//	System.out.println(matcher.start() == 3);
//	System.out.println(matcher.end() == 13);
//	System.out.println(matcher.group().equals("<b>如梦令</b>"));
//	System.out.println(matcher.group(0).equals("<b>如梦令</b>"));
//	System.out.println(matcher.group(1).equals("如梦令"));
//	System.out.println(matcher.group(2));
	pattern = Pattern.compile("<b>(\\S+?)</b>",Pattern.MULTILINE);
	input = "aaa<b>如梦令</b><b>满江红</b>vvv";
	matcher = pattern.matcher(input);
	while(matcher.find()) {
		System.out.println(matcher.group(1));
		// 如梦令
		// 满江红
	}

3.7 split 结果阈值 limit

String input = "论语,孟子,大学,中庸";
String[] array = input.split("[,,]");
System.out.println(Arrays.toString(array)); // [论语, 孟子, 大学, 中庸]
System.out.println(array.length == 4);

input = "论语,孟子,大学,中庸,";
array = input.split("[,,]",-1);
System.out.println(array.length == 5);

input = "论语,孟子,大学,中庸,,";
array = input.split("[,,]",-1);
System.out.println(array.length == 6);

array = input.split("[,,]",2);
System.out.println(array.length == 2);

本文标签: 正则表达式