Как вы используете замену StringForResult в NSRegularExpression: inString: offset: template: - PullRequest
14 голосов
/ 03 июня 2011

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

Пример:

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

Out => "Это входная строка, в которой я хочу заменить ОДИН ДВА И ТРИ"

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

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

Ответы [ 6 ]

35 голосов
/ 28 декабря 2011

Вы можете использовать метод в цикле 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
7 голосов
/ 23 мая 2014

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

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;
    }
}];
2 голосов
/ 07 ноября 2011

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

- (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" и шаблон имеет вид "ОДИН" , "ДВА" или "ТРИ" .

1 голос
/ 27 февраля 2015

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

- (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
0 голосов
/ 09 июня 2015

Я искал что-то подобное, но мне не понравилось большинство ответов здесь, поэтому я написал что-то вдохновленное тем, как 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)"
0 голосов
/ 06 июня 2013

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...