MatchResult

理解 Kotlin 中的 MatchResult 类能帮助你高效地使用正则表达式。
这个类提供了关于匹配操作结果的详细信息。例如,如果你用 find() 函数在字符串中查找匹配,它会返回一个 MatchResult 对象(如果没找到则为 null):

val regex: Regex = """a(\d+)b""".toRegex()
val input = "a123b a4bc"

val firstMatch: MatchResult? = regex.find(input)
其他函数
  • matchAt()matchEntire() 也会返回 MatchResult

  • findAll() 会返回一个匹配结果序列(Sequence<MatchResult>)。


MatchResult 的属性和方法

value:完整匹配的字符串
println(firstMatch?.value) // 输出: a123b

这个属性表示整个匹配结果的字符串。


range:匹配在原始字符串中的位置
println(firstMatch?.range) // 输出: 0..4

这个属性表示匹配发生的索引范围。


groups:捕获组集合(MatchGroupCollection
println(firstMatch?.groups)
// 输出: [MatchGroup(value=a123b, range=0..4), MatchGroup(value=123, range=1..3)]
  • 第一个组是整个匹配。

  • 后面是括号中捕获的每个组。


groupValues:组对应的值组成的 List<String>
println(firstMatch?.groupValues) // 输出: [a123b, 123]

如果某个组没有捕获内容(如它是可选的且未出现),其值是空字符串。


destructured:解构捕获组值
val regex = """(\d{4})-(\d{2})-(\d{2})""".toRegex()
val input = "2024-01-01"

val secondMatch = regex.find(input)!!

val (year, month, day) = secondMatch.destructured

println("Year: $year")   // 输出: Year: 2024
println("Month: $month") // 输出: Month: 01
println("Day: $day")     // 输出: Day: 01

destructured 允许你将捕获组解构为变量,变量数必须与捕获组数匹配。

注意:!! 会抛出空指针异常,如果 find() 返回的是 null


next():获取下一个匹配项
println(firstMatch?.next()?.groupValues) // 输出: [a4b, 4]

findAll() 不同,next() 可以更灵活地逐个处理匹配项,适合处理长文本。


实用例子:提高字符串中所有数字的 10%

fun main() {
    val regex = Regex("""\d+(\.\d+)?""")
    val input = "The price is $12.99 for the first item, and $9.99 for each additional item."
    val output = StringBuilder()
    var match = regex.find(input)
    var lastIndex = 0

    while (match != null) {
        output.append(input.substring(lastIndex, match.range.first))
        val number = match.value.toDouble()
        val increasedNumber = number * 1.1
        output.append("%.2f".format(increasedNumber))
        lastIndex = match.range.last + 1
        match = match.next()
    }

    output.append(input.substring(lastIndex))
    println(output)
}

解释:

  • 用正则 \d+(\.\d+)? 匹配整数或小数。

  • 每次找到一个匹配后:

    • 加入前面的非数字部分。

    • 解析数字并涨价 10%,保留两位小数。

    • 使用 next() 查找下一个匹配。

  • 最后补上剩下的文本。

输出:

The price is $14.29 for the first item, and $10.99 for each additional item.

非捕获组:(?:...)

有时我们只想分组但不需要捕获

val regex = Regex("""\d+(\.\d+)?""")

此时:

Groups: [12.99, .99]
Groups: [9.99, .99]

如果我们不需要 .99 被捕获,可以用非捕获组:

val regex = Regex("""\d+(?:\.\d+)?""")

现在结果是:

Groups: [12.99]
Groups: [9.99]

命名捕获组:(?<name>...)

更可读、更易维护:

val regex = """(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})""".toRegex()
val input = "2023-12-31"

val matchResult = regex.find(input)

println(matchResult?.groups?.get("year")?.value)  // 输出: 2023
println(matchResult?.groups?.get("month")?.value) // 输出: 12
println(matchResult?.groups?.get("day")?.value)   // 输出: 31

也可使用更简洁写法:

println(matchResult.groups["year"]?.value)
println(matchResult.groups["month"]?.value)
println(matchResult.groups["day"]?.value)

也可以通过索引访问:

println(matchResult.groups[1]?.value) // 同 groups["year"]

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