有没有办法在NSString stringWithFormat中指定参数位置/索引?

时间:2009-06-30 13:58:47

标签: objective-c cocoa

C#具有允许您以字符串格式说明符指定参数索引的语法,例如:

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

您可以多次使用参数,也可以省略使用时提供的参数。 Another question%[index]$[format]的形式提到了与C / C ++相同的格式,例如%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: %1$@", name, age];

所有这些调用在OS X中都能正常工作:

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

有没有办法在Objective-C / Cocoa中使用NSString实现这个目的?它对于本地化目的很有用。

3 个答案:

答案 0 :(得分:119)

NSString和CFString支持可重新排序/位置参数。

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

另请参阅Apple: String Resources

上的文档

答案 1 :(得分:1)

以下代码修复了此问题中指定的错误。这是一种解决方法,并对占位符进行重新编号以填补空白。

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

答案 2 :(得分:0)

经过更多研究后,看起来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上有这样的实现,那就太好了。