Jaki jest właściwy sposób postępowania z dużymi plikami tekstowymi w Objective-C? Powiedzmy, że muszę przeczytać każdą linię osobno i chcę traktować każdą linię jako NSString. Jaki jest najefektywniejszy sposób zrobienia tego?
Jednym z rozwiązań jest użycie metody NSString:
+ (id)stringWithContentsOfFile:(NSString *)path
encoding:(NSStringEncoding)enc
error:(NSError **)error
a następnie podziel linie za pomocą separatora nowej linii, a następnie wykonaj iterację po elementach tablicy. Wydaje się to jednak dość nieefektywne. Czy nie ma łatwego sposobu na traktowanie pliku jako strumienia, wyliczanie w każdym wierszu, zamiast po prostu wczytywać go od razu? Coś jak java.io.BufferedReader w Javie.
Odpowiedzi:
To świetne pytanie. Myślę, że @Diederik ma dobrą odpowiedź, chociaż szkoda, że Cocoa nie ma mechanizmu dokładnie tego, co chcesz zrobić.
NSInputStream
pozwala czytać fragmenty N bajtów (bardzo podobne dojava.io.BufferedReader
), ale musisz samodzielnie przekonwertować je na aNSString
, a następnie zeskanować w poszukiwaniu nowych linii (lub jakiegokolwiek innego separatora) i zapisać wszelkie pozostałe znaki do następnego odczytu lub przeczytać więcej znaków jeśli nowa linia nie została jeszcze odczytana. (NSFileHandle
pozwala przeczytać plik,NSData
który można następnie przekonwertować na plikNSString
, ale jest to zasadniczo ten sam proces).Firma Apple ma przewodnik programowania strumieniowego, który może pomóc wypełnić szczegóły, a to pytanie SO może również pomóc, jeśli masz do czynienia z
uint8_t*
buforami.Jeśli masz zamiar często czytać napisy takie jak ten (szczególnie w różnych częściach programu), dobrym pomysłem byłoby umieszczenie tego zachowania w klasie, która może obsługiwać szczegóły za Ciebie lub nawet podklasę
NSInputStream
(jest zaprojektowana tak, aby była podklasy ) i dodawanie metod, które pozwalają czytać dokładnie to, co chcesz.Dla przypomnienia, myślę, że byłoby to fajna funkcja do dodania i złożę prośbę o ulepszenie czegoś, co to umożliwia. :-)
Edycja: okazuje się, że to żądanie już istnieje. Jest do tego Radar pochodzący z 2006 roku (rdar: // 4742914 dla pracowników Apple).
źródło
To zadziała dla ogólnego czytania
String
zText
. Jeśli chcesz przeczytać dłuższy tekst (duży rozmiar tekstu) , użyj metody, o której wspomniały inne osoby, np. Buforowana (zarezerwuj rozmiar tekstu w pamięci) .Powiedzmy, że czytasz plik tekstowy.
Chcesz pozbyć się nowej linii.
Masz to.
źródło
To powinno załatwić sprawę:
Użyj w następujący sposób:
Ten kod odczytuje z pliku znaki inne niż znaki nowej linii, do 4095 na raz. Jeśli masz linię dłuższą niż 4095 znaków, odczytuje ona dalej, aż trafi w nową linię lub koniec pliku.
Uwaga : nie testowałem tego kodu. Przetestuj go przed użyciem.
źródło
"%4095[^\n]%n%*c"
po cichu pochłonie i wyrzuci jeden znak przy każdym odczytanym buforze. Wygląda na to, że ten format zakłada, że linie będą krótsze niż długość bufora.Mac OS X to Unix, Objective-C to superset w C, więc możesz po prostu używać old-school
fopen
ifgets
from<stdio.h>
. Gwarantujemy, że zadziała.[NSString stringWithUTF8String:buf]
przekonwertuje ciąg C naNSString
. Istnieją również metody tworzenia łańcuchów w innych kodowaniach i tworzenia bez kopiowania.źródło
fgets
będzie zawierało'\n'
znak, więc możesz chcieć go usunąć przed konwersją ciągu.Możesz użyć,
NSInputStream
który ma podstawową implementację dla strumieni plików. Możesz wczytać bajty do bufora (read:maxLength:
metoda). Musisz samodzielnie przeskanować bufor w poszukiwaniu nowych linii.źródło
Odpowiedni sposób czytania plików tekstowych w Cocoa / Objective-C jest udokumentowany w przewodniku programowania ciągów znaków firmy Apple. Sekcja dotycząca czytania i pisania plików powinna być właśnie tym, czego szukasz. PS: Co to jest „linia”? Dwie sekcje ciągu oddzielone znakiem „\ n”? Lub „\ r”? Lub „\ r \ n”? A może faktycznie po akapitach? Wspomniany przewodnik zawiera również sekcję dotyczącą dzielenia ciągu znaków na wiersze lub akapity. (Ta sekcja nazywa się „Akapity i podziały wierszy” i jest połączona linkiem w menu po lewej stronie wskazanej powyżej strony. Niestety ta witryna nie pozwala mi na umieszczenie więcej niż jednego adresu URL, ponieważ nie jest jeszcze godnym zaufania użytkownikiem).
Parafrazując Knutha: przedwczesna optymalizacja jest źródłem wszelkiego zła. Nie zakładaj po prostu, że „wczytywanie całego pliku do pamięci” jest powolne. Czy sprawdziłeś to? Czy wiesz, że faktycznie czyta cały plik do pamięci? Może po prostu zwraca obiekt proxy i czyta za kulisami, gdy konsumujesz ciąg? ( Zastrzeżenie: nie mam pojęcia, czy NSString faktycznie to robi. Możliwe, że mógłby. ) Chodzi o to: najpierw idź z udokumentowanym sposobem robienia rzeczy. Następnie, jeśli testy porównawcze pokazują, że nie zapewnia to oczekiwanej wydajności, zoptymalizuj.
źródło
-stringWithContentsOf*
metod, po których następuje-componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]
, widzi ona\r
i\n
oddzielnie i dodaje pusty wiersz po każdym wierszu.Wiele z tych odpowiedzi to długie fragmenty kodu lub czytane są w całym pliku. Lubię używać metod c do tego właśnie zadania.
Zauważ, że fgetln nie zachowa twojego znaku nowej linii. Ponadto dajemy +1 długości str, ponieważ chcemy zrobić miejsce na zakończenie NULL.
źródło
Aby odczytać plik wiersz po wierszu (również w przypadku bardzo dużych plików), można skorzystać z następujących funkcji:
Lub:
Klasa DDFileReader, która to umożliwia, jest następująca:
Plik interfejsu (.h):
Wdrożenie (.m)
Klasa została wykonana przez Dave DeLong
źródło
Tak jak powiedział @porneL, interfejs API C jest bardzo przydatny.
źródło
Jak inni odpowiedzieli, zarówno NSInputStream, jak i NSFileHandle są dobrymi opcjami, ale można to również zrobić w dość zwarty sposób za pomocą NSData i mapowania pamięci:
BRLineReader.h
BRLineReader.m
źródło
Ta odpowiedź NIE JEST ObjC, ale C.
Skoro ObjC jest oparty na „C”, dlaczego nie użyć fgets?
I tak, jestem pewien, że ObjC ma swoją własną metodę - po prostu nie jestem jeszcze wystarczająco biegły, aby wiedzieć, co to jest :)
źródło
meta
pytanie; czy bardzo stare pytania od zwykłych użytkowników można oznaczyć do przejrzenia?z odpowiedzi @Adama Rosenfielda, ciąg formatujący
fscanf
zostałby zmieniony jak poniżej:będzie działać na końcówkach linii OSX, Linux, Windows.
źródło
Korzystanie z kategorii lub rozszerzenia, aby ułatwić sobie życie.
źródło
Odpowiedź od @lukaswelte i kod od Dave'a DeLonga okazały się bardzo pomocne. Szukałem rozwiązania tego problemu, ale musiałem analizować duże pliki
\r\n
nie tylko\n
.Napisany kod zawiera błąd w przypadku analizowania przez więcej niż jeden znak. Zmieniłem kod jak poniżej.
plik .h:
plik .m:
źródło
Dodam to, ponieważ wszystkie inne odpowiedzi, których próbowałem, były w ten czy inny sposób krótkie. Poniższa metoda może obsługiwać duże pliki, dowolne długie wiersze, a także puste wiersze. Został przetestowany z rzeczywistą zawartością i usunie znak nowej linii z wyniku.
Podziękowania dla @Adam Rosenfield i @sooop
źródło
Widzę, że wiele z tych odpowiedzi polega na wczytywaniu całego pliku tekstowego do pamięci, zamiast brać go po jednym kawałku na raz. Oto moje rozwiązanie w ładnym, nowoczesnym języku Swift, przy użyciu FileHandle, aby utrzymać niski wpływ pamięci:
Zauważ, że zachowuje to powrót karetki na końcu wiersza, więc w zależności od potrzeb możesz dostosować kod, aby go usunąć.
Sposób użycia: po prostu otwórz uchwyt pliku do docelowego pliku tekstowego i wywołaj
readLine
z odpowiednią maksymalną długością - 1024 jest standardem dla zwykłego tekstu, ale zostawiłem go otwartego na wypadek, gdybyś wiedział, że będzie krótszy. Zwróć uwagę, że polecenie nie spowoduje przepełnienia końca pliku, więc jeśli zamierzasz przeanalizować całość, może być konieczne ręczne sprawdzenie, czy go nie osiągnąłeś. Oto przykładowy kod, który pokazuje, jak otworzyć plik pod adresemmyFileURL
i czytać go wiersz po wierszu aż do końca.źródło
Oto ładne, proste rozwiązanie, którego używam do mniejszych plików:
źródło
Użyj tego skryptu, działa świetnie:
źródło