2012-01-19 3 views
12

Mi aplicación descarga un archivo en formato UTF-8, que es demasiado grande para leer utilizando el método NSString initWithContentsOfFile. El problema que tengo es que el método NSFileHandle readDataOfLength lee un número específico de bytes, y puedo terminar leyendo solo parte de un carácter UTF-8. ¿Cuál es la mejor solución aquí?¿Cómo puedo leer un archivo UTF-8 grande en un iPhone?

TARDE:

deja que sea registrada en el cuaderno de bitácora que el siguiente código funciona:

NSData *buf = [NSData dataWithContentsOfFile:path 
             options:NSDataReadingMappedIfSafe 
             error:nil]; 

NSString *data = [[[NSString alloc] 
        initWithBytesNoCopy:(void *)buf.bytes 
        length:buf.length 
        encoding:NSUTF8StringEncoding 
        freeWhenDone:NO] autorelease]; 

Mi problema principal era en realidad que ver con la codificación, no la tarea de leer el archivo.

+0

¿Qué tan grande es este archivo? Megabytes? Gigabytes? –

+0

Digamos que el tamaño del archivo es de 5 MB, aunque no puedo ver que realmente importe. –

Respuesta

13

puede utilizar NSData +dataWithContentsOfFile:options:error: con t él NSDataReadingMappedIfSafe opción para asignar su archivo a la memoria en lugar de cargarlo. Así que usará el administrador de memoria virtual en iOS para garantizar que los bits del archivo se intercambien dentro y fuera de la RAM de la misma forma que un sistema operativo de escritorio maneja su archivo de memoria virtual en disco. Por lo tanto, no necesita suficiente RAM para mantener todo el archivo en la memoria a la vez, solo necesita que el archivo sea lo suficientemente pequeño como para caber en el espacio de direcciones del procesador (por lo tanto, gigabytes). Obtendrá un objeto que actúa exactamente como un NSData normal, lo que le ahorrará la mayor parte de la molestia relacionada con el uso de un NSFileHandle y la transmisión manual.

Es probable que este caso es necesario para convertir porciones de NSString ya que realmente puede esperar que convertir de UTF-8 a otro formato (aunque tal vez no; vale la pena tener un ir con -initWithData:encoding: y viendo si NSString es lo suficientemente inteligente solo para mantener una referencia a los datos originales y expandirse a partir de UTF-8 bajo demanda), que creo que es a lo que su pregunta realmente está llegando.

Sugiero que use -initWithBytes:length:encoding: para convertir una cantidad razonable de bytes en una cadena. A continuación, puede usar -lengthOfBytesUsingEncoding: para averiguar cuántos bytes tiene sentido y avanzar su puntero de lectura de forma adecuada. Es una suposición segura que NSString descartará cualquier carácter de parte al final de los bytes que proporcione.

EDIT: así, algo así como:

// map the file, rather than loading it 
NSData *data = [NSData dataWithContentsOfFile:...whatever... 
         options:NSDataReadingMappedIfSafe 
         error:&youdDoSomethingSafeHere]; 

// we'll maintain a read pointer to our current location in the data 
NSUinteger readPointer = 0; 

// continue while data remains 
while(readPointer < [data length]) 
{ 
    // work out how many bytes are remaining 
    NSUInteger distanceToEndOfData = [data length] - readPointer; 

    // grab at most 16kb of them, being careful not to read too many 
    NSString *newPortion = 
     [[NSString alloc] initWithBytes:(uint8_t *)[data bytes] + readPointer 
       length:distanceToEndOfData > 16384 ? 16384 : distanceToEndOfData 
       encoding:NSUTF8StringEncoding]; 

    // do whatever we want with the string 
    [self doSomethingWithFragment:newPortion]; 

    // advance our read pointer by the number of bytes actually read, and 
    // clean up 
    readPointer += [newPortion lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 
    [newPortion release]; 
} 

Por supuesto, un supuesto implícito es que todos UTF-8 codificaciones son únicos, que tengo que admitir que no es lo suficientemente eficiente para decir con absoluta certeza.

+0

esto solo puede leer el archivo de texto, de lo contrario, el 'nuevoPortion' será nulo – jimwan

2

Un enfoque sería

  1. leer hasta cierto punto -
  2. entonces examinar el último byte (s) para determinar si se está dividiendo un carácter UTF-8
  3. si no - leer el siguiente fragmento
  4. si es así, obtener el siguiente byte y fijar - a continuación, leer el siguiente fragmento
0

utf8 es auto sincronización: simplemente lea un poco más o menos según sea necesario, luego lea los valores de bytes para determinar los límites de cualquier punto de código.

Además, puede usar fopen y usar un pequeño y manejable búfer en la pila para esto y la memoria no será un problema.

3

En realidad, es muy fácil saber si se ha dividido un carácter multibyte en UTF-8.Todos los caracteres de continuación tienen los dos bits más significativos configurados así: 10xxxxxx. Entonces, si el último octeto del búfer tiene ese patrón, escanee hacia atrás para encontrar un octeto que no tenga esa forma. Este es el primer octeto del personaje. La posición de los más significativos 0 en el octeto que indica cuántos octetos están en el carácter

0xxxxxxx => 1 octet (ASCII) 
110xxxxx => 2 octets 
1110xxxx => 3 octets 

y así sucesivamente hasta 6 octetos.

Por lo tanto, es bastante trivial determinar cuántos octetos extra leer para llegar a un límite de caracteres.

+0

Hasta cuatro octetos en realidad, pero no hace mucha diferencia. Si tiene una secuencia de bytes que cree que es el inicio de una secuencia UTF-8 válida con posiblemente un carácter UTF-8 incompleto al final, omita hasta tres bytes con un valor de 0x80 a 0xbf al final, luego omita como máximo un byte con un valor> = 0xc0. presente. – gnasher729

Cuestiones relacionadas