【问题标题】:How to match lines with multiple regex-es at once?如何一次匹配具有多个正则表达式的行?
【发布时间】:2014-07-31 22:12:39
【问题描述】:

我有一个包含标签的数组,通常是简单的英文单词,大约 3-6 个元素。我必须从包含任何顺序的所有标签的文本文件中选择行(小写或大写不计,不区分大小写)。如何在 Ruby 中实现这一点?我应该使用正则表达式还是其他方法?

例如,我知道如何逻辑或正则表达式模式 /tag1|tag2|tag3/ 是否有可能以任何方式对它们进行逻辑与? /tag1 & tag2 & tag3/ ?

【问题讨论】:

标签: ruby arrays regex text filter


【解决方案1】:

是的。要对标签进行与标记,请在字符串锚^ 的开头之后使用前瞻:

^(?=.*tag1)(?=.*tag2)(?=.*tag3).*

您可以通过循环遍历您的数组以编程方式组装此正则表达式。

【讨论】:

  • 谢谢,我不太明白这一点。如何通过循环数组来组装正则表达式?也许使用 Regex.union?
  • 你说你在数组中有标签,对吧?所以你连接^,然后连接一系列(?=.* + sometag + ),最后给你你的正则表达式:^(?=.*tag1)(?=.*tag2)(?=.*tag3)
  • 不错的一个。康斯坦丁,如果r 是xz 的正则表达式,"tag1 tag2 tag3" =~ r #=> 00truey,而"tag1 tag1 tag3" =~ r => nil(不匹配)。 xy,我不明白在这个例子中0 的返回意味着什么,因为字符串的第一个字符是零宽度前瞻的一部分。你能解释一下吗?
  • @CarySwoveland 好收获!我写了 AND 但忘记匹配该行。刚刚在末尾添加了.*。谢谢。
  • 我看到我的困惑与你的答案无关,但也许你或其他人可以让我直截了当。考虑:"abc" =~ /b/ #=> 1"abc" =~ /a/ #=> 0"abc" =~ // #=> 0。最后一个没看懂也许这是一种语言妥协,因为需要一个真实的回报,但我认为这可能会很麻烦。
【解决方案2】:

非正则表达式方法是:

tags.all? {|tag| string.include? tag}

为了不区分大小写,假设string 是一个小写的行。并且标签已经被小写了。

正则表达式更灵活;它们可以配置为匹配单词边界等。

【讨论】:

  • 马克,你当然可以在块中使用正则表达式来处理单词边界和大小写,例如,string =~ /(?:^|\s)tag(?:\s|$)/i
  • @CarySwoveland 确实。
【解决方案3】:

这是您可以做到的一种方式。

代码

def line_contains_tags(str, tags)
    str.scan(/(?:^|\s)(#{tags.join('|')})(?=\s|$)/)
       .flatten(1)
       .uniq.size == tags.size
end

示例

tags = %w{tag1 tag2 tag3}
line_contains_tags("tag1 tag2 tag3", tags) #=> true
line_contains_tags("tag2 tag1 tag3", tags) #=> true
line_contains_tags("tag1 tag3"     , tags) #=> false
line_contains_tags("tag1 tag1 tag3", tags) #=> false

说明

正则表达式扫描tags 的每个元素的字符串,直到找到匹配项或断定没有匹配项。匹配是tags 的元素,前面是字符串的开头或空白字符,后面是由空白字符或字符串结尾组成的零长度(正向前瞻)字符串。

tags = %w{tag1 tag2 tag3}
  #=> ["tag1", "tag2", "tag3"]
regex = /(?:^|\s)(#{tags.join('|')})(?=\s|$)/
  #=> /(?:^|\s)(tag1|tag2|tag3)(?=\s|$)/

str = "tag1 tag2 tag3"
a = str.scan(regex)             #=> [["tag1"], ["tag2"], ["tag3"]]
b = a.flatten(1).uniq           #=> ["tag1", "tag2", "tag3"]
b.size == 3                     #=> true

对于最后一个例子,

str = "tag1 tag1 tag3"
a = str.scan(r).flatten(1).uniq #=> ["tag1", "tag3"]
a.size == 3                     #=> false

【讨论】:

  • Thx,按照你的方法我找到了一个更好的方法:如果数组“t”包含这些行,那么 t.sort{|x,y| x.scan(/#{tags.join('|')}/i).uniq.length y.scan(/#{tags.join('|')}/i).uniq.length} .last 给出了包含最多匹配项的那一行,这正是我想要的。因为有时只有标签的一个子集很好地定义了文本中的一行。
  • 更好:t.sort_by{|x| [x.scan(/(?:^|\s|_|\-)(#{tags.join('|')})(?=\s|$|_|\-)/i).uniq .length,x]}
  • 如果您只需要包含最多匹配项的行,请考虑使用Enumerable#max_by(与您的sort_by 块一起),而不是对整个数组进行排序。如果您想要所有具有最大匹配数的行(在平局的情况下),您可以按照max_byEnumerable#select
  • 哦,thx,我从来没有听说过max_by的存在,ruby是一个真正的宝藏。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-11
  • 1970-01-01
  • 2011-07-11
  • 1970-01-01
  • 2020-10-10
  • 1970-01-01
  • 2015-04-27
相关资源
最近更新 更多