Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用变量分组使用ruby正则表达式解析字符串的更优雅的方法?_Ruby_Regex - Fatal编程技术网

使用变量分组使用ruby正则表达式解析字符串的更优雅的方法?

使用变量分组使用ruby正则表达式解析字符串的更优雅的方法?,ruby,regex,Ruby,Regex,目前,我有一个正则表达式,如下所示: ^(cat|dog|bird){1}(cat|dog|bird)?(cat|dog|bird)?$ 它至少匹配一个长单词列表的1个实例,最多匹配3个实例,并通过相应的变量使每个组的匹配单词可用 有没有办法修改它,这样我就可以返回字符串中每个单词的结果,而不必事先指定组数 ^(cat|dog|bird)+$ 工作,但仅单独返回最后一个匹配,因为只有一个组。如果需要重复正则表达式的部分,一个选项是将重复的部分存储在变量中,并仅引用,例如: r = "(cat

目前,我有一个正则表达式,如下所示:

^(cat|dog|bird){1}(cat|dog|bird)?(cat|dog|bird)?$
它至少匹配一个长单词列表的1个实例,最多匹配3个实例,并通过相应的变量使每个组的匹配单词可用

有没有办法修改它,这样我就可以返回字符串中每个单词的结果,而不必事先指定组数

^(cat|dog|bird)+$

工作,但仅单独返回最后一个匹配,因为只有一个组。

如果需要重复正则表达式的部分,一个选项是将重复的部分存储在变量中,并仅引用,例如:

r = "(cat|dog|bird)"
str.match(/#{r}#{r}?#{r}?/)

您可以分两步完成:

  • 使用
    /^(猫狗鸟)+$/
    (或更好的
    /\A(猫狗鸟)+\z/
    )确保匹配
  • 然后
    string.scan(/cat | dog | bird/)
    获取片段
  • 您还可以使用
    split
    和一个集合同时执行这两项操作。假设您的单词在数组
    a
    中,字符串在
    s
    中,那么:

    words = Set.new(a)
    re    = /(#{a.map{|w| Regexp.quote(w)}.join('|')})/
    parts = s.split(re).reject(&:empty?)
    if(parts.any? {|w| !words.include?(w) })
      # 's' didn't match what you expected so throw a
      # hissy fit, format the hard drive, set fire to
      # the backups, or whatever is appropriate.
    else
      # Everything you were looking for is in 'parts'
      # so you can check the length (if you care about
      # how many matches there were) or something useful
      # and productive.
    end
    
    当您与包含组的模式一起使用时

    相应的匹配项也将在数组中返回

    在这种情况下,
    split
    将为我们提供类似于
    [“”,“cat”,“dog”]
    的内容,空字符串将只出现在我们正在寻找的分隔符之间,因此我们可以将它们设为不存在。这可能是对
    split
    的意外使用,因为我们对分隔符的兴趣大于被分隔的内容(除了确保没有任何内容被分隔),但它完成了任务


    根据您的评论,看起来您需要一个有序的替换,以便
    (ascard | car | as | id)
    尝试从左到右匹配。我在(Ruby 1.9正则表达式引擎)文档中找不到任何说明
    是有序的还是无序的;似乎被指定(或至少强烈暗示)是有序的,Ruby的行为肯定像是有序的:

    >> 'pancakes' =~ /(pan|pancakes)/; puts $1
    pan
    
    因此,在构建正则表达式时,您可以从最长到最短对单词进行排序:

    re = /(#{a.sort_by{|w| -w.length}.map{|w| Regexp.quote(w)}.join('|')})/
    
    并希望Oniguruma真的能匹配从左到右的交替。好吧,Ruby的正则表达式将是安全的,所以这种方法应该是安全的

    或者你可能是个十足的偏执狂,分步解析;首先,您要确保字符串看起来像您想要的:

    if(s !~ /\A(#{a.map{|w| Regexp.quote(w)}.join('|')})+\z/)
      # Bail out and complain that 's' doesn't look right
    end
    
    将你的单词按长度分组:

    by_length = a.group_by(&:length)
    
    扫描从最长单词到最短单词的组:

    # This loses the order of the substrings within 's'...
    matches = [ ]
    by_length.keys.sort_by { |k| -k }.each do |group|
      re = /(#{a.map{|w| Regexp.quote(w)}.join('|')})/
      s.gsub!(re) { |w| matches.push(w); '' }
    end
    # 's' should now be empty and the matched substrings will be
    # in 'matches'
    

    在这些方法中仍然有可能存在重叠的空间,但至少您可以提取最长的匹配。

    好的,所以我找到了解决方案

    看起来不可能创建未知数量的组,因此我开始寻找另一种实现预期结果的方法:能够辨别字符串是否由给定列表中的单词组成;并在每个位置匹配尽可能长的单词

    我一直在读《掌握正则表达式》一书Jeffrey E.F.Friedl和它为我揭示了一些事情。事实证明,基于NFA的Regexp引擎(如Ruby中使用的引擎)是顺序的,也是懒惰/贪婪的。这意味着您可以使用给模式选择的顺序来指定模式的匹配方式。这解释了扫描返回可变结果的原因,它在列表中查找符合条件的第一个单词,然后转到下一个匹配项。按照设计,它不是在寻找最长的比赛,而是第一场。所以为了纠正这个问题,我所需要做的就是对用于生成正则表达式的单词数组进行重新排序,从字母顺序到长度顺序(从最长到最短)

    现在,通过扫描找到的第一个匹配项将是可用的最长单词。使用扫描判断字符串是否只包含列表中的单词也非常简单:

    if "ascarid".scan(regexp).join.length == word.length
      return true
    else
      return false
    end
    
    感谢所有回复这个问题的人,我希望这将在将来帮助其他人。

    。如果我在PowerShell中编写以下内容

    $pat = [regex] "^(cat|dog|bird)+$"
    $m = $pat.match('birddogcatbird')
    $m.groups[1].captures | %{$_.value}
    
    然后我得到

    bird
    dog
    cat
    bird
    

    当我运行它时。我对IronRuby的了解甚至比我对PowerShell的了解还要少,但也许这意味着你也可以在IronRuby中使用。

    有没有理由不使用
    string.scan(/cat | dog | bird/)
    ?@riffraff:
    “dogpancakesbird”。scan(/cat | dog | bird/)
    啊,我没有注意到锚点谢谢。假阳性。只有当整个字符串被regexp吞没时,匹配才能为true。实际使用的单词列表非常大,为了让问题更容易理解,我将其缩减。我已经将单词数组(实际上比示例中长得多)存储在一个变量中,我只是从示例中删除了这个,以避免模糊问题。也许我应该提到,但我已经尝试过使用split。问题是split将返回它匹配的第一个片段,因此对于regexp中的大量单词数组,使用scan会出现许多误报和不匹配。我将示例中的单词列表删减,因为这会混淆问题。分组是我能找到的唯一方法,它可以在没有很多错误的情况下实现我所需要的,但是我必须预先指定组的数量。有没有一种方法可以动态地做到这一点?这是问题的关键,没有实现匹配。@i0n:所以有些“单词”相互重叠,您希望最长的单词匹配,然后再看较短的单词?这实际上是一个偶然的生物学问题吗?是的。例如,理想情况下,“蛔虫”一词与“蛔虫”一词匹配。目前它将匹配为3个单词:“as”“car”“id”。我需要的模式是贪婪的,但始终匹配整个字符串,如果可能的话@i0n:我添加了一个更新,其中包含一些可能性(它们太大,无法发表评论)。看起来我们得出了相同的结论。谢谢你的帮助!是的,我在寻找
    /
    
    bird
    dog
    cat
    bird