2010-01-20 7 views

Respuesta

23

No hay una función incorporada para realizar la desconexión de C.

Usted puede engañar un poco con NSPropertyListSerialization ya que un plist "estilo antiguo texto" apoya C escapar a través de \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); 

pero cuenta que esto no es muy eficiente. Es mucho mejor si escribes tu propio analizador. (Por cierto, ¿está decodificando cadenas JSON? En caso afirmativo, podría usar the existing JSON parsers.)

+0

"No hay ninguna función integrada de hacerlo" es lo que estaba tratando de averiguar. Terminé de rodar el mío, solo quería comprobar que no estaba reinventando la rueda. Los analizadores JSON existentes ya no son lo suficientemente indulgentes con la salida JSON mal formada que a veces envían los sitios web dudosos. – corydoras

+0

+1 dang esto es inteligente –

11

Esto es lo que terminé escribiendo. Espero que esto ayude a algunas personas.

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

debe ser legal matar desarrollador del lado del servidor, solo por forzarme a usar esta solución. @Christoph buen código de trabajo por cierto. ¡Aclamaciones! –

87

Es correcto que cacao no ofrece una solución , sin embargo Fundación Core hace: CFStringTransform.

CFStringTransform vive en una esquina polvorienta y remota de Mac OS (y iOS) por lo que es una pequeña joya. Es la parte delantera del motor de transformación de cadenas ICU compatible de Apple. Se puede hacer magia real como transcripciones entre griego y latín (o sobre cualquier script conocidos), pero también puede ser utilizado para hacer las tareas cotidianas como la representación no literal cuerdas desde un servidor chungo:

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! 

Como ya he dicho, CFStringTransform es realmente poderoso Es compatible con varias transformaciones predefinidas, como asignaciones de casos, normalizaciones o conversión de nombre de carácter unicode. Incluso puedes diseñar tus propias transformaciones.

No tengo idea de por qué Apple no lo pone a disposición de Cocoa.

Edición 2015:

OS X e iOS 10.11 9 añade el siguiente método a la Fundación:

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

Así que el ejemplo de arriba se convierte en ...

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

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

Gracias @nschmidt por el cara a cara.

+0

Esta es una brillante pieza de funcionalidad de Apple, y va más allá de este tipo de transformación. – Jessedc

+0

Digamos que recibo una cadena como convertingString de una fuente que no puedo cambiar. ¿Puedes decirme cómo puedo ir sobre la inversión del proceso para recuperar la cadena original? –

+1

¿Cómo elegir el CFSTR? – shiami

2

código simple:

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

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

+0

Hola a todos, me enfrenta un problema extraño, no sé por qué no funciona con las sugerencias que se hicieron anteriormente. ¿Alguien puede analizar esta cadena por mí? @ "Tenosynovitis de ElbowWristHand_DeQuervian \ U00e2 \ U0080 \ U0099s"; En realidad, es "ElbowWristHand_DeQuervian's" y he intentado con todos los métodos sugeridos anteriormente, pero todavía no funciona, por favor sugiera. Gracias – york

Cuestiones relacionadas