2010-01-20 7 views

Antwort

23

Es gibt keine integrierte Funktion, um C & scaping zu machen.

Sie können ein wenig betrügen mit NSPropertyListSerialization seit einem „alten Textstil“ plist unterstützt C über \Uxxxx Flucht:

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

aber daran, dass dies nicht sehr effizient ist. Es ist viel besser, wenn Sie Ihren eigenen Parser schreiben. (BTW dekodieren Sie JSON-Strings? Wenn ja, könnten Sie the existing JSON parsers verwenden.)

+0

"Es gibt keine integrierte Funktion, es zu tun" ist das, was ich versuche, es herauszufinden. Ich rollte meine eigene, wollte nur überprüfen, dass ich das Rad nicht neu erfunden habe. Die vorhandenen JSON-Parser sind bei fehlerhafter JSON-Ausgabe, die manchmal von zwielichtigen Websites gesendet werden, nicht annähernd fehlerverzeihend. – corydoras

+0

+1 dingt das ist klug –

11

Hier ist, was ich am Ende geschrieben habe. Hoffentlich wird dies einigen Leuten helfen.

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

es legal sein muss serverseitige Entwickler zu töten, nur um mich zu zwingen, diese Lösung zu verwenden. @Christoph Netter Arbeitscode übrigens. Prost! –

87

Es ist richtig, dass Cocoa keine Lösung , noch Core Foundation tut anbietet: CFStringTransform.

CFStringTransform lebt in einer staubigen, abgelegenen Ecke von Mac OS (und iOS) und so ist es ein wenig bekannt Juwel. Es ist das Frontend zu Apples ICU compatible String Transformation Engine. Es kann zwischen griechischen und lateinischen (oder zu bekannter Skripte) echte Magie wie Transkriptionen durchführt, aber es kann auch profane Aufgaben zu tun wie URL-Kodierung Strings von einem crappy-Server verwendet werden:

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! 

Wie gesagt, CFStringTransform ist wirklich mächtig. Es unterstützt eine Reihe vordefinierter Transformationen, wie beispielsweise Fallzuordnungen, Normalisierungen oder die Konvertierung von Unicode-Zeichennamen. Sie können sogar eigene Transformationen entwerfen.

Ich habe keine Ahnung, warum Apple es nicht von Cocoa verfügbar macht.

Bearbeiten 2015:

OS X 10.11 und iOS 9 die folgende Methode Foundation hinzufügen:

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

So das Beispiel von oben wird ...

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

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

Danke @nschmidt für die Köpfe hoch.

+0

Dies ist ein brillantes Stück Funktionalität von Apple, und es geht weit über diese Art von Transformation. – Jessedc

+0

Angenommen, ich erhalte eine Zeichenfolge wie convertedString von einer Quelle, die ich nicht ändern kann. Können Sie mir sagen, wie ich den Prozess umkehren kann, damit ich die ursprüngliche Saite zurückbekomme? –

+1

Wie wähle ich den CFSTR? – shiami

2

einfacher Code:

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

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

+0

Hallo zusammen, ich stehe vor einem seltsamen Problem, ich weiß nicht, warum es nicht mit den oben gemachten Vorschlägen funktioniert, Kann jemand bitte diese Zeichenfolge für mich analysieren? @ "ElbowWristHand_DeQuervian \ U00e2 \ U0080 \ U0099s Tenosynovitis"; In der Tat ist es "ElbowWristHand_DeQuervian's" und ich habe alle oben vorgeschlagenen Methoden versucht, aber immer noch nicht funktioniert, bitte vorschlagen. Vielen Dank – york