使用变量分组使用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