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

相同输入下JUnit测试与Android(ART)运行时正则匹配结果不一致

问题根因

1. 核心逻辑bug

你复用了同一个Matcher实例先后调用matches()和find()方法,而Matcher的所有匹配方法都会修改内部的游标位置,不同平台对匹配失败后的游标重置逻辑实现不同,直接导致结果差异:

  • OpenJDK(JUnit运行环境)中,matches()匹配整个字符串失败后,游标会被移动到字符串末尾,后续调用find()没有剩余字符可匹配,直接返回false,最终返回空列表
  • Android(ICU正则引擎)中,matches()匹配失败后游标会被重置到字符串开头,后续调用find()可以正常找到符合规则的子串,所以返回非空列表

2. 平台正则引擎实现差异

JUnit运行在标准OpenJDK环境,正则使用JDK内置的java.util.regex实现;Android平台底层使用ICU正则库,两者在边缘场景的行为存在原生差异,也是导致不一致的原因之一。

代码存在的其他问题
  • 你提前把输入字符串的所有空格都删除了,正则中的\\s?完全不会匹配任何内容,属于冗余规则,直接增加了正则的解析复杂度
  • findGroupMatches()逻辑完全冗余:你遍历所有捕获组后用完整正则去匹配组内容,只有0号全匹配组有可能通过校验,其余捕获组都是正则的一部分,不可能匹配完整规则,无需遍历
  • 捕获组遍历范围错误:Matcher.groupCount()返回的是自定义捕获组的数量,不包含0号全匹配组,0 until matcher.groupCount()的写法无法覆盖所有自定义捕获组
修复方案
// 简化正则,删除冗余的\\s?规则
private const val REGEX = "GE[a-zA-Z0-9]{2}[a-zA-Z0-9]{2}[0-9]{2}([0-9]{4}){3}[0-9]{2}"
private val PATTERN: Pattern = Pattern.compile(REGEX, Pattern.MULTILINE or Pattern.CASE_INSENSITIVE)

fun find(text: String): List<String> {
    val textFormatted = text.replace("\\s".toRegex(), "")
    val list = ArrayList<String>()
    // 匹配整个字符串,单独使用Matcher实例
    val fullMatcher = PATTERN.matcher(textFormatted)
    if (fullMatcher.matches()) {
        list.add(textFormatted)
        return list
    }
    // 查找子串匹配,使用新的Matcher实例
    val subMatcher = PATTERN.matcher(textFormatted)
    while (subMatcher.find()) { // 替换为while可匹配所有子串,原写法只会匹配第一个
        val match = subMatcher.group(0)
        if (match != null) {
            list.add(match)
        }
    }
    return list
}

修复后两个平台的运行结果会完全一致,如果你需要原测试用例返回空,可以调整正则规则或者输入内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 11:48:00