C # ma składnię, która umożliwia określenie indeksu argumentu w specyfikatorze formatu ciągu, np .:
string message = string.Format("Hello, {0}. You are {1} years old. How does it feel to be {1}?", name, age);
Możesz użyć argumentów więcej niż raz, a także pominąć argumenty, które są używane. Kolejne pytanie dotyczy tego samego formatowania dla C / C ++ w postaci %[index]$[format]
np %1$i
. Nie udało mi się sprawić, by NSString w pełni przestrzegał tej składni, ponieważ zachowuje się dobrze, gdy pomija argumenty z formatu. Poniższe nie działa zgodnie z oczekiwaniami (EXC_BAD_ACCESS, ponieważ próbuje wyodrębnić age
parametr jako obiekt NSObject *):
int age = 23;
NSString * name = @"Joe";
NSString * message = [NSString stringWithFormat:@"Age: %2$i", name, age];
Argumenty pozycyjne są przestrzegane tylko wtedy, gdy nie ma brakujących argumentów z formatu (co wydaje się dziwnym wymaganiem):
NSString * message = [NSString stringWithFormat:@"Age: %2$i; Name: %1$@", name, age];
Wszystkie te wywołania działają poprawnie w systemie OS X:
printf("Age: %2$i", [name UTF8String], age);
printf("Age: %2$i %1$s", [name UTF8String], age);
Czy istnieje sposób na osiągnięcie tego za pomocą NSString w Objective-C / Cocoa? Byłoby to przydatne do celów lokalizacyjnych.
źródło
Odpowiedzi:
NSString i CFString obsługują argumenty z możliwością zmiany kolejności / pozycjonowania.
NSString *string = [NSString stringWithFormat: @"Second arg: %2$@, First arg %1$@", @"<1111>", @"<22222>"]; NSLog(@"String = %@", string);
Zobacz także dokumentację w Apple: String Resources
źródło
stringWithFormat:
obsługuje argumenty pozycyjne, o ile wszystkie argumenty są określone w ciągu formatu.Poniższy kod naprawia błąd określony w tym problemie. Jest to obejście i ponownie numeruje symbole zastępcze, aby wypełnić luki.
+ (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; }
źródło
Po przeprowadzeniu dalszych badań wydaje się, że Cocoa przestrzega składni pozycyjnej w
printf
. Dlatego alternatywnym wzorem byłoby: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];
Jednak byłoby miło, gdyby była implementacja tego w NSString.
źródło
sprintf
nie jest częścią Cocoa, jest częścią standardowej biblioteki C, a implementacją tego jeststringWithFormat:
/initWithFormat:
.stringWithFormat:
/initWithFormat:
. To osobna implementacja (obecnieCFStringCreateWithFormat
) od implementacjisprintf
i znajomych.