regex Objective-C中的正则表达式:如何用动态模板替换匹配项?

cgyqldqp  于 2023-03-13  发布在  其他
关注(0)|答案(2)|浏览(70)

我的输入类似于"Hi {{username}}",即一个带有关键字的字符串,但是,输入相当小(大约10个关键字和1000个字符),而且我有一百万个可能的关键字存储在一个哈希表数据结构中,每个关键字都与它的替换相关联。
因此,出于明显的性能原因,我不想迭代关键字列表并尝试替换输入中的每个关键字,我更喜欢通过查找正则表达式模式"\{\{.+?\}\}"来只迭代一次输入字符。
在Java中,我使用Matcher.appendReplacementMatcher.appendTail方法来实现这一点,但是我找不到与NSRegularExpression类似的API。

private String replaceKeywords(String input)
{        
    Matcher m = Pattern.compile("\\{\\{(.+?)\\}\\}").matcher(input);
    StringBuffer sb = new StringBuffer();

    while (m.find())
    {
        String replacement = getReplacement(m.group(1));
        m.appendReplacement(sb, replacement);
    }

    m.appendTail(sb);
    return sb.toString();
}

我是被迫自己实现这样的API,还是我错过了什么?

dphi5xsq

dphi5xsq1#

您可以使用NSRegularExpression来实现这一点:

NSString *original = @"Hi {{username}} ... {{foo}}";
NSDictionary *replacementDict = @{@"username": @"Peter", @"foo": @"bar"};

NSString *pattern = @"\\{\\{(.+?)\\}\\}";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
                                       options:0
                                     error:NULL];

NSMutableString *replaced = [original mutableCopy];
__block NSInteger offset = 0;
[regex enumerateMatchesInString:original
            options:0
              range:NSMakeRange(0, original.length)
             usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
    NSRange range1 = [result rangeAtIndex:1]; // range of the matched subgroup
    NSString *key = [original substringWithRange:range1];
    NSString *value = replacementDict[key];
    if (value != nil) {
        NSRange range = [result range]; // range of the matched pattern
        // Update location according to previous modifications:
        range.location += offset;
        [replaced replaceCharactersInRange:range withString:value];
        offset += value.length - range.length; // Update offset
    }
}];
NSLog(@"%@", replaced);
// Output: Hi Peter ... bar

使用Swift 5.7中引入的Regex类型的Swift版本需要macOS 13或iOS 16以上版本):

import Foundation

let original = "Hi {{username}} ... {{foo}}"
let replacementDict = ["username" : "Peter", "foo" : "bar" ]

var replaced = ""
let regex = /\{\{(.+?)\}\}/
var pos = original.startIndex

// Look for next occurrence of {{key}}.
while let match = try? regex.firstMatch(in: original[pos...]) {
    // Append original text preceding the match.
    replaced += original[pos..<match.range.lowerBound]
    
    // Replace {{key}} by corresponding value and append to the output.
    let key = String(match.1)
    if let value = replacementDict[key] {
        replaced += value
    } else {
        replaced += match.0
    }

    // Continue search after the current match.
    pos = match.range.upperBound
}
// Append original text after the final match.
replaced += original[pos...]

print(replaced)
// Hi Peter ... bar
cbeh67ev

cbeh67ev2#

我不相信你能直接做你想做的事。
您可以考虑使用RegexKit Lite,特别是stringByReplacingOccurrencesOfRegex:usingBlock:方法,其中替换块保存来自上面while循环的逻辑,以查找并返回适当的替换。

相关问题