Есть ли способ указать позицию/индекс аргумента в NSString stringWithFormat?
С# имеет синтаксис, который позволяет указать индекс аргумента в спецификаторе формата строки, например:
string message = string.Format("Hello, {0}. You are {1} years old. How does it feel to be {1}?", name, age);
Вы можете использовать аргументы более одного раза и также опускать аргументы, которые предоставляются от использования. Другой вопрос упоминает одно и то же форматирование для C/С++ в форме %[index]$[format]
, например. %1$i
. Я не смог заставить NSString полностью соблюдать этот синтаксис, потому что он хорошо себя ведет, когда опускает аргументы из формата. Следующее не работает должным образом (EXC_BAD_ACCESS, потому что он пытается разыменовать параметр age
как NSObject *):
int age = 23;
NSString * name = @"Joe";
NSString * message = [NSString stringWithFormat:@"Age: %2$i", name, age];
Позиционные аргументы учитываются только в том случае, если в формате нет отсутствующих аргументов (что представляется нечетным требованием):
NSString * message = [NSString stringWithFormat:@"Age: %2$i; Name: %[email protected]", name, age];
Все эти вызовы работают правильно в OS X:
printf("Age: %2$i", [name UTF8String], age);
printf("Age: %2$i %1$s", [name UTF8String], age);
Есть ли способ выполнить это, используя NSString в Objective-C/Cocoa? Это было бы полезно для целей локализации.
Ответы
Ответ 1
NSString и CFString поддерживают переопределяемые/позиционные аргументы.
NSString *string = [NSString stringWithFormat: @"Second arg: %[email protected], First arg %[email protected]", @"<1111>", @"<22222>"];
NSLog(@"String = %@", string);
Также см. документацию на Apple: String Resources
Ответ 2
Следующий код исправляет ошибку, указанную в этой проблеме. Это временное решение и позволяет заполнителям заполнить пробелы.
+ (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;
}
Ответ 3
После проведения большего исследования, он выглядит Cocoa в отношении позиционного синтаксиса в printf
. Поэтому альтернативный шаблон был бы следующим:
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];
Однако было бы неплохо, если бы была реализация этого на NSString.