3

我在 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)时,以下方法非常慢,即使只有一个keywordkeywords

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 可能至少是罪魁祸首之一的可能性。

4

2 回答 2

6

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

/\w*.{0,250}

现在让我们从最坏的情况开始(在你的情况下很常见)。没有关键字,您有 1000 个字符。如果您的关键字在这里,您的正则表达式将首先检查第一个单词,然后检查 250 个字符。没有。它将倒退到第 249 位。还是不行。250 次尝试后,将开始对\w*部件执行相同操作。你很幸运,单词很短。它将对接下来的(至少)749 个字符执行相同的操作。所以,在最坏的情况下,你会遍历你的字符串 250 次。懒惰会解决问题,但是,我认为这不是您想要的行为(这.{0,250}是处理字符串的开头,不是吗?)。

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

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

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

然后对结果进行相当多的操作(通过添加捕获的组来重新组合字符串,删除作为添加的 250 个字符的一部分的最终字符,也许删除第一个单词以确保它是完整的......)它应该会更快.

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

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

于 2013-04-04T14:23:15.643 回答
2

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

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

附带说明一下,这种应用程序可能会受益于像sunspot这样的索引搜索引擎。

于 2013-04-04T13:19:16.537 回答