外观
17 正则表达式
2565 字约 9 分钟
2024-09-01
Regular expression 简称:RegExp
正则表达式:对字符串执行模式匹配的技术。一个正则表达式,就是用某种模式去匹配字符串的一个公式。除 Java 外,还有许多语言支持正则表达式。
String content = "HeruinKCoin"; //对象文本
String regular = "[A-Z]"; //[1] 创建规则
Pattern pattern = Pattern.compile(regular); //[2] 创建模式对象
Matcher matcher = pattern.matcher(content); //[3] 创建匹配器
while (matcher.find()){ //[4] find() 是否找到下一个
System.out.println(matcher.group(0)); //[5] group(0) 输出找到的当前对象
}
在这里matcher.group(0),返回的是0到1这个两个索引表示的字符串。
Matcher
底层维护了一个group[]
数组。如果 [4] 在文本里匹配到对象,会在group[0]
记载该起始位置 n1,在group[1]
记录该结束位置的下一位 n2。即 [n1,n2) 为匹配的字符串,n2 位置是下次匹配的起始位置。当 [1] 创建的规则包含分组(如
String regular = "(\\d\\d)(\\d\\d)";
),则第一组的起止位置记录在group[2]
、group[3]
,第二组在group[4]
、group[5]
。以此类推。这时,[5] 的group(0)
代表输出全部,group[1]
代表输出第一组,group[2]表示输出第二组,以此类推。
25.1 语法
元字符
- 限定符
- 选择匹配符
- 分组组合和反向引用符
- 特殊字符
- 字符匹配符
- 定位符
25.1.1 转义符号 \
使用正则表达式去检索某些特殊字符时,需要加上转义符号(如:(
需要写成 \(
)
在 Java 的正则表达式中,\\
代表一个 \
。
需要用到转义符号的字符有:.
、+
、(
、)
、$
、/
、\
、?
、[
、]
、^
、{
、}
25.1.2 字符匹配符
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
[ ] | 可接收的字符列表 | [abcd] | abcd 中的任一字符 |
[^] | 不接收的字符列表 | [^abcd] | 非 abcd 的任意字符 |
- | 连字符 | [a-z] | a - z 中的任意字符 |
. | 除了斜杆n、斜杆r、斜杆r和斜杆n,都匹配; | a..b | a 开头,b结尾,中间含 2 字符 |
\d | 匹配单个数字字符,相当于[0-9] | \d{3}(\d)? | 包含 3 个数字,或4个数字的字符串,(\d)? 要注意,这个很有用,表示前面的条件加这个条件,但是这个条件只是可能存在第四个数字。 |
\D | 匹配单个非数字字符,相当于[^0-9] | \D(\d)* | 单个非数字字符开头,后接任意个数字字符 |
\w | 匹配单个数字、下划线、大小写字母字符,相当于[0-9a-zA-Z_] | \w{2}\d{3} | 2 个数字字母字符开头(任意组合,全为数字,全为字符或者都有),后接 3 个数字字符 |
\W | 匹配单个非数字、非大小写字母字符,相当于[^0-9a-zA-Z_] | \W+\d{2} | 以至少 1 个非数字字母字符开头,后接 2 个数字字符 |
\s | 匹配空白字符(空格、制表位等) | ||
\S | 匹配非空白字符 |
关于
.
:特别地,出现[.]
的场合,那个小圆点依然表示小圆点。[?]
同理,表示问号正则表达式默认区分大小写。要不区分大小写,就加上
(?i)
(?i)abc
:即 abc 都不区分大小写a(?i)bc
:即仅 bc 不区分大小写a((?i)b)c
:即仅 b 不区分大小写创建模式对象时,若如此做:
Pattern pattern = Pattern.compile(regular, Pattern.CASE_INSENSITIVE);
这个场合,也能不区分大小写。
25.1.3 选择匹配符 |
……我的感想是,和 Java 的逻辑或 |
一样!
25.1.4 限定符
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
* | 指定字符重复任意次(可以为 0 次) | (abc)* | 仅包含任意个 abc 字符串的字符串 |
+ | 指定字符重复至少一次 | m+(abc)* | 以任意个 m 开头,后面可以有 abc 字符串的字符串 |
? | 指定字符重复最多一次(可以为 0 次) | m+abc? | 以任意个 m 开头,后面可以有最多一个 abc 字符串的字符串 |
{n} | n 个匹配 | [abc]{3} | 长度为 3 的 abc 中的任意字符的组合 |
{n,} | 至少 n 个匹配 | [abc]{3,} | 长度不小于 3 的 abc 中的任意字符的组合 |
{n,m} | n 到 m 个匹配 | [abc]{3,5} | 长度介于 3 到 5 之间的 abc 中的任意字符的组合 |
Java 的匹配模式默认是贪婪匹配。即:
aaaaa
匹配a{3,5}
的场合,会匹配到aaaaa
希望实现非贪婪匹配,可以添加额外的
?
。如:*?
、+?
、??
代表各自规则的非贪婪匹配注意,除非语句要求限定长度,否则语法都会尽量长的返回字符串
25.1.5 定位符
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
^ | 指定起始字符 | ^[0-9]+[a-z]* | 至少一个数字开头,后接任意小写字母字符串,如果是123abc12可以匹配到‘123abc’,如果是ab123abc则会匹配不到,因为开头必须为数字。结尾没有做要求。 |
$ | 指定结束字符 | ^[0-9][a]$ | 一个数字开头,一个 a 结尾,结尾也必须是a,否则匹配不到。 |
\b | 匹配目标字符串的边界 | K\.C\b | 匹配边界的 K.C |
\B | 匹配目标字符串的非边界 | K\.C\B | 匹配非边界的 K.C |
- 边界即字符串的末尾,或字符串中空格间隔的子串的末尾。
- 用的最多的是
^
和$
,他们经常搭配使用。例如要限定一个邮箱格式,则写为^ [a-zA-Z0-9_-]+@ [a-zA-Z0-9_-]+ (\\. [a-zA-Z0-9_-]+)+$
25.1.6 分组
符号 | 含义 |
---|---|
(pattern) | 非命名捕获。捕获匹配的字符串。 |
(?<name>pattern) 、(?'name'pattern) | 命名捕获,给组取名,可以在group方法中传入这个命名的字符串来得到相应分组字符串。用于 name 的字符串不能包含标点符号,也不能以数字开头 |
- 编号为 0 的第一个捕获是由整个正则表达式匹配的文本。其他捕获结果根据左括号的顺序从 1 开始自动编号。
25.1.7 非捕获分组
写法简洁,但是这样就不能gourp(1)和gourp(2)访问了。
符号 | 含义 | 示例 | 解释 |
---|---|---|---|
(?:pattern) | 匹配 pattern 但不捕获该匹配的子表达式 | `industr(?:y | ies)` |
(?=pattern) | 匹配处于 pattern 前的搜索字符串。非捕获分组。 | `Windows(?=7|10) | 只得到windows 7或windows 10前的Windows |
(?!pattern) | 匹配不处于 pattern 前的搜索字符串。非捕获分组。 | `Windows(?!7|10) | 得到除了windows7或windows10之前的windows |
25.2 常用类
Pattern
类:Pattern
对象是一个正则表达式对象,该类没有公共构造方法。用
Pattern.compile(reg)
获取一个Pattern
对象。Matcher
类:Matcher
对象是输入字符串进行解释和匹配的引擎,也没有公共构造方法。用
Pattern
对象的matcher(content)
方法获得一个Matcher
对象。PatternSyntaxExcption
类:PatternSyntaxExcption
是一个非强制异常类,表示一个正则表达式中的语法错误。
25.2.1 Pattern
类常用方法
Pattern.matches(reg, content)
:==整体匹配,==输入的字符串是否符合表达式。返回布尔值。matcher.matches()
:整体匹配,字符串是否符合表达式。返回布尔值。前面的方法实际上就是这个方法。Pattern.compile(reg)
:返回一个指定表达式的Pattern
对象pattern.matcher(content)
:返回一个字串的Matcher
对象matcher.pattern()
:返回该Matcher
对象的表达式pattern.pattern()
:返回该Pattern
对象的表达式matcher.find()
:尝试查找下一个匹配的序列,返回布尔值matcher.find(int)
:重置该匹配器,从指定索引位置开始重新查找matcher.start()
:返回本次匹配的字符起始位置的索引matcher.end()
:返回本次匹配的字符结束位置 + 1 的索引这个场合,
content.substring(matcher.start(), matcher.end())
就是匹配的字符串matcher.start(int)
:返回本次匹配的字符的该组内容的起始位置的索引matcher.end(int)
:返回本次匹配的字符的该组内容的结束位置 + 1 的索引matcher.replaceAll(str)
:替换匹配到的全部内容,返回替换的字符,原来的字符不变化,可以通过用原字符串引用接受这个返回值来变相”改变“内容(字符串是不可以修改的,只是原来的引用指向了新的字符串对象)matcher.replaceFirst(str)
:替换第一次匹配到的内容这些场合,返回的字符串才是替换后的字符串。原字符串不变。
25.3 分组、捕获、反向引用
分组(子表达式)
捕获:把正则表达式中,子表达式(分组)的内容保存到内存中以数字编号或显式命名的组里,方便后面引用。以分组的左括号为标志,第一组组号为 1,第二组为 2,以 0 代表整个正则表达式。
反向引用:分组的内容被捕获后,可以在这个括号后使用。这种引用既可以是在正则表达式内部,也可以在外部。内部反向引用
\分组号
、外部反向引用$分组号
String regular = "(\\w)\\1+"; //即,重复的字母或数字 Matcher matcher = Pattern.compile(regular).mathcer(content); content = matcher.replaceAll("$1"); //这样,就完成了去重
//前五位数字-后面9为每三位数字相同。 String content ="12345-111222333"; String regular = "[0-9]{5}-(\\d)\\1{2}(\\d)\\2{2}(\\d)\\3{2}"; Pattern pattern = Pattern.compile(regular); Matcher matcher = pattern.matcher(content); while (matcher.find()){ System.out.println(matcher.group(0)); }
25.4 在 String
中使用正则表达式
str.matches(reg)
:整体匹配str.replaceAll(reg, reg)
:替换匹配到的全部内容str.split(reg)
:分割内容
应用实例
1.验证汉字
String content = "你好";
String content2 = "你hello好";
String regular = "^[\u0391-\uffe5]+$"; //这个范围是汉字的范围
Pattern pattern = Pattern.compile(regular);
Matcher matcher = pattern.matcher(content);
if(matcher.find()){
System.out.println("满足格式");
}else {
System.out.println("不满足格式");
}
2.验证url
String regular = "^((https|http):\\/\\/)([\\w-]+\\.)+([\\w-])+(\\/[\\w.?%/&=-]*)?$";
3.结巴程序去重
String content = "我....我要....学学学学....编程java!";
String regular = "[.]+";
Pattern pattern = Pattern.compile(regular);
Matcher matcher = pattern.matcher(content);
content = matcher.replaceAll("");
System.out.println(content);
pattern = Pattern.compile("(.)\\1+");
matcher = pattern.matcher(content);
while (matcher.find()) {
System.out.println(matcher.group(0));
}
String target = matcher.replaceAll("$1");
System.out.println(target);
content = Pattern.compile("(.)\\1+").matcher(content).replaceAll("$1");
System.out.println(content);
实际上可以用String来做,这样就可以去重了。
content.replaceAll("(.)\\1+","$1");