2010-10-18 9 views
8

Tengo un proyecto de C# que tengo la intención de transferir a Objective-C. Por lo que entiendo sobre Obj-C, parece que hay una variedad confusa de opciones de Regex pero no puedo ver nada sobre una forma de reemplazarlo con la devolución de llamada.¿Hay un Objective-c regex replace con callback/C# MatchEvaluator equivalente?

Estoy buscando algo que sea equivalente al delegado C# MatchEvaluator o preg_replace_callback de PHP. Un ejemplo de lo que quiero hacer en C# es -

// change input so each word is followed a number showing how many letters it has 

string inputString = "Hello, how are you today ?"; 
Regex theRegex = new Regex(@"\w+"); 

string outputString = theRegex.Replace(inputString, delegate (Match thisMatch){ 
    return thisMatch.Value + thisMatch.Value.Length; 
}); 

// outputString is now 'Hello5, how3 are3 you3 today5 ?' 

¿Cómo podría hacer esto en Objective-C? En mi situación real, la Regex tiene aserciones de mirar adelante y mirar atrás, así que cualquier alternativa que implique buscar las cuerdas por adelantado y luego hacer una serie de reemplazos de cuerdas rectos no funcionará desafortunadamente.

Respuesta

7

Foundation tiene una clase NSRegularExpression (iOS4 y posterior), que puede serle útil. A partir de los documentos:

El método de emparejamiento fundamental para NSRegularExpression es un método iterador Bloquear que permite a los clientes suministrar un objeto de bloque que será invoca cada vez que el expresión regular coincide con una porción del blanco cuerda. Hay métodos de conveniencia adicionales para devolver todas las coincidencias como una matriz, el número total de coincidencias , la primera coincidencia, y el rango de la primera coincidencia.

Por ejemplo:

NSString *input = @"Hello, how are you today?"; 

// make a copy of the input string. we are going to edit this one as we iterate 
NSMutableString *output = [NSMutableString stringWithString:input]; 

NSError *error = NULL; 
NSRegularExpression *regex = [NSRegularExpression 
           regularExpressionWithPattern:@"\\w+" 
                options:NSRegularExpressionCaseInsensitive 
                 error:&error]; 

// keep track of how many additional characters we've added (1 per iteration) 
__block NSUInteger count = 0; 

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

    // Note that Blocks in Objective C are basically closures 
    // so they will keep a constant copy of variables that were in scope 
    // when the block was declared 
    // unless you prefix the variable with the __block qualifier 

    // match.range is a C struct 
    // match.range.location is the character offset of the match 
    // match.range.length is the length of the match   

    NSString *matchedword = [input substringWithRange:match.range]; 

    // the matched word with the length appended 
    NSString *new = [matchedword stringByAppendingFormat:@"%d", [matchedword length]]; 

    // every iteration, the output string is getting longer 
    // so we need to adjust the range that we are editing 
    NSRange newrange = NSMakeRange(match.range.location+count, match.range.length); 
    [output replaceCharactersInRange:newrange withString:new]; 

    count++; 
}]; 
NSLog(@"%@", output); //output: Hello5, how3 are3 you3 today5? 
3

he modificado el código del atshum para que sea un poco más flexible:

__block int prevEndPosition = 0; 
[regex enumerateMatchesInString:text 
         options:0 
          range:NSMakeRange(0, [text length]) 
        usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop) 
{ 
    NSRange r = {.location = prevEndPosition, .length = match.range.location - prevEndPosition}; 

    // Copy everything without modification between previous replacement and new one 
    [output appendString:[text substringWithRange:r]]; 
    // Append string to be replaced 
    [output appendString:@"REPLACED"]; 

    prevEndPosition = match.range.location + match.range.length; 
}]; 

// Finalize string end 
NSRange r = {.location = prevEndPosition, .length = [text length] - prevEndPosition}; 
[output appendString:[text substringWithRange:r]]; 

parece funcionar por ahora (probablemente necesita un poco más pruebas)