【问题标题】:Optimizing scanning large text and matching against list of words or phrases优化扫描大文本并匹配单词或短语列表
【发布时间】:2011-08-28 17:39:43
【问题描述】:

我正在开发一个应用程序,它接收一篇文章(简单的 HTML 页面)和一个词汇术语列表(每个都可以是一个单词、一个短语,甚至是一个句子),并为它的每个术语创建一个链接发现。问题是对于具有更多术语的较大文本需要很长时间。目前我们正在处理这个问题,首先显示未标记的文本,在后台处理链接,最后在处理完成时重新加载 Web 视图。不过,这可能需要一段时间,我们的一些用户对此并不满意。

现在,该应用程序在条款上使用了一个简单的循环,在 HTML 中进行替换。基本上:

for (int i=0; i<terms.count; i++){
    NSString *term = [terms objectAtIndex:i];
    NSString *replaceString = [NSString stringWithFormat:@"<a href="myUrl:\\%d>%@</a>", i, term];
    htmlString = [htmlString stringByReplacingOccurrencesOfString:term 
                                                       withString:replaceString 
                                                          options:NSCaseInsensitiveSearch 
                                                            range:NSMakeRange(0, [htmlString length] )];
}

但是,我们正在处理多种语言,因此每个学期不只有一个替换,而是二十个!那是因为我们必须在每个学期的开头(西班牙语中的倒置问号)和结尾处理标点符号。我们必须用适当的超链接替换"term""term.""term?"

我可以使用更有效的方法来标记此 HTML 吗?

我需要保留原始术语的索引,以便以后用户点击链接时可以检索到。

【问题讨论】:

  • 我在写这篇文章的时候并没有意识到链接需要包含该术语的原始索引。我不能将术语本身用作链接 url。
  • 之所以不能用这个词是因为我需要处理外文和字符,而UIWebView似乎不能正确处理带有unicode的链接。

标签: objective-c ios nsstring full-text-search replace


【解决方案1】:

您可以按如下方式处理文本:

  1. 不要循环遍历词汇表,而是将文本拆分为单词并在词汇表中查找每个单词。

  2. 创建一些索引、哈希表或字典以提高查找效率。

  3. 不要使用stringByReplacingOccurrencesOfString。每次调用它都会复制整个文本,并且在自动池耗尽之前不会释放内存。 (有趣的是,您还没有遇到内存问题。)而是使用NSMutableString 实例来附加每个单词(以及它们之间的字符),就像它在原始文本中一样,也可以作为链接装饰。

【讨论】:

  • 最后,这是最简单的,并且与亚当的正则表达式解决方案一样快,尽管它有很多循环。谢谢!
【解决方案2】:

你现在正在做的是:

for each vocabulary word 'term'
    search the HTML text for instances of term
        replace each instance of term with an appropriate hyperlink

如果您有一个大文本,那么每次搜索需要更长的时间。此外,每次进行替换时,都必须创建一个包含要替换的文本副本的新字符串,因为stringByReplacingOccurrencesOfString:withString:options:range: 返回一个新字符串而不是修改现有字符串。将其乘以 N 个替换。

更好的选择是对字符串进行一次遍历,一次搜索所有术语,并在可变字符串中构建结果输出字符串以避免Shlemiel the Painter-like runtime

例如,您可以像这样使用regular expressions

// Create a regular expression that is an alternation of all of the vocabulary
// words.  You only need to create this once at startup.
NSMutableString *pattern = [[[NSMutableString alloc] init] autorelease];
[pattern appendString:@"\\b("];

BOOL isFirstTerm = YES;
for (NSString *term in vocabularyList)
{
    if (!isFirstTerm)
    {
        [pattern appendString:@"|"];
        isFirstTerm = NO;
    }
    [pattern appendString:term];
}
[pattern appendString:@")\\b"];

// Create regular expression object
NSError *error = NULL;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];

// Replace vocabulary matches with a hyperlink
NSMutableString *htmlCopy = [[htmlString mutableCopy] autorelease];
[regex replaceMatchesInString:htmlCopy
                      options:0
                        range:NSMakeRange(0, [htmlString length])
                 withTemplate:@"<a href=\"vocab.html?word=\\1\">\\1</a>"];
// Now use htmlCopy

【讨论】:

  • 我试图实现这个,它看起来确实更快。但是,我需要 url 包含术语的索引,而不是术语本身。我已经更新了我的问题。
【解决方案3】:

由于您调用的字符串替换函数是 Order N(它扫描替换 n 个单词)并且您正在为 m 个词汇术语执行此操作,因此您有一个 n^2 算法。

如果您可以一次性完成,那将是最佳选择(在 html 中排序 n - n 个单词)。首先呈现未替换文本的想法仍然是一个不错的想法,除非即使对于大型文档也不会引起注意。

词汇单词的哈希集怎么样,通过(跳过 html 标记)扫描 html 单词,如果当前扫描的单词在哈希集中,则将其附加到目标缓冲区而不是扫描的单词。这使您最多可以在内存中拥有 2 X html 内容 + 1 个词汇散列。

【讨论】:

    【解决方案4】:

    有两种方法。

    1. Hash Maps - 如果你的短语的最大长度被限制为例如两个,你可以遍历所有单词和二元组(2 个单词)并在 HashMap 中检查它们 - 复杂性是线性的,因为 Hash 是常数时间理想中

    2. 自动机理论 您可以将简单的自动机组合起来,将字符串匹配为单个并更快地评估(即动态编程)。例如,我们有“John Smith”|“John Stuard”合并它们,我们得到 John S(mith|tuard) 这就是所谓的前缀优化(http://code.google.com/p/graph-expression/wiki/正则表达式优化)

    更多高级算法可以在这里找到http://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_string_matching_algorithm

    我更喜欢这种方法,因为它没有短语长度的限制,并且它允许组合复杂的正则表达式。


    【讨论】:

      猜你喜欢
      • 2021-05-11
      • 1970-01-01
      • 2019-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-04
      • 2015-08-28
      相关资源
      最近更新 更多