Scala正则表达式特殊规则详解

作者:技术拾光时间:2026-06-07 08:09:07浏览:468

Scala正则表达式通过Regex类实现,字符串字面量用r方法转换,需注意反斜杠等特殊字符的转义。支持字符类、量词(*+?{n,m})、分组捕获、边界匹配(^$\b)及特殊字符转义。模式匹配中可结合分组与replaceAllIn实现替换,如手机号脱敏。

字面量规则

在 Scala 里,正则表达式是通过 scala.util.matching.Regex 这个类来体现的。最直接的方式就是用 r 方法把一个字符串字面量转换成正则表达式。举个例子,val pattern = "abc".r 跑完之后,pattern 就是一个正儿八经的正则对象,专门用来匹配包含 abc 的字符串。

不过有个小坑:当正则里要匹配那些在 Scala 字符串里本身就带特殊含义的字符(比如反斜杠 \)时,必须做转义。比如你想匹配一个反斜杠字符,正则里写 "\\",而在 Scala 代码里创建对象就得写成 val backslashPattern = "\\\\".r。原因很简单:Scala 字符串里一个反斜杠需要另一个反斜杠来转义,到了正则层面,反斜杠本身又是特殊字符(用于标记字符类、转义等),所以里外里一共要写四个反斜杠——听着绕,但习惯就好。

字符类规则

字符类用方括号 [] 来定义。比如 [abc] 能匹配 a、b 或 c 这三个字符中的任意一个。在 Scala 里写成 val charClassPattern = "[abc]".r 就行。

如果字符范围是连续的,可以用范围表示法,比如 [a-z] 匹配所有小写字母,对应代码 val lowerCasePattern = "[a-z]".r。

如果你想匹配“除了某几个字符以外的任意字符”,就在方括号内第一个位置放一个 ^。例如 [^abc] 会匹配任何不是 a、b、c 的字符。Scala 代码为 val notABCPattern = "[^abc]".r。

量词规则

量词控制的是“前面那个元素出现多少次”。这块是正则里最常用的部分,我们来逐一梳理。

  • * 表示前面的元素出现零次或多次。比如 a* 可以匹配空字符串、a、aa、aaa……说白了就是“有也行,没有也行,越多越好”。Scala 里 val starPattern = "a*".r。
  • + 表示前面元素出现一次或多次。比如 a+ 匹配 a、aa……但不匹配空串。Scala 代码 val plusPattern = "a+".r。
  • 表示前面元素出现零次或一次。比如 a 只能匹配空串或者单个 a。Scala 写成 val questionMarkPattern = "a".r。
  • 用 {n} 可以指定精确的出现次数。例如 a{3} 只匹配连续三个 a,即“aaa”。Scala 代码 val exactCountPattern = "a{3}".r。
  • {n,} 表示至少 n 次。比如 a{2,} 匹配 aa、aaa……Scala 代码 val atLeastCountPattern = "a{2,}".r。
  • {n,m} 表示至少 n 次,最多 m 次。例如 a{1,3} 匹配 a、aa、aaa,但不匹配四个 a。Scala 代码 val rangeCountPattern = "a{1,3}".r。

分组规则

分组用圆括号 () 实现。它的作用是两方面的:一是能把一组字符当成一个整体来应用量词,二是能在匹配后提取出子串(即“捕获组”)。

比如 (ab)+ 可以匹配 ab、abab、ababab……注意,这里 + 作用在 (ab) 这个整体上。Scala 里写 val groupPattern = "(ab)+".r。

当拿到匹配结果后,你可以通过 match 语句或者 findFirstMatchIn 等方法获取每个分组捕获到的具体内容。这个能力在实际项目中非常有用,比如提取电话号码、身份证号里的特定部分。

边界匹配规则

边界匹配不匹配具体字符,而是匹配位置。主要有三个。

  • ^ 匹配字符串开头。比如 ^abc 只匹配以 abc 开头的字符串。Scala 代码 val startPattern = "^abc".r。
  • $ 匹配字符串结尾。比如 abc$ 只匹配以 abc 结尾的字符串。Scala 代码 val endPattern = "abc$".r。
  • \b 匹配单词边界。单词边界指的是单词字符(字母、数字、下划线)和非单词字符之间的位置。例如 \babc\b 能匹配独立的 abc 单词,但不会匹配 abcd 里的 abc。在 Scala 里要小心转义:val wordBoundaryPattern = "\\babc\\b".r。

特殊字符转义规则

正则表达式里很多字符都有特殊含义,比如 ^(在字符类外表示开头)、$(结尾)、*(量词)等等。如果你想匹配这些字符本身,就需要用反斜杠进行转义。比如匹配一个星号,正则里要写成 \*,然后在 Scala 代码中创建对象时写成 val asteriskPattern = "\\*".r。——记住,每一次转义都意味着在字符串层面要多加一层反斜杠。

模式匹配中的特殊规则

Scala 的模式匹配和正则结合得相当紧密,你可以在 match 语句中直接使用正则表达式来匹配字符串,非常方便。下面这个例子能让你直观感受到分组和替换的威力:

package test37
// 任务:把字符串中的手机号打码:类似:138****5678
object test2 {
  def main(args: Array[String]): Unit = {
    var str= "张先生,手机号:13812345678  你买的 xxx 到了"
    // 开始你的代码
    // ()是分组,不会影响正则表达式的查询结果,但是会把分组找到的内容单独保存在group中
    val reg = "(1[3-9]\\d)(\\d{4})(\\d{4})".r
    // reg.replaceAllIn的功能:在目标字符串str中,用正则表达式reg去查找,找到之后,用箭头函数的返回值来替换
    // m就是正则表达式找到的内容
    // 如果正则表达式中有(),则可以通过m.group来找到对应的分组
    val newStr = reg.replaceAllIn(str,(m)=>{
      println(s"第一组:${m.group(1)}")
      println(s"第二组:${m.group(2)}")
      println(s"第三组:${m.group(3)}")
      println("-"*48)
      m.group(1)+"****"+m.group(3)
    })
    println(newStr)
    // 输出:张先生,手机号:138****5678  你买的 xxx 到了
  }
}

这个例子里,我们把手机号分成了三段:前三位(运营商号段)、中间四位(隐藏)、后四位。然后通过 replaceAllIn 和分组提取,只保留第一组和第三组,中间用 **** 替代,轻松实现脱敏。分组判断、替换逻辑都在一行 lambda 里完成,简洁又实用。