2009-06-30 10 views
71

C# ma składnię, która pozwala określić indeks argument w ciągu formatu specyfikacją, np .:Czy istnieje sposób określenia pozycji/indeksu argumentu w łańcuchu znaków NSString stringWithFormat?

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

Można użyć arguments więcej niż raz, a także pominąć argumentów, które są przewidziane zostały użyte. Another question wymienia to samo formatowanie C/C++ w postaci %[index]$[format], np. %1$i. Nie udało mi się uzyskać NSString do w pełni szanować tę składnię, ponieważ zachowuje się dobrze, gdy pomija argumenty z formatu. Poniższa nie działa zgodnie z oczekiwaniami (EXC_BAD_ACCESS ponieważ próbuje nieprawidłowego parametru age jako NSObject *):

int age = 23; 
NSString * name = @"Joe"; 
NSString * message = [NSString stringWithFormat:@"Age: %2$i", name, age]; 

pozycyjnych argumentów są przestrzegane tylko wtedy, gdy nie ma żadnych brakujących argumentów od formatu (co wydaje się być dziwne wymaganie):

NSString * message = [NSString stringWithFormat:@"Age: %2$i; Name: %[email protected]", name, age]; 

Wszystkie te połączenia działają prawidłowo w OS X:

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

Czy istnieje sposób accomplishin g to za pomocą NSString w Objective-C/Cocoa? Byłoby przydatne do celów lokalizacyjnych.

+0

Zgłoś błąd (i daj nam znać o błędzie #). –

Odpowiedz

110

Obsługa NSString i CFString argumenty reorderable/positional.

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

również zapoznać się z dokumentacją w Apple: String Resources

+3

Zaktualizowałem to pytanie, dodając kilka wyjaśnień. Wygląda na to, że Cocoa nie uwzględnia pominiętych argumentów z formatu, co było skutkiem ubocznym naruszenia zasad dostępu, które otrzymywałem. – Jason

+2

Poszanowanie pominiętych argumentów nie jest możliwe z powodu sposobu działania varargów w C. Nie ma standardowego sposobu na poznanie liczby argumentów lub ich wielkości. Parsowanie łańcuchów obsługuje to przez wnioskowanie informacji ze specyfikatorów formatu, co wymaga, aby specyfikatory faktycznie tam były. –

+1

Rozumiem, jak działa va_args; jednak wydaje się działać zgodnie z oczekiwaniami: printf ("Wiek:% 2 $ i", [nazwa UTF8String], wiek); Próbowałem innych printfów z zmienionymi/brakującymi argami i wszystkie dają oczekiwany wynik, podczas gdy NSString nie. – Jason

0

Po wykonaniu większej liczby badań okazało się, że Cocoa przestrzega składni pozycyjnej w printf. Dlatego alternatywny wzór byłby następujący:

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

Byłoby jednak miło, gdyby była implementacja tego na NSString.

+1

'sprintf' nie jest częścią kakao, jest częścią standardowej biblioteki C, a implementacja tego jest' stringWithFormat: '/' initWithFormat: '. –

+0

Wyjaśnienie mojego poprzedniego komentarza: Wersja kakao to 'stringWithFormat:'/'initWithFormat:'. Jest to osobna implementacja (obecnie 'CFStringCreateWithFormat') od wersji' sprintf' i przyjaciół. –

+4

Przypuszczam, że nie ma sensu komentować faktu, że inicjowanie msg z dokładnie 512 bajtami jest tak bezpieczne, jak wykonywanie losowego selektora na losowym obiekcie, ale w każdym razie. Dla każdego, kto nie jest świadomy: bufory o stałym rozmiarze to jedne z najprostszych sposobów na wystrzelenie. google: przepełnienie buforu –

1

Poniższy kod naprawia błąd określony w tej kwestii. Jest to obejście i zmienia numerację symboli zastępczych w celu wypełnienia luk.

+ (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; 
} 
Powiązane problemy