好好聊一聊Java中的正则表达式

写在前面

如果觉得有所收获,记得点个关注和点个赞哦,非常感谢支持
正则表达式,又称规则表达式。正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。通常都是用来验证输入的字符串数据是否复合正则表达式定义的规范,常被用来做验证操作。

正则表达式(regular expression),根据英文也可简写为regex、regexp 或 RE。正则表达式只用于字符串,用来检查字符串是否符合某种规定(例如只有字母、又例如没有数字等),或者是取出符合规定的字符串子串(用于后续替换等)。

例如这里举个例子

#表达式
/Hello.*/g

#匹配字符串
Hello.
Hello. world
...

这里放一个正则表达式在线测试网站,挺好用的

正则表达式规则

正则表达式的概念不难理解,若不清晰用过一两次便懂。使用正则表达式若有困难,都是因为语法繁多,需要记忆的内容量大。下面整理了三张正则表达式的常用语法字符表,主要来源为菜鸟教程的正则表达式教程。分三张表,分别是:

  • 非打印字符:转义后的字符,代表某一类字符
  • 特殊字符:含特殊含义的字符
  • 限定字符:字符次数限制

非打印字符

字符 描述 正确示例 错误示例
\w 数字、字母、下划线 1/a/A/_ ./&/の/【/︹(空格)/ㄱ(换行)
\W 除数字、字母、下划线以外 ./&/の/【/︹(空格)/ㄱ(换行) 1/a/A/_
\s 空白字符 ︹(空格)/→(制表符)/ㄱ(换行)/☇(换页) 1/a/A/_/./&/♂
\S 除空白字符以外 1/a/A/_/./&/♂/张 ︹(空格)/→
\d 数字(单个) 1/2/3/4/5/6/7/8/9/0 a/?/&/1234(整体)
\D 除数字以外 a/B/,/%/张/︹(空格) 1/2/3/1234(整体)
\n 换行符 ㄱ(换行) 1/a/A/_/?
\t 制表符 →|(制表符) 1/a/A/_/?

特殊字符

字符 描述 正确示例 错误示例
. 除换行以外任意字符 1/a/A/%/_ ㄱ(换行)/123(整体)
^ 从字符串开始的位置处匹配 ^12 -> 12/123/12a ^12 -×-> a12/abc12/
$ 从字符串结束的位置处匹配(倒着) 12$ -> 12/abc12/012 12$ -×-> 123/12aaa
| [1|2|3] -> 1/2/3 [1|2|3] -×-> a/4/&
[] 方框运算符,字符集合/范围/反向
()
{}

限定字符

字符 描述 正确示例 错误示例
* 匹配零个、一个或多个字符 12*3 -> 13/123/1223 12*3 -×-> 1
+ 匹配一个或多个字符 12+3 -> 123/12223 12+3 -×-> 13
? 匹配零个或一个字符 12?3 -> 13/123 12?3 -×-> 12223
{n} 字符限定n次 2{4} -> 2222 2{4} -×-> 222/22222(整体)
{n,} 字符限定最少n次 2{2,} -> 22/222/22222 2{2,} -×-> 2
{n,m} 字符限定n-m次 2{2,3} -> 22/222 2{2,3} -×-> 2/2222(整体)

除以上常用的语法字符之外,还有以下规则,需要单独注意:

  • [ ]:方括号表示字符集合,可配合 ^ 符合表示反向的字符集合,常见用法有:
示例 说明
[12a] 可匹配 ‘1’ 或 ‘2’ 或 ‘a’
[1|2|a|,] 可匹配 ‘1’ 或 ‘2’ 或 ‘a’ 或 ‘,’
[1-9] 可匹配 ‘1’ 或 ‘2’ 或 …… 或 ‘9’
[a-z] 可匹配 ‘a’ 或 ‘b’ 或 …… 或 ‘z’
[^12a] 可匹配除了 ‘1’ 或 ‘2’ 或 ‘a’ 以外的字符
[^1-9] 可匹配除了 ‘1’ 或 ‘2’ 或 …… 或 ‘9’ 以外的字符
  • ():圆括号代表同时匹配多个字符,如匹配 hello 这五个字符,可以使用 (hello) 来匹配。但是除此之外,() 匹配到的字符串还会被缓存起来,缓存起来的子表达式可以在之后使用。例如使用([nN]o)匹配 No, get out!,不光能匹配到 No,还能通过 $1oooooooo 将原文替换成 Nooooooooo, get out!。
示例 说明
(hello) 可以匹配 “hello”,并缓存起来
(?:hello) 可以匹配 “hello”,但不缓存结果(用于匹配多字符但并不需要结果,有时能让正则表达式更简洁)
hello(?=AA) 可以匹配 “helloAA……” 中的 “hello”,但是不能匹配 “helloBB……” 中的 “hello”,且不缓存结果
hello(?!AA) 不能匹配 “helloAA……” 中的 “hello”,但是可以匹配 “helloBB……” 中的 “hello”,且不缓存结果
(?<=AA)hello 可以匹配 “AAhello……” 中的 “hello”,但是不能匹配 “BBhello……” 中的 “hello”,且不缓存结果
(?<!AA)hello 不能匹配 “AAhello……” 中的 “hello”,但是可以匹配 “BBhello……” 中的 “hello”,且不缓存结果

特别说明

贪婪是编程和算法中常见的概念,意思是越多越好。* 和 + 这两个限定符都是贪婪的,如果用 hello* 或者 hello+ 来匹配 hellooooooo,它们的匹配结果都是 hellooooooo,而不是 hell、 hello 或 hellooo 或是其他的。在 * 和 + 后面加上 ? 就可以使匹配结果是非贪婪的(最小匹配)。按照上面的例子,hello*? 匹配的结果是 hell,hello+? 匹配的结果是 hello。

