You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin Regex的find方法起始索引异常行为及高效解决方案

问题解析与解决方案

为什么find(1)返回的还是"This"?

问题出在正则的正向回顾断言(lookbehind)上。你的正则(?<=[ \t\n$PUNC])(\w+)里的(?<=[ \t\n$PUNC])是零宽度断言,它只会检查当前位置左侧是否存在空白或标点,但不会受find方法指定的起始索引限制——哪怕你从索引1开始扫描,它仍然会回头检查索引0的字符。

当调用find(1)时,匹配器从索引1(字符'T')开始:

  • 检查索引1左侧的字符是'#',属于$PUNC,满足回顾条件;
  • 紧接着的\w+匹配从索引1开始的连续字母"This",所以直接返回该结果,不会继续向后查找"is"。

高效解决办法(无需生成子串)

方案1:跟踪上一次匹配的结束位置(推荐)

这是遍历正则匹配的标准做法,既高效又能精准控制查找范围:

val possibleString = "#This is a comment"
val regex = "(?<=[ \t\n$PUNC])(\\w+)".toRegex()

// 第一次查找
var currentMatch = regex.find(possibleString)
println(currentMatch?.value) // 输出: This

// 从上次匹配的结束位置开始下一次查找
currentMatch?.let {
    currentMatch = regex.find(possibleString, it.range.last + 1)
}
println(currentMatch?.value) // 输出: is

这种方式直接跳过已匹配的区域,完全避免了重复匹配,也不会占用额外内存。

方案2:调整正则表达式

如果需要更灵活的起始位置控制,可以修改正则,同时兼容字符串开头的单词(原正则无法匹配开头无分隔符的单词):

// 匹配规则:要么是字符串开头的单词,要么是前面有分隔符的单词
val regex = "(?:^|[ \t\n$PUNC])(\\w+)".toRegex()

使用这个正则时,需要通过groupValues[1]获取实际的单词内容(因为整个匹配包含了前面的分隔符或空字符串)。如果要从指定索引start开始查找有效单词,可以循环调用find,直到匹配的起始位置符合要求:

val start = 1
var targetMatch: MatchResult? = null
var currentMatch = regex.find(possibleString)
while (currentMatch != null) {
    // 计算单词的实际起始位置(跳过前面的分隔符)
    val wordStart = currentMatch.range.start + if (currentMatch.value[0].isLetterOrDigit()) 0 else 1
    if (wordStart >= start) {
        targetMatch = currentMatch
        break
    }
    currentMatch = regex.find(possibleString, currentMatch.range.last + 1)
}
println(targetMatch?.groupValues[1]) // 输出: is

内容的提问来源于stack exchange,提问作者qwerty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 11:31:08