(Ruby)正则表达式可选匹配

时间:2014-09-03 03:00:04

标签: ruby regex

我正在编写一个Rack应用程序来拆分以某些前缀结尾的主机名。

例如,主机名(和端口)hello.world.lvh.me:3000需要分为令牌hello.world.lvh.me:3000。此外,前缀(hello.world),后缀(.lvh.me)和端口(:3000)都是可选的。

到目前为止,我有一个(Ruby)正则表达式,看起来像/(.*)(\.lvh\.me)(\:\d+)?/

这成功地将主机名分解为组件部分,但是当缺少一个或多个可选组件时,它会崩溃,例如,适用于hello.world:3000lvh.me:3000或甚至普通hello.world

我尝试将?添加到每个组中以使其成为可选项(/(.*)?(\.lvh\.me)?(\:(\d+)?/),但这总是以第一个组(.*)结束,捕获整个字符串并停在那里

我的直觉是,这可以通过环视来解决,但我承认这对我来说是一个全新的正则表达式。

5 个答案:

答案 0 :(得分:3)

您可以尝试使用此模式:

\A(?=[^:])(.+?)??((?:\.|\A)lvh\.me)?(:[0-9]+)?\z

前瞻(?=[^:])检查至少有一个字符不是: (换句话说,不是单独的端口)。这意味着至少存在hello.wordlvh.me

第一组是可选的非贪婪??,这意味着它只在需要时才匹配。

\A\z是字符串的开头和结尾的锚点(当^$用于该行时)

请注意,字符类\d匹配Ruby中的所有unicode数字,但在这种情况下,您只需要ascii数字。最好使用[0-9]

另请注意\A(?=[^:])((?>[^l:\n.]+|\.|\Bl|l(?!vh\.me\b))*)((?:\.|\A)lvh\.me)?(:[0-9]+)?\z可能更高效。

online demo

答案 1 :(得分:2)

尝试^(.*?)?(\.?lvh\.me)?(\:\d+)?$

我补充说:

  • ?*非贪婪
  • 的第一个群组
  • ^,$将其锚定到开头和结尾。
  • a ?\.之前lvh,因为你想匹配lvh.me:3000而不是.lvh.me:3000

答案 2 :(得分:2)

标记答案

只是为了好玩,我决定看看是否有一种相对简单的方法可以在没有复杂的正则表达式的情况下做你想做的事情。我使用的唯一正则表达式是分割和验证。

这适用于我提供的语料库,以及一些变体。

str    = 'hello.world.lvh.me:3000'
tokens = str.split /[.:]/
port   = tokens.last =~ /\A\d+\z/ ? ?: + tokens.pop : ''
domain = sprintf '.%s.%s', *tokens.pop(2)
prefix = tokens.join ?.

在某些情况下,你肯定需要检查空字符串,但它似乎比纯正则表达式解决方案更简单和/或更灵活。无论如何,发现它更具可读性。但是,如果你真的需要一个正则表达式,我相信其他一个答案会帮助你。

答案 3 :(得分:0)

您可以尝试拆分而不是匹配,

irb(main):012:0> "hello.world.lvh.me:3000".split(/\.(?=[^.:]+\.[^:.]+(?::\d+)?$)|:/)
=> ["hello.world", "lvh.me", "3000"]
irb(main):013:0> "hello.world:3000".split(/\.(?=[^.:]+\.[^:.]+(?::\d+)?$)|:/)
=> ["hello.world", "3000"]
irb(main):014:0> "lvh.me:3000".split(/\.(?=[^.:]+\.[^:.]+(?::\d+)?$)|:/)
=> ["lvh.me", "3000"]
irb(main):015:0> "hello.world".split(/\.(?=[^.:]+\.[^:.]+(?::\d+)?$)|:/)
=> ["hello.world"]
irb(main):016:0> "hello.world.lvh.me".split(/\.(?=[^.:]+\.[^:.]+(?::\d+)?$)|:/)
=> ["hello.world", "lvh.me"]

答案 4 :(得分:0)

看,妈,没有正则表达式!

def split_up(str)
  str.sub(':','.:')
     .split('.')
     .each_slice(2)
     .map { |arr| arr.join('.') }
end

split_up("hello.world.lvh.me:3000") #=> ["hello.world", "lvh.me", ":3000"]
split_up("hello.world:3000")        #=> ["hello.world", ":3000"]
split_up("hello.world.lvh.me")      #=> ["hello.world", "lvh.me"]
split_up("hello.world")             #=> ["hello.world"]
split_up("")                        #=> []

步骤:

str1 = "hello.world.lvh.me:3000" #=> "hello.world.lvh.me:3000"
str2 = str1.sub(':','.:')        #=> "hello.world.lvh.me.:3000"
arr  = str2.split('.')           #=> ["hello", "world", "lvh", "me", ":3000"]
enum = arr.each_slice(2)         #=> #<Enumerator: ["hello", "world", "lvh",
                                 #     "me", ":3000"]:each_slice(2)>
enum.to_a                        #=> [["hello", "world"], ["lvh", "me"],
                                 #    [":3000"]]
enum.map { |arr| arr.join('.') } #=> ["hello.world", "lvh.me", ":3000"]