我怎样才能加速这个正则表达式?

时间:2013-04-04 13:10:04

标签: ruby-on-rails ruby regex

我在Ruby 1.9.2p320上使用Rails 3.2.11。

我创建了一个summarized()方法,以显示某些关键字出现在一系列1,000到15,000个单词文档中的位置,这些文档存储为文本字符串。每个文档可以包含0到100次的每个关键字。

我有:

ActiveRecord::Schema.define(:version => 20130404000559) do
  create_table "references", :force => true do |t|
    t.text     "source_text"
  end
end

当我致电@reference.source_text.summarize(keywords)时,即使keyword 中只有一个keywords,以下方法也非常慢,

class String
  def summarized(keywords)
    safe_text = Array.new
    result = String.new
    keywords.each do |keyword|
      safe_text << self.strip_tags.gsub(/\n|\r/, " ").gsub("  ", " ").scan(/\w*.{0,250}#{keyword}.{0,250}\w*/im)
    end    
    return safe_text.flatten.uniq
  end    
end

如何加快速度?

更新:我现在正在考虑strip_tags may be at least one of the culprits的可能性。

2 个答案:

答案 0 :(得分:6)

让我们先来看看为什么它很慢。你的正则表达式是这样开始的:

/\w*.{0,250}

现在让我们从更糟糕的情况开始(在你的情况下非常频繁)。你有1000个字符没有你的关键字。您的正则表达式将首先出现第一个单词,如果您的关键字在此处,则稍后检查250个字符。不。它会回到249。还是不高兴。 250次尝试之后,将开始对\w*部分进行相同操作。你很幸运,单词很短。 它会对下一个(至少)749个字符做同样的事情。所以,在更糟糕的情况下,你会像250次一样通过你的字符串。 Laziness会解决问题,但是,我认为这不是你想要的行为(这个.{0,250}是处理字符串的开头,不是吗?)。

现在,一个很酷的正则表达式工具是外观。您可以继续或返回字符串以进行一些额外的检查。虽然它不是捕获字符串的一部分,但您可以在其中使用捕获组 缺点?你不能有任意大小的落后(该死,只是你需要的)。所以它不会很有效,因为你不能确保在开头有一个完整的单词,但你也无法匹配字符串的250个第一个字符中的关键字。

所以,这是一个奇怪的解决方案:
- 在字符串的开头添加250个字符(空格?...),以确保我们可以在开头捕获单词。
- 使用此正则表达式(您可以考虑m_x所说的内容,并且只对所有关键字使用一次):

/#{keyword}(?<=(.{250}))(.{0,250}\w*)/im

然后操作相当多的结果(通过添加捕获的组重新组合字符串,删除作为250个添加的部分的最终字符,可能删除第一个单词以确保它的整体...)它应该要快得多。

编辑(关于正则表达式的评论):

您可能想知道为什么(?<=(.{250}))放在关键字之后。好吧,只是在匹配关键字后进行捕获。这也意味着关键字也将成为捕获组的一部分,并且捕获组将仅在关键字之前捕获250-keyword.length字符(这不是一个问题,您可以根据长度动态创建正则表达式你的关键字,无论如何,我认为这不是一个问题)。你也可以在关键字的第一个字符之后放置lookbehind但是这很痛苦......好吧,我会在这里停止我的独白。

答案 1 :(得分:2)

这个怎么样?你只扫描一次字符串。

/\w*.{0,250}#{keywords.join('|')}.{0,250}\w*/im

作为旁注,这类应用可能会受益于sunspot这样的索引搜索引擎。