Использование Objective C / Cocoa для удаления символов Юникода, т. Е. \ U1234 - PullRequest
34 голосов
/ 20 января 2010

Некоторые сайты, с которых я получаю данные, возвращают строки UTF-8 с экранированными символами UTF-8, то есть: \u5404\u500b\u90fd

Есть ли встроенная функция какао, которая может помочь с этим, или мне придется написать собственный алгоритм декодирования.

Ответы [ 4 ]

90 голосов
/ 23 июля 2012

Правильно, что Какао не предлагает решения , но Core Foundation делает: CFStringTransform.

CFStringTransform жизнейв пыльном, удаленном уголке Mac OS (и iOS), и это немного жемчужина.Это внешний интерфейс для совместимого с ICU механизма преобразования строк Apple.Он может выполнять настоящую магию, такую ​​как транслитерация между греческим и латинским языками (или с любыми известными скриптами), но он также может использоваться для выполнения таких рутинных задач, как удаление строк с дрянного сервера:CFStringTransform действительно мощный.Он поддерживает ряд предопределенных преобразований, таких как сопоставления регистров, нормализации или преобразования имен символов Юникода.Вы даже можете создавать свои собственные преобразования.

Я понятия не имею, почему Apple не делает их доступными из Какао.

Редактировать 2015:

OS X 10.11 и iOS 9 добавляют следующий метод в Foundation:

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

Таким образом, приведенный выше пример становится ...

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

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

Спасибо @нщмидт для головы.

24 голосов
/ 20 января 2010

Нет встроенной функции для удаления C.

Вы можете немного обмануть с помощью NSPropertyListSerialization, так как список "старого стиля текста" поддерживает экранирование C через \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);

но учтите, что это не очень эффективно. Намного лучше, если вы напишите свой собственный парсер. (Кстати, вы декодируете строки JSON? Если да, вы можете использовать существующие парсеры JSON .)

12 голосов
/ 29 октября 2011

Вот что я в итоге написал.Надеюсь, это поможет некоторым людям вместе.

+ (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;
}
3 голосов
/ 21 ноября 2014

простой код:

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

от: https://stackoverflow.com/a/7861345

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...