Scala中的正则表达式(一)

67 阅读2分钟

1. 创建正则表达式

基本语法

// 方式1:字符串的 .r 方法
val pattern1 = "\\d+".r

// 方式2:使用 Regex 类
import scala.util.matching.Regex
val pattern2 = new Regex("\\d+")

// 方式3:三重引号(避免转义)
val pattern3 = """\d+""".r

2. 常用模式

// 数字相关
val digits = "\\d".r        // 单个数字
val multiDigits = "\\d+".r  // 一个或多个数字
val exactly3Digits = "\\d{3}".r  // 正好3位数字

// 字母相关
val letters = "[a-zA-Z]".r  // 单个字母
val word = "\\w+".r         // 单词字符(字母、数字、下划线)

// 空白字符
val whitespace = "\\s".r    // 空白字符

// 特殊字符需要转义
val dot = "\\.".r           // 匹配点号
val slash = "\\\\".r        // 匹配反斜杠

3. 常用方法

查找匹配

val text = "我的电话是 13800138000,邮箱是 abc@example.com"
val phonePattern = "\\d{11}".r

// 1. findAllIn - 查找所有匹配
val allPhones = phonePattern.findAllIn(text).toList
// List(13800138000)

// 2. findFirstIn - 查找第一个匹配
val firstPhone = phonePattern.findFirstIn(text)
// Some(13800138000)

// 3. findPrefixOf - 检查字符串开头是否匹配
val startsWithNum = "\\d+".r.findPrefixOf("123abc")
// Some(123)

提取匹配组

val datePattern = """(\d{4})-(\d{2})-(\d{2})""".r
val date = "2024-01-15"

date match {
  case datePattern(year, month, day) =>
    println(s"年份: $year, 月份: $month, 日期: $day")
  case _ =>
    println("格式不匹配")
}
// 输出: 年份: 2024, 月份: 01, 日期: 15

替换操作

val text = "价格: $100, 折扣: $20"

// 1. replaceAllIn - 替换所有匹配
val result1 = "\\$\\d+".r.replaceAllIn(text, "***")
// 价格: ***, 折扣: ***

// 2. replaceFirstIn - 替换第一个匹配
val result2 = "\\$\\d+".r.replaceFirstIn(text, "***")
// 价格: ***, 折扣: $20

// 3. 使用函数进行替换
val result3 = "\\d+".r.replaceAllIn(text, m => (m.matched.toInt * 2).toString)
// 价格: $200, 折扣: $40

分割字符串

val text = "apple,banana,orange,grape"
val items = ",".r.split(text)
// Array(apple, banana, orange, grape)

4. 完整示例

object RegexDemo {
  def main(args: Array[String]): Unit = {
    // 验证邮箱格式
    val emailPattern = """^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$""".r
    
    val emails = List(
      "user@example.com",
      "invalid-email",
      "test@gmail.com"
    )
    
    emails.foreach { email =>
      email match {
        case emailPattern() => println(s"$email ✓ 有效")
        case _ => println(s"$email ✗ 无效")
      }
    }
    
    // 提取文本中的所有URL
    val text = "访问 https://www.example.com 或 http://test.org 获取更多信息"
    val urlPattern = """https?://[^\s]+""".r
    
    println("\n找到的URL:")
    urlPattern.findAllIn(text).foreach(println)
    
    // 格式化电话号码
    val phoneText = "我的电话是13812345678,另一个是13987654321"
    val phoneFormatPattern = "(\\d{3})(\\d{4})(\\d{4})".r
    
    val formatted = phoneFormatPattern.replaceAllIn(phoneText, 
      m => s"${m.group(1)}-${m.group(2)}-${m.group(3)}")
    println(s"\n格式化后: $formatted")
  }
}

5. 实用技巧

忽略大小写

val caseInsensitive = "(?i)hello".r
val result = caseInsensitive.findFirstIn("Hello WORLD")  // 匹配 Hello

多行模式

val multiLine = """(?m)^\d+\. .*$""".r
val text = """1. 第一行
              |2. 第二行
              |3. 第三行""".stripMargin

命名捕获组

val namedPattern = """(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})""".r
val date = "2024-01-15"

date match {
  case namedPattern(year, month, day) =>
    println(s"$year$month$day日")
}

6. 性能建议

  1. 预编译正则表达式:如果同一个模式需要多次使用,先编译好
  2. 避免过度使用:简单字符串操作能用 containsstartsWith 等方法的就不要用正则
  3. 注意贪婪匹配.* 会匹配尽可能多的字符,有时需要用 .*? 进行非贪婪匹配