Подтвердить что ты не робот

Как я могу прочитать большой файл UTF-8 на iPhone?

Мое приложение загружает файл в формате UTF-8, который слишком велик для чтения с использованием метода NSString initWithContentsOfFile. Проблема заключается в том, что метод NSFileHandle readDataOfLength считывает указанное количество байтов, и я могу закончить только чтение части символа UTF-8. Какое лучшее решение здесь?

СПУСТЯ:

Позволяет записывать в журнале судна, что работает следующий код:

    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];

Моя основная проблема была в действительности связана с кодировкой, а не с задачей чтения файла.

4b9b3361

Ответ 1

Вы можете использовать NSData +dataWithContentsOfFile:options:error: с опцией NSDataReadingMappedIfSafe, чтобы скопировать файл в память, а не загружать его. Таким образом, он будет использовать диспетчер виртуальной памяти в iOS, чтобы гарантировать, что биты файла будут заменены и выведены из ОЗУ таким же образом, что операционная система Windows обрабатывает свой файл виртуальной памяти на диске. Таким образом, вам не нужно достаточно оперативной памяти для одновременного хранения всего файла в памяти, вам просто нужно, чтобы файл был достаточно мал, чтобы вписываться в адресное пространство процессора (так, гигабайты). Вы получите объект, который будет действовать как обычный NSData, который должен сэкономить вам большую часть проблем, связанных с использованием NSFileHandle и ручным потоком.

Вам, вероятно, понадобится преобразовать части в NSString, так как вы можете реально ожидать, что конвертировать из UTF-8 в другой формат (хотя это может и не быть: стоит пойти с -initWithData:encoding: и посмотреть, будет ли NSString достаточно умен, чтобы поддерживать ссылку на исходные данные и расширяться с UTF-8 по требованию), который, по моему мнению, является тем, на что ваш вопрос действительно попадает.

Я бы предложил использовать -initWithBytes:length:encoding: для преобразования разумного количества байтов в строку. Затем вы можете использовать -lengthOfBytesUsingEncoding:, чтобы узнать, сколько байтов оно действительно имело смысл и соответствующим образом улучшить ваш указатель чтения. Это безопасное предположение, что NSString будет отбрасывать любые части символов в конце предоставленных вами байтов.

EDIT: так что-то вроде:

// 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];
}

Конечно, подразумеваемое предположение состоит в том, что все кодировки UTF-8 уникальны, и я должен признать, что он недостаточно осведомлен, чтобы сказать для абсолютного определенного.

Ответ 2

На самом деле очень просто определить, разделили ли вы многобайтовый символ в UTF-8. Все символы продолжения имеют два наиболее значимых бита, заданных следующим образом: 10xxxxxx. Поэтому, если последний октет буфера имеет этот шаблон, сканируйте назад, чтобы найти октет, который не имеет этой формы. Это первый октет персонажа. Позиция наиболее значимого 0 в октете говорит вам, сколько октетов в символе

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

и так далее до 6 октетов.

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

Ответ 3

Один подход заключается в

  • прочитайте до определенного момента -
  • то рассмотрим последний байт (ы), чтобы определить, если он разделяет символ UTF-8
  • если нет - прочитайте следующий фрагмент
  • если да, получите следующий байт и исправьте - затем прочитайте следующий фрагмент

Ответ 4

utf8 - это самосинхронизация - просто прочитайте немного больше или меньше по мере необходимости, затем прочитайте значения байтов, чтобы определить границы для любой кодовой точки.

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