2010-01-20 9 views

Odpowiedz

23

Brak wbudowanej funkcji do cofania C.

można oszukać trochę z NSPropertyListSerialization od „starego stylu tekstu” plist obsługuje C ucieczce poprzez \Uxxxx:

NSString* input = @"ab\"cA\"BC\\u2345\\u0123"; 

// will cause trouble if you have "abc\\\\uvw" 
NSString* esc1 = [input stringByReplacingOccurrencesOfString:@"\\u" withString:@"\\U"]; 
NSString* esc2 = [esc1 stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; 
NSString* quoted = [[@"\"" stringByAppendingString:esc2] stringByAppendingString:@"\""]; 
NSData* data = [quoted dataUsingEncoding:NSUTF8StringEncoding]; 
NSString* unesc = [NSPropertyListSerialization propertyListFromData:data 
        mutabilityOption:NSPropertyListImmutable format:NULL 
        errorDescription:NULL]; 
assert([unesc isKindOfClass:[NSString class]]); 
NSLog(@"Output = %@", unesc); 

jednak pamiętać, że to nie jest bardzo skuteczny. Znacznie lepiej, jeśli napiszesz własny parser. (BTW to dekoduje ciągi JSON? Jeśli tak, możesz użyć the existing JSON parsers.)

+0

"Nie ma wbudowaną funkcję to zrobić" to, co starałem się dowiedzieć. Skończyłem przetaczać własne, po prostu chciałem sprawdzić, czy nie wymyśliłem ponownie koła. Istniejące parsery JSON nie są wystarczająco bliskie dostatecznie na źle sformułowanym wyjściu JSON, które czasami wysyłane są przez podejrzane strony internetowe. – corydoras

+0

+1 dang to sprytnie –

11

Oto, co napisałem. Mam nadzieję, że to pomoże niektórym ludziom.

+ (NSString*) unescapeUnicodeString:(NSString*)string 
{ 
// unescape quotes and backwards slash 
NSString* unescapedString = [string stringByReplacingOccurrencesOfString:@"\\\"" withString:@"\""]; 
unescapedString = [unescapedString stringByReplacingOccurrencesOfString:@"\\\\" withString:@"\\"]; 

// tokenize based on unicode escape char 
NSMutableString* tokenizedString = [NSMutableString string]; 
NSScanner* scanner = [NSScanner scannerWithString:unescapedString]; 
while ([scanner isAtEnd] == NO) 
{ 
    // read up to the first unicode marker 
    // if a string has been scanned, it's a token 
    // and should be appended to the tokenized string 
    NSString* token = @""; 
    [scanner scanUpToString:@"\\u" intoString:&token]; 
    if (token != nil && token.length > 0) 
    { 
     [tokenizedString appendString:token]; 
     continue; 
    } 

    // skip two characters to get past the marker 
    // check if the range of unicode characters is 
    // beyond the end of the string (could be malformed) 
    // and if it is, move the scanner to the end 
    // and skip this token 
    NSUInteger location = [scanner scanLocation]; 
    NSInteger extra = scanner.string.length - location - 4 - 2; 
    if (extra < 0) 
    { 
     NSRange range = {location, -extra}; 
     [tokenizedString appendString:[scanner.string substringWithRange:range]]; 
     [scanner setScanLocation:location - extra]; 
     continue; 
    } 

    // move the location pas the unicode marker 
    // then read in the next 4 characters 
    location += 2; 
    NSRange range = {location, 4}; 
    token = [scanner.string substringWithRange:range]; 
    unichar codeValue = (unichar) strtol([token UTF8String], NULL, 16); 
    [tokenizedString appendString:[NSString stringWithFormat:@"%C", codeValue]]; 

    // move the scanner past the 4 characters 
    // then keep scanning 
    location += 4; 
    [scanner setScanLocation:location]; 
} 

// done 
return tokenizedString; 
} 

+ (NSString*) escapeUnicodeString:(NSString*)string 
{ 
// lastly escaped quotes and back slash 
// note that the backslash has to be escaped before the quote 
// otherwise it will end up with an extra backslash 
NSString* escapedString = [string stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]; 
escapedString = [escapedString stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]; 

// convert to encoded unicode 
// do this by getting the data for the string 
// in UTF16 little endian (for network byte order) 
NSData* data = [escapedString dataUsingEncoding:NSUTF16LittleEndianStringEncoding allowLossyConversion:YES]; 
size_t bytesRead = 0; 
const char* bytes = data.bytes; 
NSMutableString* encodedString = [NSMutableString string]; 

// loop through the byte array 
// read two bytes at a time, if the bytes 
// are above a certain value they are unicode 
// otherwise the bytes are ASCII characters 
// the %C format will write the character value of bytes 
while (bytesRead < data.length) 
{ 
    uint16_t code = *((uint16_t*) &bytes[bytesRead]); 
    if (code > 0x007E) 
    { 
     [encodedString appendFormat:@"\\u%04X", code]; 
    } 
    else 
    { 
     [encodedString appendFormat:@"%C", code]; 
    } 
    bytesRead += sizeof(uint16_t); 
} 

// done 
return encodedString; 
} 
+0

to musi być legalne, by zabić programistę po stronie serwera, tylko po to, aby zmusić mnie do skorzystania z tego rozwiązania. @Christoph nice działający kod przy okazji. Twoje zdrowie! –

87

To prawda, że ​​ Cocoa nie oferuje rozwiązanie jeszcze Rdzeń Fundacja robi: CFStringTransform.

CFStringTransform Mieszka w zakurzonym, odległym zakątku Mac OS (i iOS), więc jest to mały klejnot. Jest to front end do silnika przekształceń strun Apple ICU compatible. Może wykonywać prawdziwą magię jak transliteracji między grecki i łaciński (lub o wszelkich znanych skryptów), ale mogą być również wykorzystywane do wykonywania zadań przyziemne jak unescaping ciągów z serwera brzydko:

NSString *input = @"\\u5404\\u500b\\u90fd"; 
NSString *convertedString = [input mutableCopy]; 

CFStringRef transform = CFSTR("Any-Hex/Java"); 
CFStringTransform((__bridge CFMutableStringRef)convertedString, NULL, transform, YES); 

NSLog(@"convertedString: %@", convertedString); 

// prints: 各個都, tada! 

Jak powiedziałem, CFStringTransform jest naprawdę potężny. Obsługuje wiele predefiniowanych transformacji, takich jak odwzorowywanie przypadków, normalizacje lub konwersja nazw znaków Unicode. Możesz nawet zaprojektować własne transformacje.

Nie mam pojęcia, dlaczego Apple nie udostępnia go z kakao.

Edycja 2015:

OS X 10.11 i iOS 9 dodaj następującą metodę założenia:

- (nullable NSString *)stringByApplyingTransform:(NSString *)transform reverse:(BOOL)reverse; 

więc przykład z góry staje ...

NSString *input = @"\\u5404\\u500b\\u90fd"; 
NSString *convertedString = [input stringByApplyingTransform:@"Any-Hex/Java" 
                reverse:YES]; 

NSLog(@"convertedString: %@", convertedString); 

Dzięki @nschmidt dla heads up.

+0

To jest świetny kawałek funkcjonalności firmy Apple i wykracza daleko poza tego rodzaju transformację. – Jessedc

+0

Powiedzmy, że otrzymuję ciąg znaków, jak convertString ze źródła, którego nie mogę zmienić. Czy możesz mi powiedzieć, w jaki sposób mogę odwrócić proces, aby odzyskać oryginalny ciąg? –

+1

Jak wybrać CFSTR? – shiami

2

prosty kod:

const char *cString = [unicodeStr cStringUsingEncoding:NSUTF8StringEncoding]; 
NSString *resultStr = [NSString stringWithCString:cString encoding:NSNonLossyASCIIStringEncoding]; 

od: https://stackoverflow.com/a/7861345

+0

Witam wszystkich, mam do czynienia z dziwnym problemem, nie wiem, dlaczego nie działa z sugestiami podanymi powyżej, Czy ktoś może sparsować ten ciąg dla mnie? @ "ElbowWristHand_DeQuervian \ U00e2 \ U0080 \ U0099s Tenosynovitis"; W rzeczywistości jest to "ElbowWristHand_DeQuervian's" i próbowałem wszystkich wyżej wymienionych metod, ale nadal nie działa, Proszę zasugerować. Dzięki – york