Ruby正则表达式:带可选匹配且优先右侧匹配的问题求助
Ruby正则提取主机名中的租户与环境
我不是正则表达式专家,日常使用没问题,但现在碰到个棘手的需求,想请教大家。
我用Ruby处理一系列主机名,示例如下:
demo.some.host demo-tj.some.host demo-staging.some.host demo-tj-staging.some.host demo-dev-staging.some.host demo-tj-dev-staging.some.host
需要从这些主机名里一致提取出租户(tenant)和环境(environment),预期结果是:
demo, nil demo-tj, nil demo, staging demo-tj, staging demo, dev-staging demo-tj, dev-staging
我之前尝试了下面的正则:
> 'demo-dev-staging.test.host'.match(/\A(?<tenant>[a-z\-]+[^\-]{1})?(-(?<env>dev-staging|staging))?\.test\.host\z/) => #<MatchData "demo-dev-staging.test.host" tenant:"demo-dev-staging" env:nil>
问题出在租户的匹配把环境部分的字符也包含进去了——我原以为明确指定环境值会有优先级,但因为环境是可选匹配,正则优先匹配了更长的租户部分,导致环境始终捕获不到。
我想知道能不能用单个正则解决这个问题?有没有办法让右侧的环境匹配优先?我知道可以用多个正则依次处理,但还是希望用单个正则搞定。
解决方案
可以通过优先匹配带环境的模式来实现,把带环境的匹配分支放在前面,让正则先尝试匹配这种情况,再匹配不带环境的:
regex = /\A(?<tenant>[a-z-]+)-(?<env>dev-staging|staging)\.some\.host\z|\A(?<tenant>[a-z-]+)\.some\.host\z/
匹配逻辑:
- 第一个分支
\A(?<tenant>[a-z-]+)-(?<env>dev-staging|staging)\.some\.host\z:专门匹配带有-staging或-dev-staging后缀的主机名,先捕获前面的租户部分,再捕获环境值。 - 第二个分支
\A(?<tenant>[a-z-]+)\.some\.host\z:匹配没有环境后缀的主机名,只捕获租户,环境会自动为nil。
用这个正则测试所有示例:
demo-dev-staging.some.host→ tenant:demo,env:dev-stagingdemo-tj-staging.some.host→ tenant:demo-tj,env:stagingdemo.some.host→ tenant:demo,env:nil
完全符合预期。
为什么之前的正则失效?
你之前的正则里,租户部分的[a-z\-]+[^\-]{1}写法冗余([a-z\-]+已经包含了最后一个非-字符),而且因为环境匹配是可选的,正则会尽可能匹配更长的租户字符串,导致环境部分永远不会被触发。而把带环境的模式放在前面,正则会优先匹配这种明确的结构,避免了贪婪匹配的问题。
内容的提问来源于stack exchange,提问作者RobL
相关产品推荐
相关产品推荐

