NSString stringWithFormat에서 인수 위치 / 인덱스를 지정하는 방법이 있습니까?


83

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는 매개 변수를 NSObject *로 역 참조하려고하기 때문에 ).age

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

답변:


126

NSString 및 CFString은 재정렬 가능 / 위치 인수를 지원합니다.

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

또한 Apple : String Resources 의 문서를 참조하십시오.


5
몇 가지 설명으로 질문을 업데이트했습니다. Cocoa는 형식에서 생략 된 인수를 존중하지 않는 것 같습니다. 이것은 제가받은 액세스 위반의 부작용이었습니다.
Jason

2
C의 varargs가 작동하는 방식 때문에 생략 된 인수를 존중하는 것은 불가능합니다. 인수의 수나 크기를 알 수있는 표준 방법은 없습니다. 문자열 구문 분석은 형식 지정자로부터 정보를 유추하여이를 처리하며, 지정자가 실제로 거기에 있어야합니다.
Jens Ayton

1
va_args가 어떻게 작동하는지 이해합니다. 그러나 이것은 예상대로 작동하는 것으로 보입니다. printf ( "Age : % 2 $ i", [name UTF8String], age); 재정렬 / 누락 된 인수를 사용하여 다른 printf를 시도했지만 모두 예상 된 출력을 제공하지만 NSString은 그렇지 않습니다.
Jason

7
내 결과를 반복하기 위해 stringWithFormat:모든 인수가 형식 문자열에 지정되어있는 한 위치 인수를 지원합니다.
Jason


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
sprintfCocoa의 일부가 아니며 C 표준 라이브러리의 일부이며 구현은 stringWithFormat:/ initWithFormat:입니다.
Peter Hosey

내 이전 의견을 명확히하기 : Cocoa 버전은 stringWithFormat:/ initWithFormat:입니다. 및 친구와 는 별도의 구현 (현재 CFStringCreateWithFormat)입니다 sprintf.
Peter Hosey

4
정확히 512 바이트로 msg를 초기화하는 것이 임의의 객체에 대해 임의 선택기를 수행하는 것만 큼 안전하다는 사실에 대해 언급 할 필요가 거의 없다고 생각합니다. 모르는 사람에게 : 고정 크기 버퍼는 가장 쉬운 방법 중 하나입니다. google : 버퍼 오버 플로우
George Penn.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.