写几个看到过的正则表达式,举几个例子

  • 身份证校验(简易版)
^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$
  • 不能包含数字
^[^\d]+$
  • 浮点数
^(-?\d+)(\.\d+)?$
  • 域名
^[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+$

Java中的正则表达式

学习过正则表达式的基本语法之后,感觉在工作中还是不够用,还要学习 Java 中正则表达式的使用。正则表达式从 JDK 1.4 之后出现,涉及到两个新类:Pattern 和 Matcher。Pattern 类代表一个正则表达式,而 Matcher 类代表一个正则表达式对一个字符串进行校验后的结果,例如:

// 正则表达式,规则:不能包含数字
String regex = "^[^\\d]+$";
Pattern compile = Pattern.compile(regex);

// 匹配abcABC?&
String str = "abcABC?&";
Matcher matcher = compile1.matcher(str);

Pattern 类没有(对外的)构造方法,因此生成一个 pattern 对象只能通过 Pattern 类的静态方法。如果只是想检验字符串是否符合要求(即只需要一个boolean值),那么使用 Pattern 类的静态方法就可以,pattern 对象是为了更多操作。

String regex = "^[^\\d]+$";
String str = "abcABC?&";
boolean pass = Pattern.matches(regex, str);

// ------结果:pass:true

Pattern 类多实现一些,用代码实现正则表达式的功能,而不是完全只用字符串来实现。例如它能指定正则表达式可以同时匹配大小写字母(指定后,正则表达式字符串即使只有 ‘a’,匹配时也可以同时匹配 ‘a’ 和 ‘A’),又或者可以用指定正则表达式分隔字符串,例如将一串带有数字的字符串,以数字为分隔拆分成多个子字符串。Matcher 类多实现一些对匹配结果的处理,最后提一句,Java 中使用正则表达式时,基本上要出现 \ 的地方,都要转义成 \,例如 \d -> \d

Pattern

Pattern 类可以理解成是正则表达式,可以使用 Pattern 对象来对字符串进行正则校验。不能通过 new 来创建 Pattern 对象,只能通过 Pattern 类的静态方法创建。

Pattern pattern = Pattern.compile("\\w"); // 正则表达式:单个数字、字母或下划线

日常使用 Pattern 类有两个用途:

  • 直接校验字符串,返回 true/false
// 校验是否是qq邮箱
String regex = "^\\w*@qq.com$";
boolean matches = Pattern.matches(regex, "[email protected]");

这里有一处暗坑,校验成功要求全部匹配,如果是部分匹配,那么返回值将是 false。

boolean matches1 = Pattern.matches("[0-9]", "123");   // false
boolean matches2 = Pattern.matches("[0-9]*", "123");  // true

  • 生成一个校验过的结果,返回 Matcher 对象
// 正则表达式:是否是数字
String regex = "[0-9]*";
Pattern pattern = Pattern.compile(regex);

// 生成对"123"校验的结果(结果是一个Matcher对象)
Matcher matcher = pattern.matcher("123");

Matcher 对象能做更多的事情,比如部分校验、取出校验结果等。Pattern 类的其他使用,例如指定正则模式 flag、分割字符串的 split() 方法(我好像觉得跟 String 对象的 split() 方法 没有任何区别?)。

Matcher

Matcher 类可以理解成经过校验的字符串的校验结果,可以通过该对象处理校验结果。Matcher 对象由 Pattern 对象的 matcher() 方法获取:

// pattern表示正则表达式,方法内传入待校验的字符串
Matcher matcher = pattern.matcher("wait to check");

日常使用 Matcher 类也是两个用途:

  • 检查字符串中是否含正则校验内容
// 校验是否包含数字
Matcher matcher = Pattern.compile("[0-9]").matcher("12345");
boolean contains = matcher.find();  // true

请注意理解 find() 方法的语义,find 的意思是找到了,并不代表完全匹配,换言之只需要字符串中有正则内容就可以。因此使用 matcher.find() 和 Pattern.matches() 是不等价的。

  • 取出正则校验结果,这里有一个需要先指出的暗坑:在取结果之前,必须先执行 Matcher 对象的 find() 方法,如果不执行,将抛出 IllegalStateException 异常:No match found (没找到匹配结果)。
// 正则表达式:数字(字母)数字(字母)数字(字母)
Matcher matcher = Pattern.compile("\\d+([a-z])\\d+([a-z])\\d+([a-z])").matcher("111a111b111c");
boolean find = matcher.find();      // true
String result = matcher.group();    // 111a111b111c
String result0 = matcher.group(0);  // 111a111b111c
String result1 = matcher.group(1);  // a
String result2 = matcher.group(2);  // b
String result3 = matcher.group(3);  // c

使用 group() 方法可以捕获到正则结果,如果正则表达式中有( )括起来的内容,这部分内容可以取出来。有两种常用的 group() 方法,一种是传入 int 值的 group(int group) 方法,另一种是无参的 group() 方法。

  • 带 int 参数的 group() 方法,参数为 0 时代表整个表达式,为 1 时代表匹配到的第 1 个( )内容,为 2 时代表匹配到的第 2 个( )内容……以此类推,正如上面的代码示例。如果参数是 n,但是实际上并没有 n 个匹配内容,会抛出 IndexOutOfBoundsException 异常。

  • 无参的 group() 方法实际上就是 group(0),代表整个表达式。

此外还可以通过 groupCount() 方法获取匹配到( )的数量。

// ...延续上面的代码
int count = matcher.groupCount();  // 3
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章