scala中的正则表达式(一)

56 阅读4分钟

一、正则表达式:定义、作用、应用场景

  1. 定义正则表达式(Regular Expression)是一种用于匹配、查找、替换字符串的字符模式规则,通过预定义的符号和语法组合,描述字符串的结构特征。
  2. 作用
  • 字符串匹配:验证字符串是否符合特定格式(如手机号、邮箱);
  • 字符串查找:提取文本中符合规则的子串(如日志中的时间);
  • 字符串替换:批量修改文本中符合规则的内容(如替换敏感词)。
  1. 应用场景
  • 数据验证:手机号、邮箱、身份证号等格式校验;
  • 文本解析:日志分析、爬虫数据提取;
  • 内容处理:代码注释清理、敏感词过滤;
  • 数据格式化:日期、数字的统一格式转换。

二、正则表达式的组成部分

正则表达式由普通字符元字符组成:

  1. 普通字符:无特殊含义的字符(如字母、数字、空格),直接匹配自身(如abc匹配字符串中的 “abc”)。

  2. 元字符:具有特殊含义的字符,是正则的核心,常见类型:

    • 匹配位置:^(行开头)、$(行结尾);
    • 匹配数量:*(0 次或多次)、+(1 次或多次)、?(0 次或 1 次)、{n}(恰好 n 次);
    • 匹配范围:[](字符集)、[^](非字符集)、|(或);
    • 预定义字符:\d(数字)、\w(字母 / 数字 / 下划线)、\s(空白符)等。

三、正则表达式的核心规则

  1. 匹配位置

    • ^abc:匹配以 “abc” 开头的字符串;
    • abc$:匹配以 “abc” 结尾的字符串;
    • ^abc$:精确匹配 “abc”(仅该字符串)。
  2. 匹配数量

    • a*:匹配 0 个或多个 “a”;
    • a+:匹配 1 个或多个 “a”;
    • a?:匹配 0 个或 1 个 “a”;
    • a{3}:匹配恰好 3 个 “a”;
    • a{2,5}:匹配 2 到 5 个 “a”;
    • a{2,}:匹配至少 2 个 “a”。
  3. 匹配范围

    • [a-z]:匹配任意小写字母;
    • [0-9]:匹配任意数字(等价于\d);
    • [^0-9]:匹配非数字字符;
    • a|b:匹配 “a” 或 “b”。
  4. 预定义字符(简化写法)

    • \d:等价于[0-9](数字);
    • \w:等价于[a-zA-Z0-9_](单词字符);
    • \s:等价于[ \t\n\r](空白符);
    • .:匹配任意字符(除换行符)。

四、Scala 中正则表达式的应用案例

Scala 通过scala.util.matching.Regex类实现正则操作,以下是常见场景示例:

案例 1:数据格式验证(手机号)

scala

import scala.util.matching.Regex

// 定义手机号正则:1开头,第二位为3-9,后9位为数字
val phoneRegex: Regex = "^1[3-9]\d{9}$".r

def validatePhone(phone: String): Boolean = phoneRegex.matches(phone)

// 测试
println(validatePhone("13812345678"))  // true
println(validatePhone("12812345678"))  // false(第二位不符合)

案例 2:提取文本中的邮箱

scala

val emailRegex: Regex = "\w+@\w+\.\w+".r
val text = "我的邮箱是test@example.com,备用邮箱是abc123@xyz.cn"

// 提取所有匹配的邮箱
val emails = emailRegex.findAllIn(text).toList
println(emails)  // List(test@example.com, abc123@xyz.cn)

案例 3:替换文本中的敏感词

scala

val sensitiveRegex: Regex = "敏感词1|敏感词2".r
val content = "这是敏感词1,那是敏感词2"

// 将敏感词替换为“***”
val filteredContent = sensitiveRegex.replaceAllIn(content, "***")
println(filteredContent)  // 这是***,那是***

案例 4:解析日志中的时间

scala

// 日志格式示例:2025-12-22 14:30:00 [INFO] 操作成功
val logTimeRegex: Regex = "(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})".r

val log = "2025-12-22 14:30:00 [INFO] 操作成功"
logTimeRegex.findFirstIn(log) match {
  case Some(time) => println(s"日志时间:$time")  // 日志时间:2025-12-22 14:30:00
  case None => println("未匹配到时间")
}

模板:

模板说明

  • 所有模板基于 scala.util.matching.Regex 实现;
  • 关键参数已标注注释,替换正则规则 / 目标文本即可适配业务场景;
  • 包含异常处理(如无匹配时的兜底逻辑),保证代码健壮性。

scala

import scala.util.matching.Regex

/**
 * Scala 正则表达式通用操作模板
 * 涵盖:格式验证、全量提取、单值提取、分组提取、批量替换、单次替换
 */
object RegexTemplate {
  // ====================== 1. 格式验证模板(如手机号/邮箱/身份证) ======================
  /**
   * 验证字符串是否符合指定正则规则
   * @param regexStr 正则表达式
   * @param target 待验证字符串
   * @return 验证结果(true/false)
   */
  def validateFormat(regexStr: String, target: String): Boolean = {
    val regex: Regex = regexStr.r
    regex.pattern.matcher(target).matches() // 精准匹配(全字符串符合规则)
  }

