有没有一种方法可以在NSString stringWithFormat中指定参数位置/索引?


82

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

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

您可以多次使用参数,也可以省略使用时提供的参数。另一个问题提到了C / 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: %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来实现?这对本地化很有用。


提交错误报告(并告知我们错误号)。
Dirk Theisen

Answers:


125

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

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

另外,请参阅Apple上的文档:字符串资源


5
我对这个问题做了一些澄清。看来Cocoa不尊重格式中省略的参数,这是我收到的访问冲突的副作用。
杰森

2
由于varargs在C语言中的工作方式,因此无法尊重省略的参数。没有标准的方法可以知道参数的数量或其大小。字符串解析通过从格式说明符推断信息来处理此问题,这要求说明符实际上在那儿。
詹斯·艾顿

1
我了解va_args的工作原理;但是,这似乎可以正常工作:printf(“ Age:%2 $ i”,[name UTF8String],age); 我已经尝试了其他带有重新排序/缺少args的printf,它们都提供了预期的输出,而NSString没有。
杰森

7
仅重申一下我的发现,然后:stringWithFormat:只要在格式字符串中指定了所有参数,就支持位置参数。
杰森


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

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上实现了这一点,那就太好了。


1
sprintf不是Cocoa的一部分,而是C标准库的一部分,并且其实现是stringWithFormat:/ initWithFormat:
Peter Hosey,

澄清我之前的评论:可可版本为stringWithFormat:/ initWithFormat:。这CFStringCreateWithFormat是与sprintf和朋友分开的实现(当前为)。
Peter Hosey,

4
我想几乎没有用评论这样一个事实,即以512个字节为单位初始化msg与对随机对象执行随机选择器一样安全,但是无论如何。对于任何不知道的人:固定大小的缓冲区是触发的最简单方法之一。Google:缓冲区溢出
乔治·佩恩。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.