2009-06-30 17 views
71

C# tiene una sintaxis que le permite especificar el índice de argumento en un especificador de formato de cadena, por ejemplo .:¿Hay alguna forma de especificar la posición/índice del argumento en NSString stringWithFormat?

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

Puede utilizar argumentos más de una vez, y también omitir los argumentos que se proporcionan de ser utilizado. Another question menciona el mismo formato para C/C++ en forma de %[index]$[format], p. Ej. %1$i. No he podido obtener NSString a totalmente respecto a esta sintaxis, porque se comporta bien cuando omite los argumentos del formato. El siguiente no funciona como se esperaba (EXC_BAD_ACCESS porque intenta eliminar la referencia al parámetro age como NSObject *):

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

Los argumentos posicionales son respetados sólo si no hay argumentos que faltan en el formato (que parece ser un requisito impar):

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

Todas estas llamadas funciona correctamente en OS X:

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

¿hay una manera de accomplishin g esto usando NSString en Objective-C/Cocoa? Sería útil para fines de localización.

+0

Presente un informe de error (y avísenos el error #). –

Respuesta

110

NSString y CFString admiten argumentos reordenables/posicionales.

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

Asimismo, consulte la documentación en Apple: String Resources

+3

He actualizado la pregunta con algunas aclaraciones. Parece que Cocoa no respeta los argumentos omitidos del formato, que era un efecto secundario de la violación de acceso que estaba recibiendo. – Jason

+2

Respetar los argumentos omitidos no es posible debido a la forma en que funcionan los varargs en C. No hay una forma estándar de saber la cantidad de argumentos o su tamaño. El análisis de cadenas maneja esto al inferir la información de los especificadores de formato, lo que requiere que los especificadores estén realmente allí. –

+1

Entiendo cómo va_args funciona; sin embargo, esto parece funcionar como se esperaba: printf ("Edad:% 2 $ i", [nombre UTF8String], edad); He intentado otros printf con args reordenados/perdidos y todos dan el resultado esperado, mientras que NSString no lo hace. – Jason

0

Después de hacer más investigaciones, 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 había una implementación de este en NSString.

+1

'sprintf' no es parte de Cocoa, es parte de la biblioteca estándar C, y la implementación de eso es' stringWithFormat: '/' initWithFormat: '. –

+0

Aclarando mi comentario anterior: La versión de Cocoa es 'stringWithFormat:'/'initWithFormat:'. Es una implementación separada (actualmente, 'CFStringCreateWithFormat') de' sprintf' y amigos. –

+4

Supongo que hay poco uso para comentar el hecho de que inicializar msg con exactamente 512 bytes es tan seguro como realizar un selector aleatorio en un objeto aleatorio, pero de todos modos. Para cualquiera que no lo sepa: los buffers de tamaño fijo son algunas de las formas más fáciles de ser despedido. google: desbordamiento de búfer –

1

El código siguiente corrige el error especificado en este tema. Es una solución y renumera los marcadores de posición para llenar las lagunas.

+ (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; 
} 
Cuestiones relacionadas