  // ====================== 2. 全量提取模板(提取所有匹配的子串) ======================
  /**
   * 提取文本中所有符合正则规则的子串
   * @param regexStr 正则表达式
   * @param text 目标文本
   * @return 匹配结果列表(无匹配则返回空列表)
   */
  def extractAll(regexStr: String, text: String): List[String] = {
    val regex: Regex = regexStr.r
    regex.findAllIn(text).toList // 惰性迭代器转列表,无匹配则为空
  }

  // ====================== 3. 单值提取模板(提取第一个匹配的子串) ======================
  /**
   * 提取文本中第一个符合正则规则的子串
   * @param regexStr 正则表达式
   * @param text 目标文本
   * @return 第一个匹配结果(Option类型,避免空指针)
   */
  def extractFirst(regexStr: String, text: String): Option[String] = {
    val regex: Regex = regexStr.r
    regex.findFirstIn(text)
  }

  // ====================== 4. 分组提取模板(提取正则分组中的内容) ======================
  /**
   * 提取正则分组中的内容(如从日志中拆分时间、级别、内容)
   * @param regexStr 带分组的正则表达式(分组用()包裹)
   * @param text 目标文本
   * @return 分组结果(List[List[String]],外层是匹配项,内层是分组)
   */
  def extractGroups(regexStr: String, text: String): List[List[String]] = {
    val regex: Regex = regexStr.r
    regex.findAllMatchIn(text)
      .map(matchResult => (0 to matchResult.groupCount).map(matchResult.group).toList)
      .toList
  }

  // ====================== 5. 批量替换模板(替换所有匹配的子串) ======================
  /**
   * 批量替换文本中所有符合正则规则的子串
   * @param regexStr 正则表达式
   * @param text 目标文本
   * @param replacement 替换后的内容
   * @return 替换后的文本
   */
  def replaceAll(regexStr: String, text: String, replacement: String): String = {
    val regex: Regex = regexStr.r
    regex.replaceAllIn(text, replacement)
  }

  // ====================== 6. 单次替换模板(替换第一个匹配的子串) ======================
  /**
   * 替换文本中第一个符合正则规则的子串
   * @param regexStr 正则表达式
   * @param text 目标文本
   * @param replacement 替换后的内容
   * @return 替换后的文本
   */
  def replaceFirst(regexStr: String, text: String, replacement: String): String = {
    val regex: Regex = regexStr.r
    regex.replaceFirstIn(text, replacement)
  }

  // ====================== 模板使用示例 ======================
  def main(args: Array[String]): Unit = {
    // 示例1:验证手机号
    val phoneRegex = "^1[3-9]\d{9}$"
    println("手机号验证(13812345678):" + validateFormat(phoneRegex, "13812345678")) // true
    println("手机号验证(12812345678):" + validateFormat(phoneRegex, "12812345678")) // false

    // 示例2:提取所有邮箱
    val emailRegex = "\w+@\w+\.\w+"
    val text = "邮箱1:test@example.com,邮箱2:abc123@xyz.cn"
    println("提取所有邮箱:" + extractAll(emailRegex, text)) // List(test@example.com, abc123@xyz.cn)

    // 示例3:提取第一个匹配的数字
    val numRegex = "\d+"
    val content = "年龄:25,身高:180cm"
    extractFirst(numRegex, content).foreach(firstNum => println("第一个数字:" + firstNum)) // 25

    // 示例4:分组提取日志(时间、级别、内容)
    val logRegex = "(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(\w+)\] (.*)"
    val log = "2025-12-22 15:30:00 [INFO] 操作成功;2025-12-22 15:31:00 [ERROR] 连接失败"
    println("日志分组提取:")
    extractGroups(logRegex, log).foreach(group => println(group))
    // 输出:
    // List(2025-12-22 15:30:00 [INFO] 操作成功, 2025-12-22 15:30:00, INFO, 操作成功)
    // List(2025-12-22 15:31:00 [ERROR] 连接失败, 2025-12-22 15:31:00, ERROR, 连接失败)

    // 示例5:替换所有敏感词
    val sensitiveRegex = "敏感词1|敏感词2"
    val sensitiveText = "这是敏感词1,那是敏感词2"
    println("敏感词替换后:" + replaceAll(sensitiveRegex, sensitiveText, "***")) // 这是***,那是***

    // 示例6:替换第一个数字为99
    println("替换第一个数字:" + replaceFirst(numRegex, content, "99")) // 年龄:99,身高:180cm
  }
}

模板扩展说明

  1. 正则转义处理:Scala 字符串中 `` 需转义为 \(如 \d 写为 \d),若正则规则复杂,可使用原始字符串 raw"^1[3-9]\d{9}$" 避免多次转义;
  2. 分组索引matchResult.group(0) 是整个匹配串,group(1)/group(2) 对应正则中第 1/2 个括号分组;
  3. 性能优化:若同一正则需多次使用,建议提前编译为 Regex 实例(避免重复编译);
  4. 复杂匹配:可结合 Regex.Match 的 start/end 方法获取匹配子串的位置,实现更精细的文本处理。