Deencoding de UTF-8 parcial en NSString

Mientras se obtiene un file UTF-8 través de la networking utilizando la class NSURLConnection , existe una buena probabilidad de que la connection:didReceiveData: del delegado connection:didReceiveData: se envíe con un NSData que trunca el file UTF-8 , ya que UTF-8 es un file multi- esquema de encoding de byte y un solo carácter puede enviarse en dos NSData separados

En otras palabras, si connection:didReceiveData: todos los datos que obtengo de la connection:didReceiveData: tendré un file UTF-8 válido, pero cada dato separado no es UTF-8 () válido.

No quiero almacenar todo el file descargado en la memory.

Lo que quiero es: dado NSData , decodifique todo lo que pueda en un NSString . En caso de que el último byte de NSData sea ​​un sustituto no cerrado, dímelo, así puedo savelos para el siguiente NSData .

Una solución obvia trata repetidamente de decodificar usando initWithData:encoding: cada vez que trunca el último byte, hasta el éxito. Esto, desafortunadamente, puede ser muy derrochador.

Si desea asegurarse de que no se detiene en medio de una secuencia de múltiples bytes de UTF-8, necesitará ver el final de la matriz de bytes y comprobar los 2 bits superiores.

  1. Si el bit superior es 0, entonces es uno de los códigos UTF-8 a testing de estilo ASCII, y ya está.
  2. Si el bit superior es 1 y el segundo de arriba es 0, entonces se trata de la continuación de una secuencia de escape y podría representar el último byte de esa secuencia, por lo que necesitará almacenar temporalmente el carácter para luego ver el anterior personaje*
  3. Si el bit superior es 1 y el segundo de arriba es también 1, entonces es el comienzo de la secuencia de múltiples bytes y debe determinar cuántos caracteres hay en la secuencia buscando el primer bit de 0.

Mire la tabla de múltiples bytes en la input de Wikipedia: http://en.wikipedia.org/wiki/UTF-8

 // assumes that receivedData contains both the leftovers and the new data unsigned char *data= [receivedData bytes]; UInteger byteCount= [receivedData length]; if (byteCount<1) return nil; // or @""; unsigned char *lastByte = data[byteCount-1]; if ( lastByte & 0x80 == 0) { NSString *newString = [NSString initWithBytes: data length: byteCount encoding: NSUTF8Encoding]; // verify success // remove bytes from mutable receivedData, or set overflow to empty return newString; } // now eat all of the continuation bytes UInteger backCount=0; while ( (byteCount > 0) && (lastByte & 0xc0 == 0x80)) { backCount++; byteCount--; lastByte = data[byteCount-1]; } // at this point, either we have exhausted byteCount or we have the initial character // if we exhaust the byte count we're probably in an illegal sequence, as we should // always have the initial character in the receivedData if (byteCount<1) { // error! return nil; } // at this point, you can either use just byteCount, or you can compute the // length of the sequence from the lastByte in order // to determine if you have exactly the right number of characters to decode UTF-8. UInteger requinetworkingBytes = 0; if (lastByte & 0xe0 == 0xc0) { // 110xxxxx // 2 byte sequence requinetworkingBytes= 1; } else if (lastByte & 0xf0 == 0xe0) { // 1110xxxx // 3 byte sequence requinetworkingBytes= 2; } else if (lastByte & 0xf8 == 0xf0) { // 11110xxx // 4 byte sequence requinetworkingBytes= 3; } else if (lastByte & 0xfc == 0xf8) { // 111110xx // 5 byte sequence requinetworkingBytes= 4; } else if (lastByte & 0xfe == 0xfc) { // 1111110x // 6 byte sequence requinetworkingBytes= 5; } else { // shouldn't happen, illegal UTF8 seq } // now we know how many characters we need and we know how many // (backCount) we have, so either use them, or take the // introductory character away. if (requinetworkingBytes==backCount) { // we have the right number of bytes byteCount += backCount; } else { // we don't have the right number of bytes, so remove the intro character byteCount -= 1; } NSString *newString = [NSString initWithBytes: data length: byteCount encoding: NSUTF8Encoding]; // verify success // remove byteCount bytes from mutable receivedData, or set overflow to the // bytes between byteCount and [receivedData count] return newString; 

UTF-8 es una encoding bastante simple para analizar y fue diseñada para facilitar la detección de secuencias incompletas y, si comienza en medio de una secuencia incompleta, para encontrar su comienzo.

Busca hacia atrás desde el final para un byte que sea <= 0x7f o> 0xc0. Si es <= 0x7f, está completo. Si está entre 0xc0 y 0xdf, inclusive, requiere que se complete un byte siguiente. Si está entre 0xe0 y 0xef, requiere que se completen dos bytes siguientes. Si es> = 0xf0, requiere que se completen tres bytes siguientes.

Tengo un problema similar, en parte decodificando utf8

antes de

  NSString * adsTopic = [components[2] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; adsInfo->adsTopic = malloc(sizeof(char) * adsTopic.length + 1); strncpy(adsInfo->adsTopic, [adsTopic UTF8String], adsTopic.length + 1); 

después de [resuelto]

  NSString *adsTopic = [components[2] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; NSUInteger byteCount = [adsTopic lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; NSLog(@"number of Unicode characters in the string topic == %lu",(unsigned long)byteCount); adsInfo->adsTopic = malloc(byteCount+1); strncpy(adsInfo->adsTopic, [adsTopic UTF8String], byteCount + 1); NSString *text=[NSString stringWithCString:adsInfo.adsTopic encoding:NSUTF8StringEncoding]; NSLog(@"=== %@", text);