1. 概述
正则表达式(Regular Expressions,简称 RegEx)是一种强大的文本处理工具,广泛应用于字符串匹配、替换、提取等场景。本文将从基础语法讲起,逐步深入到实际应用,帮助你掌握 Kotlin 中正则表达式的使用。
我们将学习如何构建各种复杂度的正则表达式,并通过实际案例练习来理解如何将问题转化为正则表达式解决方案。
2. 正则表达式简介
正则表达式是用于匹配字符串中特定字符序列的公式。它的核心思想是:在一段未知或部分未知的文本中,提取满足某些条件的子串。
正则表达式在自然语言处理(NLP)、文本解析、词法分析、字符串替换、信息检索等领域中非常常见。它几乎被所有主流编程语言支持,包括 Kotlin、Java、Scala、Groovy 等。
3. 正则表达式语法
3.1 单个字符匹配
最简单的正则表达式就是直接匹配一个字符。例如:
val r = "A".toRegex()
val s = "aaAbbBccC"
println(r.findAll(s).map { it.value }.toList()) // 输出 ["A"]
如果我们要匹配多个相同的字符,比如两个连续的 a
:
val r = "aa".toRegex()
val s = "aabaA"
println(r.findAll(s).map { it.value }.toList()) // 输出 ["aa"]
3.2 字符组与方括号
我们可以使用方括号 []
来表示多个可选字符。例如,匹配 a
或 A
:
val r = "[aA]".toRegex()
val s = "aaAbbBccC"
println(r.findAll(s).map { it.value }.toList()) // 输出 ["a", "a", "A"]
如果我们想匹配 a
后面跟一个 a
或 A
:
val r = "a[aA]".toRegex()
val s = "aabaA"
println(r.findAll(s).map { it.value }.toList()) // 输出 ["aa", "aA"]
3.3 字符范围
使用 -
可以表示字符范围。例如,匹配所有大写字母:
val r = "[A-Z]".toRegex()
val s = "aaAbbBccC"
println(r.findAll(s).map { it.value }.toList()) // 输出 ["A", "b", "B", "C"]
匹配所有字母(大小写)和数字:
val r = "[a-zA-Z0-9]".toRegex()
val s = "abcABC123"
println(r.findAll(s).map { it.value }.toList()) // 输出 ["a", "b", "c", "A", "B", "C", "1", "2", "3"]
3.4 特殊字符:单词与数字
.
:匹配任意单个字符(除换行符)\w
:匹配字母、数字、下划线\W
:匹配非字母、数字、下划线字符\d
:匹配数字\D
:匹配非数字字符
示例:
val r = "\\w".toRegex()
val s = "a_B1?"
println(r.findAll(s).map { it.value }.toList()) // 输出 ["a", "_", "B", "1"]
3.5 空字符串与空白字符
\b
:匹配单词边界\s
:匹配空白字符(空格、制表符、换行符等)\S
:匹配非空白字符
示例:
val r = "\\bship\\b".toRegex()
val s1 = "flagship"
val s2 = "ship"
println(r.find(s1)) // 输出 null
println(r.find(s2)?.value) // 输出 "ship"
3.6 量词
量词用于指定字符或分组的重复次数:
量词 | 含义 |
---|---|
? |
0 次或 1 次 |
* |
0 次或多次 |
+ |
1 次或多次 |
{n} |
恰好 n 次 |
{n,} |
至少 n 次 |
{n,m} |
至少 n 次,最多 m 次 |
示例:
val r = "ni*ce".toRegex()
val s1 = "nice"
val s2 = "niiice"
val s3 = "nce"
println(r.findAll(s1).map { it.value }.toList()) // ["nice"]
println(r.findAll(s2).map { it.value }.toList()) // ["niiice"]
println(r.findAll(s3).map { it.value }.toList()) // ["nce"]
3.7 分组
使用 ()
可以将多个字符或表达式组合成一个整体,并对整个组应用量词。
示例:
val r = "(abc){2}".toRegex()
val s = "abcabc"
println(r.find(s)?.value) // 输出 "abcabc"
3.8 逻辑 OR
使用 |
可以实现逻辑“或”操作。
示例:
val r = "gr(a|e)y".toRegex()
val s1 = "gray"
val s2 = "grey"
println(r.find(s1)?.value) // 输出 "gray"
println(r.find(s2)?.value) // 输出 "grey"
3.9 行首与行尾
^
:匹配行首$
:匹配行尾
示例:
val r = "^0,0,0,$".toRegex()
val s = "0,0,0,"
println(r.find(s)?.value) // 输出 "0,0,0,"
3.10 贪婪与非贪婪
默认情况下,正则表达式是贪婪的(尽可能匹配更多字符),可以通过添加 ?
变为非贪婪模式。
示例:
val r = "\\d+?".toRegex()
val s = "555"
println(r.findAll(s).map { it.value }.toList()) // ["5", "5", "5"]
4. 实战示例
4.1 匹配 Mr.、Mrs.、Ms.
我们希望构建两个正则表达式:
- 匹配男性称谓(Mr.)
- 匹配女性称谓(Mrs. 或 Ms.)
示例:
val r1 = "Mr\\.".toRegex()
val r2 = "Mr?s\\.".toRegex()
val s1 = "Mr."
val s2 = "Mrs."
val s3 = "Ms."
println(r1.find(s1)?.value) // 输出 "Mr."
println(r2.find(s2)?.value) // 输出 "Mrs."
println(r2.find(s3)?.value) // 输出 "Ms."
4.2 加入 Miss
如果还要匹配 Miss
(无点号):
val r = "M(r?s\\.|iss)".toRegex()
val s1 = "Mr."
val s2 = "Mrs."
val s3 = "Ms."
val s4 = "Miss"
println(r.find(s1)?.value) // 输出 "Mr."
println(r.find(s2)?.value) // 输出 "Mrs."
println(r.find(s3)?.value) // 输出 "Ms."
println(r.find(s4)?.value) // 输出 "Miss"
4.3 验证电子邮件地址
目标:提取所有合法的电子邮件地址。
规则:
- 包含
@
- 域名部分包含
.
,如example.com
- 顶级域名至少两个字符
- 可包含字母、数字、下划线、连字符、加号、百分号等
完整正则表达式:
val emailRegex = "\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b".toRegex()
val text = "请发送邮件至 john.doe@example.com 或 jane_doe+work@sub.domain.co.uk"
val emails = emailRegex.findAll(text).map { it.value }.toList()
println(emails) // 输出 ["john.doe@example.com", "jane_doe+work@sub.domain.co.uk"]
5. 小结
本文系统介绍了 Kotlin 中正则表达式的基础语法和实际应用技巧。通过掌握这些内容,你可以更高效地进行文本处理、数据清洗、格式验证等任务。
✅ 正则表达式虽然强大,但也要注意其可读性和性能问题。
❌ 避免过度使用正则表达式处理复杂结构(如 HTML、JSON)。
⚠️ 使用时注意转义字符,避免语法错误。
如果你经常处理字符串,正则表达式是不可或缺的工具之一。熟练掌握它,能让你在日常开发中事半功倍。