简体   繁体   English

在目标C中高效查找和替换文本

[英]Efficient find and replace text in Objective C

I'm currently trying to find an replace text strings as a user types text into a tool bar. 我目前正在尝试查找替换文本字符串,因为用户在工具栏中输入了文本。 I've done this so far by updating the text view with the ViewDidChange method and using stringByReplacingOccurrencesOfString as shown in the code below. 到目前为止,我已经通过使用ViewDidChange方法更新文本视图并使用stringByReplacingOccurrencesOfString来完成此操作,如下面的代码所示。

This works well when finding and replacing a few (10-20) strings however when doing this with 1,000 + potential replacements the system become extremely laggy - you can see the drop in performance in the simulator as you type. 这在查找和替换几个(10-20)字符串时效果很好,但是当用1,000多个可能的替换字符串进行替换时,系统变得非常缓慢-您可以在键入时看到模拟器的性能下降。

Does anyone know how to make this more efficient? 有谁知道如何提高效率? Is this because of a memory leak? 这是因为内存泄漏吗? One idea I've had is only to only use the replace string method on the last word in the string (ie only look at the last word the user has typed however that is just a guess at how this might speed things up, any other thoughts/ideas/hits would be greatly appreciated!! 我曾经有一个想法是只对字符串中的最后一个单词使用替换字符串方法(即仅查看用户键入的最后一个单词,但这只是一个猜测,可能会加快速度)的想法/想法/命中将不胜感激!

Another point to note, if replacing text with anything unicode based the system slows down even more, does anyone know of a specific way to fix this? 还要注意的另一点是,如果用基于unicode的系统替换文本会进一步降低速度,那么有人知道解决此问题的特定方法吗?

- (void)textViewDidChange:(UITextView *)textView
{
    if (textView != self.inputToolbar.contentView.textView) {
        return;
    }

    textView.text =  [textView.text stringByReplacingOccurrencesOfString:@"(?wi)\\bsmart\\b" withString: @"clever" options: NSRegularExpressionSearch range: NSMakeRange(0, [textView.text length]) ];
    textView.text =  [textView.text stringByReplacingOccurrencesOfString:@"(?wi)\\bfast\\b" withString: @"speedy" options: NSRegularExpressionSearch range: NSMakeRange(0, [textView.text length]) ];
    textView.text =  [textView.text stringByReplacingOccurrencesOfString:@"(?wi)\\bhappy\\b" withString: @"content" options: NSRegularExpressionSearch range: NSMakeRange(0, [textView.text length]) ];         
}

Currently, you're doing multiple passes along the string, one for each word-to-be-replaced. 当前, 您正在字符串中进行多次遍历,每个要替换的单词遍历一次。 You don't need this; 您不需要这个; simply go through the string once, replacing words on the fly, thereby saving memory allocations. 只需遍历字符串一次,即可动态替换单词,从而节省了内存分配。

You could also pre-compile one (huge) regular expression and store it in an instance variable in order to avoid frequent recompilation thereof. 您还可以预编译一个(巨大的)正则表达式并将其存储在实例变量中,以避免频繁地对其进行重新编译。

You could also move the options out of the non-capturing group and specify them as global flags (I don't know how good NSRegularExpression 's optimizer is at detecting and hoisting superfluously repeated flags, but regex optimizers are traditionally not very smart – if any.) 您也可以将选项从非捕获组中移出并将它们指定为全局标志(我不知道NSRegularExpression的优化器在检测和提升多余的重复标志方面有多好,但是regex优化器传统上不是很聪明–如果任何。)

// ivars
NSRegularExpression *regEx;
NSDictionary *replacementRules;

- (instancetype)init {
    ...
    replacementRules = @[
        @"smart": @"clever",
        @"fast":  @"speedy",
        @"happy": @"content"
    ];

    // Build regular expression
    NSMutableArray *patterns = [NSMutableArray arrayWithCapacity:
        replacementRules.count];

    for (NSString *str in replacementRules.allKeys) {
        [patterns addObject:[NSString stringWithFormat:@"\\b(%@)\\b", str]];
    }

    NSString *reStr = [patterns componentsJoinedByString:@"|"];

    regEx = [NSRegularExpression
        regularExpressionWithPattern:reStr
                             options:NSRegularExpressionUseUnicodeWordBoundaries | NSRegularExpressionCaseInsensitive
                               error:NULL];
    ...
}

- (void)textViewDidChange:(UITextView *)textView {
    ...

    // our new string
    NSMutableString *s = [NSMutableString new];
    NSUInteger __block lastPos = 0;

    [regEx enumerateMatchesInString:textView.text
                     options:kNilOptions
                       range:(NSRange){ 0, textView.text.length }
                  usingBlock:^(NSTextCheckingResult *result,
                                       NSMatchingFlags flags,
                                       BOOL *stop) {

        // Append the string from _before_ the match
        [s appendString:[textView.text substringWithRange:(NSRange){
             lastPos, result.range.location - lastPos
        }]];
        lastPos = result.range.location + result.range.length;

        // actually replace the string
        NSString *captured = [textView.text substringWithRange:result.range];
        [s appendString:replacementRules[captured]];
    }];

    // append rest of string, from after the last match
    [s appendString:[textView.text substringWithRange:(NSRange){
         lastPos, textView.text.length - lastPos
    }]];

    textView.text = s;
}

however when doing this with 1,000 + potential replacements the system become extremely laggy 但是,当使用1,000多个潜在替代品进行此操作时,系统将变得非常缓慢

That's not surprising. 这不足为奇。 Notice that -stringByReplacingOccurancesOfString: creates an entirely new copy of the target. 注意, -stringByReplacingOccurancesOfString:创建目标的全新副本。 Doing that 1000+ times even on a small string is going to take a little time. 即使在一个很小的字符串上执行1000次以上也将花费一些时间。 Alternatively, doing that even a few times on a string that's large enough to contain 1000+ substrings is going to take some time. 另外,即使对足以容纳1000+个子字符串的字符串进行几次操作也将花费一些时间。

The first step in any performance improvement effort should be to measure the actual performance you're getting now. 任何性能改进工作的第一步都应该是衡量您现在获得的实际性能。 You can use Instruments for that. 您可以使用乐器。 Run the same test a few times and get a baseline for the time needed to do whatever task you're doing. 几次运行相同的测试,并获得执行所需任务所需时间的基线。 Then start making changes and measure each one to see where you get a real improvement. 然后开始进行更改并评估每个步骤,以查看您真正获得了改进。

When you're ready to start making changes, I'd try switching to using a single mutable string and a method like -replaceOccurrencesOfString:withString:options:range: instead of your current approach. 当您准备开始进行更改时,我将尝试切换为使用单个可变字符串和-replaceOccurrencesOfString:withString:options:range:之类的方法,而不是当前的方法。 That should at least eliminate most of the copying you're doing now. 那至少应该消除您现在正在执行的大多数复制操作。

I can't think of a better application for regular expressions. 我想不出一个更好的正则表达式应用程序。

Please see: NSRegularExpression, and this article: 请参阅:NSRegularExpression和本文:

http://www.raywenderlich.com/30288/nsregularexpression-tutorial-and-cheat-sheet http://www.raywenderlich.com/30288/nsregularexpression-tutorial-and-cheat-sheet

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM