Как вы используете замену NSRegularExpressionStringForResult: inString: offset: template:

У меня есть серия символов, которые я хочу сопоставить с регулярным выражением, и заменяю их на определенные строки в зависимости от того, что они есть.

Пример:

In = > "Это строка ввода, где я хочу заменить 1 2 и 3"

Out = > "Это строка ввода, где я хочу заменить ONE TWO и THREE"

В настоящее время я делаю это, разбивая строку, используя пробелы, в качестве разделителя, и разбору каждой строки отдельно, постепенно перестраивая строку. Я чувствую, что это уродливо, и у него нет воображения, и что-то медленное.

Согласно документации Apple, я должен сделать это, используя метод replacementStringForResult:inString:offset:template:. Однако я не могу понять, как правильно его использовать.

Ответы

Ответ 1

Вы можете использовать метод в цикле for in, используя matchesInString:options:range:, который возвращает массив совпадений как NSTextCheckingResult s:

NSError* error = NULL;
NSRegularExpression* regex = [NSRegularExpression 
                              regularExpressionWithPattern:@"\\b[1-3]\\b"
                              options:NSRegularExpressionCaseInsensitive
                              error:&error]; 

NSString* yourString = @"This is the input string where i want to replace 1 2 & 3";

NSMutableString* mutableString = [yourString mutableCopy];
NSInteger offset = 0; // keeps track of range changes in the string
                      // due to replacements.
for (NSTextCheckingResult* result in [regex matchesInString:yourString 
                                                    options:0 
                                                      range:NSMakeRange(0, [yourString length])]) {

    NSRange resultRange = [result range];   
    resultRange.location += offset; // resultRange.location is updated 
                                    // based on the offset updated below

    // implement your own replace functionality using
    // replacementStringForResult:inString:offset:template:
    // note that in the template $0 is replaced by the match
    NSString* match = [regex replacementStringForResult:result 
                                               inString:mutableString 
                                                 offset:offset 
                                               template:@"$0"];
    NSString* replacement;
    if ([match isEqualToString:@"1"]) {
        replacement = @"ONE";
    } else if ([match isEqualToString:@"2"]) {
        replacement = @"TWO";
    } else if ([match isEqualToString:@"3"]) {
        replacement = @"THREE";
    }

    // make the replacement
    [mutableString replaceCharactersInRange:resultRange withString:replacement];

    // update the offset based on the replacement
    offset += ([replacement length] - resultRange.length);
}

NSLog(@"mutableString: %@", mutableString); // mutableString: This is the input string where i want to replace ONE TWO & THREE

Ответ 2

Ответ Dano отлично работает и, следуя идее Педро в комментариях, я завернул код в категорию, которая берет блок, который выполняет замену. Это очень удобно использовать.

NSRegularExpression + Replacement.h

@interface NSRegularExpression (Replacement)

- (NSString *)stringByReplacingMatchesInString:(NSString *)string
                                       options:(NSMatchingOptions)options
                                         range:(NSRange)range
                                      template:(NSString *)templ
                       withMatchTransformation: (NSString* (^) (NSString* element))transformation;

@end

NSRegularExpression + Replacement.m

@implementation NSRegularExpression (Replacement)

- (NSString *)stringByReplacingMatchesInString:(NSString *)string
                                       options:(NSMatchingOptions)options
                                         range:(NSRange)range
                                      template:(NSString *)templ
                       withMatchTransformation: (NSString* (^) (NSString* element))transformation
{
    NSMutableString* mutableString = [string mutableCopy];
    NSInteger offset = 0; // keeps track of range changes in the string due to replacements.
    for (NSTextCheckingResult* result in [self matchesInString:string
                                                        options:0
                                                          range:range])
    {
        NSRange resultRange = [result range];
        resultRange.location += offset; // resultRange.location is updated based on the offset updated below

        // implement your own replace functionality using
        // replacementStringForResult:inString:offset:template:
        // note that in the template $0 is replaced by the match
        NSString* match = [self replacementStringForResult:result
                                                   inString:mutableString
                                                     offset:offset
                                                   template:templ];

        // get the replacement from the provided block
        NSString *replacement = transformation(match);

        // make the replacement
        [mutableString replaceCharactersInRange:resultRange withString:replacement];

        // update the offset based on the replacement
        offset += ([replacement length] - resultRange.length);
    }
    return mutableString;
}

@end

И вот как вы используете его для решения начального вопроса:

NSString* yourString = @"This is the input string where i want to replace 1 2 & 3";
NSError* error = nil;
NSRegularExpression* regex = [NSRegularExpression
                              regularExpressionWithPattern:@"\\b[1-3]\\b"
                              options:0
                              error:&error];

return [regex stringByReplacingMatchesInString:yourString options:0 range:NSMakeRange(0, [yourString length]) template:@"$0" withMatchTransformation:^NSString *(NSString *match) {
    NSString* replacement;
    if ([match isEqualToString:@"1"]) {
        replacement = @"ONE";
    } else if ([match isEqualToString:@"2"]) {
        replacement = @"TWO";
    } else if ([match isEqualToString:@"3"]) {
        replacement = @"THREE";
    } else {
        return replacement;
    }
}];

Ответ 3

Вы должны использовать

- (NSString *)stringByReplacingMatchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)template

или

- (NSUInteger)replaceMatchesInString:(NSMutableString *)string options:(NSMatchingOptions)options range:(NSRange)range withTemplate:(NSString *)template

со строкой: "Это строка ввода, где я хочу заменить 1 2 и 3", а шаблон - "ONE", "TWO" или "THREE".

Ответ 4

Мне понадобилось более общее решение для одной и той же проблемы, поэтому я уточнил Dano ответ на такой метод, используя пример использования, описанный ниже:

- (NSMutableString *)replaceSubstringsInString:(NSString*)string
                                    usingRegex:(NSString*)searchRegex
                              withReplacements:(NSDictionary*)replacements {

    NSMutableString *newString = [string mutableCopy];
    __block NSInteger offset = 0;

    NSError *error = NULL;

    NSRegularExpression *regex = [NSRegularExpression
                                  regularExpressionWithPattern:searchRegex
                                  options:0
                                  error:&error];

    [regex enumerateMatchesInString:string
                            options:0
                              range:NSMakeRange(0, [string length])
                         usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){

                             NSRange resultRange = [match range];
                             resultRange.location += offset;

                             NSString *substring = [string substringWithRange:match.range];

                             __block NSString* replacement;

                             [replacements enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {

                                 if ([key isEqualToString:substring]) {
                                     replacement = obj;
                                 }

                             }];

                             [newString replaceCharactersInRange:resultRange withString:replacement];

                             offset += ([replacement length] - resultRange.length);
                         }];

    return newString;
}

Применение:

NSString *string = @"This is the input string where i want to replace 1 2 & 3";
NSString *searchRegex = @"\\b[1-3]\\b";
NSDictionary *replacements = @{@"1":@"ONE",@"2":@"TWO",@"3":@"THREE"};

NSMutableString *result = [self replaceSubstringsInString:string
                                               usingRegex:searchRegex
                                         withReplacements:replacements];

Объяснение: Вам просто нужно пройти в string для поиска соответствующих подстрок, а также желаемого шаблона regexSearch и словаря replacements , содержащего пары строк с key, являющимися подстрокой для замены и object с нужной заменой.

Вывод:

 //   This is the input string where i want to replace ONE TWO & THREE

Ответ 5

Я искал что-то подобное, но мне не понравилось большинство ответов здесь, поэтому я написал что-то, вдохновленное тем, что PHP выполняет замену строки:

@implementation NSString (preg_replace)

- (instancetype)stringByReplacingMatchesFromRegularExpression:(NSRegularExpression *)regularExpression replacementBlock:(NSString * (^)(NSArray *matches))replacementBlock
{
    NSMutableString *finalString = self.mutableCopy;
    NSUInteger offset = 0;

    for (NSTextCheckingResult *match in [regularExpression matchesInString:self options:0 range:NSMakeRange(0, self.length)]) {
        NSMutableArray *matches = [NSMutableArray array];
        for (NSUInteger index = 0; index < match.numberOfRanges; ++index) {
            [matches addObject:[self substringWithRange:[match rangeAtIndex:index]]];
        }
        NSString *replacementString = replacementBlock(matches.copy);

        [finalString replaceCharactersInRange:NSMakeRange(match.range.location + offset, match.range.length) withString:replacementString];
        offset += replacementString.length - match.range.length;
    }

    return finalString;
}

@end

Чтобы использовать его:

NSRegularExpression *expression = [[NSRegularExpression alloc] initWithPattern:@"[0-9A-F]{2}" options:0 error:nil];
NSString *string = @"AB-DE-EF";
NSString *result = [string stringByReplacingMatchesFromRegularExpression:expression replacementBlock:^NSString *(NSArray *matches) {
    return [NSString stringWithFormat:@"(%@)", [matches[0] lowercaseString]];
}];
NSLog(@"Result = %@", result); // "(ab)-(de)-(ef)"

Ответ 6

Я рекомендую это, намного короче и использует небольшую память: Другое решение