¿Hay alguna forma de especificar posición/índice del argumento en NSString stringWithFormat?


C # tiene una sintaxis que le permite especificar el argumento index en un especificador de formato de cadena, por ejemplo:

string message = string.Format("Hello, {0}. You are {1} years old. How does it feel to be {1}?", name, age);

Puede usar argumentos más de una vez y también omitir los argumentos que se proporcionan. Otra pregunta menciona el mismo formato para C/C++ en la forma de %[index]$[format], por ejemplo, %1$i. No he podido obtener NSString para completamente respetar esta sintaxis, porque se comporta bien cuando omite argumentos del formato. Los siguientes no funciona como se esperaba (EXC_BAD_ACCESS porque intenta desreferenciar el parámetro age como un NSObject*):

int age = 23;
NSString * name = @"Joe";
NSString * message = [NSString stringWithFormat:@"Age: %2$i", name, age];

Los argumentos posicionales se respetan solo si no faltan argumentos del formato (lo que parece ser un requisito impar):

NSString * message = [NSString stringWithFormat:@"Age: %2$i; Name: %1$@", name, age];

Todas estas llamadas funcionan correctamente en OS X:

printf("Age: %2$i", [name UTF8String], age);
printf("Age: %2$i %1$s", [name UTF8String], age);

¿Hay alguna forma de lograr esto usando NSString en Objective-C / Cocoa? Sería útil para fines de localización.

Author: Community, 2009-06-30

3 answers

NSString y CFString soportan argumentos reordenables/posicionales.

NSString *string = [NSString stringWithFormat: @"Second arg: %2$@, First arg %1$@", @"<1111>", @"<22222>"];
NSLog(@"String = %@", string);

También, vea la documentación en Apple: String Resources

 116
Author: Jim Correia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-27 12:37:16

El siguiente código corrige el error especificado en este problema. Es una solución alternativa y renumera los marcadores de posición para llenar los vacíos.

+ (id)stringWithFormat:(NSString *)format arguments:(NSArray*) arguments 
{
    NSMutableArray *filteredArguments = [[NSMutableArray alloc] initWithCapacity:arguments.count];
    NSMutableString *correctedFormat = [[NSMutableString alloc ] initWithString:format];
    NSString *placeHolderFormat = @"%%%d$";

    int actualPlaceholderIndex = 1;

    for (int i = 1; i <= arguments.count; ++i) {
        NSString *placeHolder = [[NSString alloc] initWithFormat:placeHolderFormat, i];
        if ([format rangeOfString:placeHolder].location != NSNotFound) {
            [filteredArguments addObject:[arguments objectAtIndex:i - 1]];

            if (actualPlaceholderIndex != i) {
                NSString *replacementPlaceHolder = [[NSString alloc] initWithFormat:placeHolderFormat, actualPlaceholderIndex];
                [correctedFormat replaceAllOccurrencesOfString:placeHolder withString:replacementPlaceHolder];    
                [replacementPlaceHolder release];
            }
            actualPlaceholderIndex++;
        }
        [placeHolder release];
    }

    if (filteredArguments.count == 0) {
        //No numbered arguments found: just copy the original arguments. Mixing of unnumbered and numbered arguments is not supported.
        [filteredArguments setArray:arguments];
    }

    NSString* result;
    if (filteredArguments.count == 0) {
        //Still no arguments: don't use initWithFormat in this case because it will crash: just return the format string
        result = [NSString stringWithString:format];
    } else {
        char *argList = (char *)malloc(sizeof(NSString *) * [filteredArguments count]);
        [filteredArguments getObjects:(id *)argList];
        result = [[[NSString alloc] initWithFormat:correctedFormat arguments:argList] autorelease];
        free(argList);    
    }

    [filteredArguments release];
    [correctedFormat release];

    return result;
}
 1
Author: Werner Altewischer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-25 17:25:50

Después de hacer más investigación, parece que Cocoa respeta la sintaxis posicional en printf. Por lo tanto, un patrón alternativo sería:

char msg[512] = {0};
NSString * format = @"Age %2$i, Name: %1$s"; // loaded from resource in practice
sprintf(msg, [format UTF8String], [name UTF8String], age);
NSString * message = [NSString stringWithCString:msg encoding:NSUTF8StringEncoding];

Sin embargo, sería bueno si hubiera una implementación de esto en NSString.

 0
Author: Jason,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-01 22:47:30