正则表达式(Scala 版)全解析
一、正则表达式的定义、作用、应用场景
1. 定义
正则表达式(Regular Expression,简称 Regex/RE)是一种描述文本模式的语法规则,本质是一串特殊字符组成的 “匹配模板”,用于匹配、查找、替换、校验字符串。Scala 中没有独立的正则语法,复用 Java 的正则引擎,同时提供了更简洁的语法糖(如.r方法、模式匹配集成)。
2. 核心作用
- 匹配校验:验证字符串是否符合指定格式(如手机号、邮箱、身份证);
- 查找提取:从文本中提取符合规则的子串(如从日志中提取所有 IP 地址);
- 替换修改:批量替换文本中符合规则的内容(如替换所有敏感词、格式化字符串);
- 分割切分:按指定模式分割字符串(如按 “任意空白符” 分割文本)。
3. 典型应用场景
| 场景 | 示例 |
|---|---|
| 数据校验 | 校验手机号、邮箱、密码复杂度 |
| 日志分析 | 从 Nginx 日志中提取请求 URL、响应状态码 |
| 文本清洗 | 去除文本中的特殊符号、多余空格 |
| 爬虫数据提取 | 从 HTML 中提取标题、价格、链接 |
| 配置文件解析 | 解析.properties/.yml 中的规则配置 |
二、正则表达式的组成部分
正则表达式由原子、量词、边界符、修饰符、分组 / 断言五类核心元素组成:
1. 原子(最基础的匹配单元)
是正则的最小组成单位,代表 “匹配什么字符”,分为以下类型:
| 类型 | 示例 | 说明 |
|---|---|---|
| 普通字符 | a、1、@ | 匹配自身(如abc匹配字符串 "abc") |
| 预定义字符类 | \d、\w、\s | 匹配一类字符(如\d匹配任意数字) |
| 自定义字符集 | [0-9]、[a-z] | 匹配集合内任意一个字符 |
| 转义字符 | .、* | 匹配特殊符号(如.匹配点号,需转义) |
2. 量词(控制原子的匹配次数)
用于指定 “原子重复匹配多少次”,必须跟在原子 / 分组后:
| 量词 | 说明 | 示例 |
|---|---|---|
* | 匹配 0 次或多次(贪婪) | a* 匹配 ""、"a"、"aaa" |
+ | 匹配 1 次或多次(贪婪) | a+ 匹配 "a"、"aaa"(不匹配 "") |
? | 匹配 0 次或 1 次 | a? 匹配 ""、"a" |
{n} | 精确匹配 n 次 | a{3} 匹配 "aaa" |
{n,} | 匹配 n 次及以上 | a{2,} 匹配 "aa"、"aaa" |
{n,m} | 匹配 n 到 m 次(包含 n 和 m) | a{2,5} 匹配 "aa" 到 "aaaaa" |
3. 边界符(限定匹配位置)
用于指定 “匹配的字符串在文本中的位置”:
| 边界符 | 说明 | 示例 |
|---|---|---|
^ | 匹配字符串开头 | ^abc 匹配以 "abc" 开头的字符串 |
$ | 匹配字符串结尾 | xyz$ 匹配以 "xyz" 结尾的字符串 |
\b | 匹配单词边界(非单词字符) | \bhello\b 匹配独立的 "hello" |
4. 修饰符(全局规则)
Scala 中通过Regex的配置指定,控制匹配的全局行为:
| 修饰符 | 说明 | Scala 中使用方式 |
|---|---|---|
i | 忽略大小写 | "(abc)".r.unanchored(或结合(?i)) |
s | 单行模式(.匹配换行) | "(?s)a.b".r |
m | 多行模式(^/$匹配行首 / 行尾) | "(?m)^line".r |
5. 分组与断言(高级特性)
| 类型 | 示例 | 说明 |
|---|---|---|
| 捕获分组 | (abc) | 把多个原子视为一个整体,可提取匹配结果 |
| 非捕获分组 | (?:abc) | 分组但不捕获结果(性能更高) |
| 正向断言 | (?=abc) | 匹配 “后面跟着 abc” 的位置(不占字符) |
| 反向断言 | (?!abc) | 匹配 “后面不跟着 abc” 的位置 |
三、正则表达式的核心规则(Scala 常用)
1. 基础字符匹配规则
| 规则 | 含义 | Scala 示例 |
|---|---|---|
\d | 匹配任意数字(等价于[0-9]) | """\d""".r.findFirstIn("a1b") → Some("1") |
\D | 匹配非数字 | """\D""".r.findFirstIn("123a") → Some("a") |
\w | 匹配单词字符(0-9/A-Z/a-z/_) | """\w""".r.findFirstIn("a_@") → Some("a") |
\W | 匹配非单词字符 | """\W""".r.findFirstIn("a_@") → Some("@") |
\s | 匹配空白字符(空格 / 制表符 / 换行) | """\s""".r.findFirstIn("a b") → Some(" ") |
\S | 匹配非空白字符 | """\S""".r.findFirstIn(" a ") → Some("a") |
. | 匹配任意字符(除换行,单行模式可匹配) | """.""".r.findFirstIn("a\n") → Some("a") |
[abc] | 匹配 a、b、c 中的任意一个 | """[abc]""".r.findFirstIn("xby") → Some("b") |
[^abc] | 匹配非 a、b、c 的任意字符 | """[^abc]""".r.findFirstIn("xby") → Some("x") |
[a-z] | 匹配 a 到 z 的小写字母 | """[a-z]""".r.findFirstIn("A1b") → Some("b") |
2. 量词规则(贪婪 / 非贪婪)
- 贪婪匹配:默认行为,尽可能匹配更多字符(如
a.*b匹配 "a123b456b" 会匹配整个 "a123b456b"); - 非贪婪匹配:量词后加
?,尽可能匹配更少字符(如a.*?b匹配 "a123b456b" 会匹配 "a123b")。
3. Scala 特有的正则规则
- Scala 字符串中``是转义符,正则中的
\d需写为\d; - 通过
.r方法将字符串转为Regex对象(核心语法糖); - 支持模式匹配中直接使用正则。
四、Scala 中正则表达式的应用案例
以下案例基于 Scala 标准库scala.util.matching.Regex,覆盖常见场景:
案例 1:手机号校验
需求:校验字符串是否为合法手机号(1 开头,第二位 3-9,共 11 位数字)
scala
import scala.util.matching.Regex
object RegexDemo {
def main(args: Array[String]): Unit = {
// 定义正则表达式(注意双反斜杠)
val phonePattern: Regex = "^1[3-9]\d{9}$".r
// 待校验的手机号
val phones = List("13812345678", "12812345678", "1381234567", "138123456789")
// 校验逻辑
phones.foreach { phone =>
phonePattern.findFirstIn(phone) match {
case Some(_) => println(s"$phone 是合法手机号")
case None => println(s"$phone 是非法手机号")
}
}
}
}
输出:
plaintext
13812345678 是合法手机号
12812345678 是非法手机号
1381234567 是非法手机号
138123456789 是非法手机号
案例 2:邮箱提取
需求:从文本中提取所有合法邮箱地址
scala
object RegexDemo {
def main(args: Array[String]): Unit = {
// 邮箱正则:用户名@域名(简化版)
val emailPattern = """\w+@\w+.\w+""".r
val text = "联系我们:support@example.com 或 sales@company.cn,电话123456"
// 提取所有匹配的邮箱
val emails = emailPattern.findAllIn(text).toList
println("提取的邮箱:" + emails) // 输出:List(support@example.com, sales@company.cn)
}
}
案例 3:字符串替换
需求:将文本中的所有数字替换为*
scala
object RegexDemo {
def main(args: Array[String]): Unit = {
val numPattern = """\d""".r
val text = "订单号:123456,金额:999元"
val replaced = numPattern.replaceAllIn(text, "*")
println(replaced) // 输出:订单号:******,金额:***元
}
}
案例 4:模式匹配中使用正则
需求:根据字符串格式(手机号 / 邮箱 / 普通文本)分类处理
scala
object RegexDemo {
def main(args: Array[String]): Unit = {
val phonePattern = "^1[3-9]\d{9}$".r
val emailPattern = """\w+@\w+.\w+""".r
def classifyString(str: String): String = str match {
case phonePattern() => "手机号"
case emailPattern() => "邮箱"
case _ => "普通文本"
}
println(classifyString("13812345678")) // 手机号
println(classifyString("test@example.com")) // 邮箱
println(classifyString("hello world")) // 普通文本
}
}
案例 5:复杂分组提取(日志解析)
需求:从 Nginx 日志行中提取 IP、请求方法、URL
scala
object RegexDemo {
def main(args: Array[String]): Unit = {
// Nginx日志行示例:192.168.1.1 - - [10/Oct/2025:12:34:56 +0800] "GET /index.html HTTP/1.1" 200 1234
val nginxPattern = """(\d+.\d+.\d+.\d+) .* "(\w+) (/\S+) """.r
val logLine = "192.168.1.1 - - [10/Oct/2025:12:34:56 +0800] "GET /index.html HTTP/1.1" 200 1234"
logLine match {
case nginxPattern(ip, method, url) =>
println(s"IP: $ip, 方法: $method, URL: $url")
case _ =>
println("日志格式不匹配")
}
}
}
输出:
plaintext
IP: 192.168.1.1, 方法: GET, URL: /index.html
总结
Scala 中的正则表达式核心是复用 Java 正则引擎,通过.r方法简化使用,重点掌握:
- 基础规则(
\d/\w/ 量词 / 边界符); - 字符串中转义符
\的使用; findFirstIn/findAllIn/replaceAllIn等核心方法;- 模式匹配与正则的结合。实际开发中,优先使用简化版正则满足需求,复杂场景(如 URL/HTML 解析)可结合专用库(如 jsoup)提升效率。