34 votes

Utilisation d'Objective C/Cocoa pour décomposer les caractères unicode, c'est à dire \u1234

Certains sites à partir desquels je récupère des données renvoient des chaînes UTF-8, avec les caractères UTF-8 échappés, par exemple : \u5404\u500b\u90fd

Existe-t-il une fonction cacao intégrée qui pourrait m'aider ou dois-je écrire mon propre algorithme de décodage ?

92voto

Nikolai Ruhe Points 45433

Il est vrai que Cacao ne propose pas de solution, mais Fondation de base fait : CFStringTransform .

CFStringTransform vit dans un coin poussiéreux et éloigné de Mac OS (et iOS) et est donc un joyau peu connu. Il s'agit de l'interface de l'application Compatible avec les soins intensifs moteur de transformation de chaînes de caractères. Il peut faire de la vraie magie comme des translittérations entre le grec et le latin (ou à peu près tous les scripts connus), mais il peut aussi être utilisé pour faire des tâches banales comme l'uncaping de chaînes de caractères à partir d'un serveur pourri :

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!

Comme je l'ai dit, CFStringTransform est vraiment puissant. Il prend en charge un certain nombre de transformations prédéfinies, comme les correspondances de casse, les normalisations ou la conversion des noms de caractères unicode. Vous pouvez même concevoir vos propres transformations.

Je ne sais pas pourquoi Apple ne le rend pas disponible à partir de Cocoa.

24voto

KennyTM Points 232647

Il n'existe pas de fonction intégrée permettant d'effectuer le désencadrement en C.

Vous pouvez tricher un peu avec NSPropertyListSerialization puisqu'un plist "ancien style de texte" supporte l'échappement C par le biais 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);

mais sachez que ce n'est pas très efficace. Il vaut mieux que vous écriviez votre propre analyseur syntaxique. (BTW, est-ce que vous décodez des chaînes JSON ? Si oui, vous pouvez utiliser les analyseurs JSON existants .)

12voto

Christoph Points 81

Voici ce que j'ai fini par écrire. J'espère que cela aidera certaines personnes.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X