优化扫描大文本并匹配单词或短语列表

时间:2011-08-28 17:39:43

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

我正在开发一个带文章的应用程序(简单的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] )];
}

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

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

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

4 个答案:

答案 0 :(得分:3)

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

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

  2. 创建一些索引,哈希表或字典以使查找更有效。

  3. 请勿使用stringByReplacingOccurrencesOfString。每次调用它都会生成整个文本的副本,并且在autopool耗尽之前不会释放内存。 (有趣的是,你还没有遇到内存问题。)而是使用NSMutableString实例来附加每个单词(以及它们之间的字符),无论是原始文本还是装饰链接。

答案 1 :(得分: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

答案 2 :(得分:1)

由于字符串替换功能你的调用是命令N(它扫描替换n个单词)而你正在用m个词汇表术语,你有一个n ^ 2算法。

如果你可以一次性完成,那将是最佳的(在html中命令n - n个单词)。首先展示未被替换的文本的想法仍然是一个好的,除非它对于大型文档来说是不明显的。

如何对词汇单词的哈希集进行扫描,通过html单词扫描(跳过html标记),如果当前扫描的单词在哈希集中,则将其附加到目标缓冲区而不是扫描的单词。这样你就可以在内存中拥有2 X个html内容+ 1个词汇表哈希值。

答案 3 :(得分:1)

有两种方法。

  1. 哈希地图 - 如果你的短语的最大长度被限制为例如2,你可以迭代所有单词和双字母(2个单词)并在HashMap中检查它们 - 复杂性是liniar,因为哈希是恒定的时间在理想

  2. 自动机理论 您可以将简单的自动机组合为单一的自动机和更快的评估(即动态编程)。例如,我们将“John Smith”|“John Stuard”合并,我们得到John S(mith | tuard)它就是所谓的前缀优化(http://code.google.com/p/graph-expression/wiki/ RegexpOptimization)

  3. 此处可以找到更多advenced算法http://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_string_matching_algorithm

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