Oracle REGEXP_SUBSTR提取NUPKG包信息时无法匹配点字符问题
问题背景
需要从NuGet包名字符串中拆分出模块名(module_name)和版本号(version),字符串格式示例如下:
FOO-BAR-AUDIT-DATABASE.NUPKG FOO.BAR.DATABASE-2.0.0.NUPKG
期望拆分效果:
'FOO.BAR.DATABASE' '2.0.0' | | module_name version
现有问题
当前使用的正则拆分逻辑,在模块名包含.字符时解析失败:仅FOO.BAR.DATABASE-2.0.0.NUPKG这条用例返回错误,module_name取值为foo而非预期的foo.bar.database,其余用例均正常。
现有执行代码(Oracle 19c环境):
select case module_name when expected then 'pass' else 'fail' end as test, y.* from( select lower(regexp_substr(t.pck, g.regex, 1, 1, '', 1)) as module_name, t.expected, to_number(regexp_substr(t.pck, g.regex, 1, 1, '', 3)) as major, to_number(regexp_substr(t.pck, g.regex, 1, 1, '', 5)) as minor, to_number(regexp_substr(t.pck, g.regex, 1, 1, '', 7)) as patch, (t.pck) as package_name from (select 'FUNKY_LOG_DATABASE-1.0.0.NUPKG' as pck, 'funky_log_database' as expected from dual union select 'FOO.BAR.DATABASE-2.0.0.NUPKG', 'foo.bar.database' from dual union select 'FOO-BAR-AUDIT-DATABASE.NUPKG', 'foo-bar-audit-database' from dual union select 'funk-database-1.nupkg', 'funk-database' from dual union select 'funk-database-1.2.nupkg', 'funk-database' from dual union select 'baz-database-1.0.1.nupkg', 'baz-database' from dual) t cross join (select '(.*?)(-(\d+)(\.(\d+))?(\.(\d+))?)?(\..*)' as regex from dual) g )y;
已经尝试将第一分组替换为([[:alnum:]._-]*?),结果无变化;改用贪婪匹配又会将版本号部分误匹配到模块名中。
解决方案
根因分析
原正则最后的(\..*)规则过于宽松,会匹配字符串中第一个.之后的所有内容。第一分组使用惰性匹配.*?时,会尽可能少匹配内容,直接停在第一个.的位置,把模块名中间的.误判为后缀的开头,导致模块名截断。
修正方案
仅需要将正则最后的后缀匹配规则调整为仅匹配字符串末尾的后缀即可,修改后的正则为:(.*?)(-(\d+)(\.(\d+))?(\.(\d+))?)?\.[^\.]+$
把原来cross join部分的正则替换为上述内容即可,无需修改其他代码。
规则说明
- 末尾的
\.[^\.]+$限制了后缀只能是字符串最后一个.到结尾的内容,不会误匹配模块名中间的. - 原有分组序号完全不变,major、minor、patch的提取逻辑不需要做任何调整
- 所有测试用例均可正常通过,模块名包含
.的场景也能正确解析
内容的提问来源于stack exchange,提问作者oey
相关产品推荐
相关产品推荐

