2012-01-19 7 views
12

Moja aplikacja pobiera plik w formacie UTF-8, który jest zbyt duży, aby można go było odczytać przy użyciu metody NSString initWithContentsOfFile. Problem polega na tym, że metoda NSFileHandle readDataOfLength odczytuje określoną liczbę bajtów, a ja mogę skończyć tylko odczytując część znaku UTF-8. Jakie jest najlepsze rozwiązanie?Jak mogę odczytać duży plik UTF-8 na iPhonie?

PÓŹNIEJ:

Niech to być zapisane w dzienniku okrętowym, że następujący kod działa:

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

Moim głównym problemem było rzeczywiście do czynienia z kodowaniem, a nie zadanie odczytu pliku.

+0

Jak duży jest ten plik? Megabajty? Gigabajty? –

+0

Powiedzmy, że rozmiar pliku to 5 MB, ale nie widzę, żeby to miało naprawdę znaczenie. –

Odpowiedz

13

można użyć NSData +dataWithContentsOfFile:options:error: zt on NSDataReadingMappedIfSafe opcja mapowania pliku do pamięci zamiast ładowania go. Dzięki temu będzie korzystać z menedżera pamięci wirtualnej w systemie iOS, aby zapewnić, że bity pliku są wymieniane i usuwane z pamięci RAM w taki sam sposób, jak system operacyjny komputera obsługuje wirtualny plik pamięci na dysku. Tak więc nie potrzebujesz wystarczającej ilości pamięci RAM, aby zachować cały plik w pamięci naraz, wystarczy, że plik będzie wystarczająco mały, aby zmieścił się w przestrzeni adresowej procesora (czyli gigabajtach). Otrzymasz obiekt, który działa dokładnie tak, jak normalny NSData, co powinno zaoszczędzić Ci większości kłopotów związanych z korzystaniem z NSFileHandle i ręcznego przesyłania strumieniowego.

Pewnie wtedy trzeba konwertować porcje NSString ponieważ można realnie oczekiwać, że do konwersji z UTF-8 do innego formatu (choć to może nie, to warto mieć go z -initWithData:encoding: i sprawdzając czy NSString jest wystarczająco inteligentny tylko po to, aby zachować odniesienie do oryginalnych danych i rozwijać się z UTF-8 na żądanie), o którym myślę, że tak naprawdę jest twoje pytanie.

Proponuję użyć -initWithBytes:length:encoding: do konwersji rozsądnej liczby bajtów na ciąg znaków. Następnie można użyć numeru -lengthOfBytesUsingEncoding:, aby dowiedzieć się, ile bajtów faktycznie ma sens i odpowiednio przesuwać wskaźnik odczytu. Jest to bezpieczne założenie, że NSString odrzuci wszystkie znaki części na końcu dostarczonych bajtów.

EDIT: tak, coś jak:

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

oczywiście, ukryte założenie, że wszystkie UTF-8 kodowania są wyjątkowe, które mam nie przyznają się do bycia na tyle kompetentny, aby powiedzieć za absolutną pewność.

+0

to może tylko odczytać plik tekstowy, w przeciwnym razie 'newPortion' będzie zerowe – jimwan

2

Jedno podejście byłoby

  1. czytać aż do pewnego momentu -
  2. następnie zbadać ostatni bajt (y) w celu określenia, czy to jest dzielenie UTF-8 znak
  3. jeśli nie - przeczytać następny kawałek
  4. jeśli tak, dostać następny bajt i naprawić - wtedy przeczytać następny kawałek
0

utf8 jest samoczynną synchronizacją - po prostu przeczytaj trochę mniej lub więcej w razie potrzeby, a następnie odczytaj wartości bajtów, aby określić granice dla dowolnego punktu kodowego.

również można użyć fopen i użyć do tego celu małego, łatwego do zarządzania bufora, a pamięć nie będzie stanowić problemu.

3

W rzeczywistości bardzo łatwo jest powiedzieć, czy podzieliłeś znak wielobajtowy w UTF-8.Wszystkie znaki kontynuacji mają dwa najbardziej znaczące bity ustawione w następujący sposób: 10xxxxxx. Więc jeśli ostatni oktet bufora ma ten wzorzec, skanuj do tyłu, aby znaleźć oktet, który nie ma tej formy. To jest pierwszy oktet postaci. Pozycję najbardziej znaczących 0 w oktetu powie Ci ile oktety są w charakterze

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

i tak dalej aż do 6 oktetów.

To dość trywialne, aby dowiedzieć się, ile dodatkowych oktetów należy przeczytać, aby dostać się do granicy znaku.

+0

Prawie do czterech oktetów w rzeczywistości, ale nie robi dużej różnicy. Jeśli masz ciąg bajtów, które uważasz za początek prawidłowej sekwencji UTF-8 z możliwym niekompletnym znakiem UTF-8 na końcu, pomiń do trzech bajtów o wartości od 0x80 do 0xbf na końcu, a następnie pomiń co najwyżej jeden bajt o wartości> = 0xc0. obecny. – gnasher729