常用的正则表达式
正则表达式中的分组(Groups in regexps)
首先,我们来介绍一个重要的概念:分组,这是我们在编写复杂正则表达式时需要用到的。分组的作用与数学表达式中的括号类似:它可以帮助我们设置操作的优先级。
正则表达式的一部分可以用小括号括起来形成一个分组。我们也可以对每个分组应用量词(quantifier):如果你在括号后加上量词,它会应用于整个括号内的内容,而不是单个字符。
例如,下面的代码会查找文本中一对 “ho” 字符串的重复(一次或多次):
Java 示例
Pattern regexWithGroups = Pattern.compile("(ho)+");
String text = "ho hoho hohoho";Matcher resultWithGroups = regexWithGroups.matcher(text);
while (resultWithGroups.find()) {System.out.println(resultWithGroups.group());
}
Kotlin 示例
val regexWithGroups = """(ho)+""".toRegex()
val text = "ho hoho hohoho"val resultWithGroups = regexWithGroups.findAll(text)
for (res in resultWithGroups) println(res.value)
输出结果:
ho
hoho
hohoho
日期(Dates)
让我们从一个简单且常见的任务开始。假设你需要找出以下两种格式的日期:yyyy-mm-dd
和 yyyy/mm/dd
。怎么做?
我们需要匹配如下格式的文本:4 个数字,然后是一个可能的分隔符(/
或 -
),接着是 2 个数字,相同的分隔符,再加 2 个数字。下面是实现这个目标的正则表达式:
\d{4}(-|\/)\d{2}\1\d{2}
解释:
-
\d{4}(-|\/)
:匹配 4 位数字,接着匹配一个分隔符(-
或/
),并把它作为第一个分组。 -
\d{2}\1
:接下来匹配 2 位数字,后跟 第一个分组中提取的相同分隔符(\1
表示第一个分组)。 -
最后是
\d{2}
:再匹配两个数字。
Java 示例
Pattern regex = Pattern.compile("\\d{4}(-|/)\\d{2}\\1\\d{2}");
String text = "Date 1: 2022-06-06" + "Date 2: 2021/01/01; date 3: 2020-02-02";Matcher dates = regex.matcher(text);
while (dates.find()) {System.out.println(dates.group());
}
Kotlin 示例
val regex = Regex("""\d{4}(-|\/)\d{2}\1\d{2}""")
val dates = regex.findAll("Date 1: 2022-06-06" +"Date 2: 2021/01/01; date 3: 2020-02-02")for (date in dates) println(date.value)
输出结果:
2022-06-06
2021/01/01
2020-02-02
注意:这个正则表达式虽然有效,但在现实场景中可能不够健壮,它仅覆盖了少数几种格式。
电话号码(Phone numbers)
之前我们简单介绍过电话号的正则写法。现在我们来写一个稍微复杂一些的正则。
我们假设电话号码有如下格式之一:
-
XXX-XXX-XXXX
-
(XXX)-XXX-XXXX
-
(XXX)XXXXXXX
-
XXXXXXXXXX
以下是相应的正则表达式:
\(?[\d]{3}\)?-?[\d]{3}-?[\d]{4}
解释:
-
\(?[\d]{3}\)?-?
:匹配前 3 个数字,可能有括号包裹,也可能有连字符。 -
[\d]{3}-?
:匹配中间 3 位数字和可选的连字符。 -
[\d]{4}
:匹配最后 4 位数字。
Java 示例
Pattern regex = Pattern.compile("\\(?\\d{3}\\)?-?\\d{3}-?\\d{4}");
String text = "Ann's phone: 123-345-6789 " +"Dave's phone: (111)-234-5678, and next phone is (101)-234-5000";Matcher phones = regex.matcher(text);
while (phones.find()) {System.out.println(phones.group());
}
Kotlin 示例
val regex = Regex("""\(?[\d]{3}\)?-?[\d]{3}-?[\d]{4}""")
val phones = regex.findAll("Ann's phone: 123-345-6789 " +"Dave's phone: (111)-234-5678, and next phone is (101)-234-5000")for (phone in phones) println(phone.value)
输出结果:
123-345-6789
(111)-234-5678
(101)-234-5000
邮箱(Email)
假设你需要找出文本中的所有邮箱地址。标准邮箱格式是 "用户名@子域.主域"
。下面的正则表达式可以匹配大多数常规邮箱:
([a-z0-9_\.-]+)@([a-z0-9_\.-]+)\.([a-z\.]{2,6})
解释:
-
第 1 组:
([a-z0-9_\.-]+)
匹配用户名,可以包含小写字母、数字、下划线、点和连字符。 -
然后是
@
符号。 -
第 2 组:
([a-z0-9_\.-]+)
匹配域名,同样可以包含这些字符。 -
然后是
.
。 -
第 3 组:
([a-z\.]{2,6})
匹配顶级域名(2-6 个小写字母或点)。
Java 示例
String patternStr = "([a-z0-9_\\.-]+)@([a-z0-9_\\.-]+)\\.([a-z\\.]{2,6})";
Pattern regex = Pattern.compile(patternStr);
String text = "We have the following emails: abc@mail.com, joe_blow@address.ing";Matcher matchResult = regex.matcher(text);
while (matchResult.find()) {System.out.println(matchResult.group());
}
Kotlin 示例
val regex = Regex("""([a-z0-9_\.-]+)@([a-z0-9_\.-]+)\.([a-z\.]{2,6})""")
val matchResult = regex.findAll("We have the following emails: " +"abc@mail.com, joe_blow@address.ing")for (matches in matchResult) println(matches.value)
输出结果:
abc@mail.com
joe_blow@address.ing
URL(网址)
手动从文本中提取链接非常繁琐,但使用正则表达式就轻松多了!
URL 示例:https://www.somesite.com/index.html
以下正则可以匹配大多数 URL:
(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w\.-]*)*\/?
解释:
-
第 1 组:
(https?:\/\/)?
匹配http://
或https://
,是可选的。 -
第 2 组 + 第 3 组:
([\da-z\.-]+)\.([a-z\.]{2,6})
匹配主机名和顶级域名。 -
第 4 组:
([\/\w\.-]*)*\/?
匹配路径部分(字母、数字、点、斜杠等)。
Java 示例
String patternStr = "(https?:\\/\\/)?([\\da-z\\.-]+)\\.([a-z\\.]{2,6})([\\/\\w\\.-]*)*\\/?";
Pattern regex = Pattern.compile(patternStr);
String text = "Jet Brains Website: https://www.jetbrains.com/ " +"And here is information about Hyperskill: https://hi.hyperskill.org/how-we-teach";Matcher matchResult = regex.matcher(text);
while (matchResult.find()) {System.out.println(matchResult.group());
}
Kotlin 示例
val regex = Regex("""(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w\.-]*)*\/?""")
val matchResult = regex.findAll("Jet Brains Website: https://www.jetbrains.com/ " +"And here is information about Hyperskill: https://hi.hyperskill.org/how-we-teach")for (matches in matchResult) println(matches.value)
输出结果:
https://www.jetbrains.com/
https://hi.hyperskill.org/how-we-teach