Was ist der geeignete Umgang mit großen Textdateien in Objective-C? Angenommen, ich muss jede Zeile einzeln lesen und möchte jede Zeile als NSString behandeln. Was ist der effizienteste Weg, dies zu tun?
Eine Lösung ist die Verwendung der NSString-Methode:
+ (id)stringWithContentsOfFile:(NSString *)path
encoding:(NSStringEncoding)enc
error:(NSError **)error
Teilen Sie dann die Zeilen mit einem Zeilenumbruchtrennzeichen und iterieren Sie dann über die Elemente im Array. Dies scheint jedoch ziemlich ineffizient zu sein. Gibt es keine einfache Möglichkeit, die Datei als Stream zu behandeln, der über jede Zeile aufgezählt wird, anstatt alles auf einmal einzulesen? Ein bisschen wie Javas java.io.BufferedReader.
Antworten:
Das ist eine gute Frage. Ich denke, @Diederik hat eine gute Antwort, obwohl es bedauerlich ist, dass Cocoa keinen Mechanismus für genau das hat, was Sie tun möchten.
NSInputStream
Ermöglicht das Lesen von Teilen von N Bytes (sehr ähnlich zujava.io.BufferedReader
), aber Sie müssen diese selbst in eine konvertierenNSString
, dann nach Zeilenumbrüchen (oder einem anderen Trennzeichen) suchen und alle verbleibenden Zeichen für den nächsten Lesevorgang speichern oder weitere Zeichen lesen wenn noch kein Zeilenumbruch gelesen wurde. (NSFileHandle
Sie können eine lesen,NSData
die Sie dann in eine konvertieren könnenNSString
, aber es ist im Wesentlichen der gleiche Vorgang.)Apple verfügt über ein Stream-Programmierhandbuch , mit dessen Hilfe Sie die Details ergänzen können. Diese SO-Frage kann auch hilfreich sein, wenn Sie sich mit
uint8_t*
Puffern befassen .Wenn Sie solche Zeichenfolgen häufig lesen (insbesondere in verschiedenen Teilen Ihres Programms), ist es eine gute Idee, dieses Verhalten in einer Klasse zu kapseln, die die Details für Sie verarbeiten kann, oder sogar in Unterklassen
NSInputStream
(wie es entworfen wurde) Unterklasse ) und Hinzufügen von Methoden, mit denen Sie genau lesen können, was Sie wollen.Für die Aufzeichnung denke ich, dass dies eine nette Funktion wäre, um hinzuzufügen, und ich werde eine Verbesserungsanfrage für etwas einreichen, das dies möglich macht. :-)
Bearbeiten: Es stellt sich heraus, dass diese Anforderung bereits vorhanden ist. Hierfür gibt es ein Radar aus dem Jahr 2006 (rdar: // 4742914 für Apple-interne Personen).
quelle
Dies funktioniert für das allgemeine Lesen eines
String
vonText
. Wenn Sie längeren Text lesen möchten (große Textgröße) , verwenden Sie die Methode, die andere Personen hier erwähnt haben, z. B. gepuffert (reservieren Sie die Größe des Textes im Speicherplatz) .Angenommen, Sie lesen eine Textdatei.
Sie möchten neue Linie loswerden.
Hier hast du es.
quelle
Dies sollte den Trick tun:
Verwenden Sie wie folgt:
Dieser Code liest Nicht-Zeilenumbrüche aus der Datei, bis zu 4095 gleichzeitig. Wenn Sie eine Zeile haben, die länger als 4095 Zeichen ist, wird sie so lange gelesen, bis sie auf eine neue Zeile oder ein Dateiende trifft.
Hinweis : Ich habe diesen Code nicht getestet. Bitte testen Sie es, bevor Sie es verwenden.
quelle
"%4095[^\n]%n%*c"
wird bei jedem gelesenen Puffer stillschweigend ein Zeichen verbraucht und weggeworfen . Es sieht so aus, als würde dieses Format davon ausgehen, dass die Zeilen kürzer als die Pufferlänge sind.Mac OS X ist Unix, Objective-C ist C-Obermenge, sodass Sie nur Old-School
fopen
undfgets
von verwenden können<stdio.h>
. Es wird garantiert funktionieren.[NSString stringWithUTF8String:buf]
konvertiert C-String inNSString
. Es gibt auch Methoden zum Erstellen von Zeichenfolgen in anderen Codierungen und zum Erstellen ohne Kopieren.quelle
fgets
enthält das'\n'
Zeichen. Sie können es daher entfernen, bevor Sie die Zeichenfolge konvertieren.Sie können
NSInputStream
eine grundlegende Implementierung für Dateistreams verwenden. Sie können Bytes in einen Puffer (read:maxLength:
Methode) einlesen . Sie müssen den Puffer selbst nach Zeilenumbrüchen durchsuchen.quelle
Die geeignete Methode zum Lesen von Textdateien in Cocoa / Objective-C ist im Apple-Programmierhandbuch für Zeichenfolgen dokumentiert. Der Abschnitt zum Lesen und Schreiben von Dateien sollte genau das sein, wonach Sie suchen. PS: Was ist eine "Linie"? Zwei durch "\ n" getrennte Abschnitte einer Zeichenfolge? Oder "\ r"? Oder "\ r \ n"? Oder vielleicht bist du tatsächlich hinter Absätzen her? Die zuvor erwähnte Anleitung enthält auch einen Abschnitt zum Aufteilen einer Zeichenfolge in Zeilen oder Absätze. (Dieser Abschnitt heißt "Absätze und Zeilenumbrüche" und ist im Menü auf der linken Seite der Seite, auf die ich oben verwiesen habe, verlinkt. Leider kann ich auf dieser Website nicht mehr als eine URL veröffentlichen, wie ich bin noch kein vertrauenswürdiger Benutzer.)
Um Knuth zu paraphrasieren: Vorzeitige Optimierung ist die Wurzel allen Übels. Gehen Sie nicht einfach davon aus, dass das "Einlesen der gesamten Datei in den Speicher" langsam ist. Haben Sie es bewertet? Wissen Sie, dass es tatsächlich die gesamte Datei in den Speicher liest? Vielleicht gibt es einfach ein Proxy-Objekt zurück und liest weiter hinter den Kulissen, während Sie die Zeichenfolge verbrauchen? ( Haftungsausschluss: Ich habe keine Ahnung, ob NSString dies tatsächlich tut. Es ist denkbar, dass dies möglich ist. ) Der Punkt ist: Gehen Sie zuerst mit der dokumentierten Vorgehensweise vor. Wenn Benchmarks zeigen, dass dies nicht die gewünschte Leistung bietet, optimieren Sie.
quelle
-stringWithContentsOf*
Methoden verwenden-componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]
, wird das\r
und\n
separat angezeigt und nach jeder Zeile wird eine Leerzeile hinzugefügt.Viele dieser Antworten sind lange Codestücke oder sie lesen die gesamte Datei ein. Ich verwende gerne die c-Methoden für genau diese Aufgabe.
Beachten Sie, dass fgetln Ihren Zeilenumbruch nicht beibehält. Außerdem +1 die Länge der str, weil wir Platz für die NULL-Terminierung schaffen wollen.
quelle
Das zeilenweise Lesen einer Datei (auch für extrem große Dateien) kann mit den folgenden Funktionen erfolgen:
Oder:
Die Klasse DDFileReader, die dies ermöglicht, ist die folgende:
Schnittstellendatei (.h):
Implementierung (.m)
Der Unterricht wurde von Dave DeLong durchgeführt
quelle
Genau wie @porneL sagte, ist die C api sehr praktisch.
quelle
Wie andere geantwortet haben, sind sowohl NSInputStream als auch NSFileHandle gute Optionen, aber es kann auch auf ziemlich kompakte Weise mit NSData und Speicherzuordnung durchgeführt werden:
BRLineReader.h
BRLineReader.m
quelle
Diese Antwort lautet NICHT ObjC, sondern C.
Warum nicht fgets verwenden, da ObjC auf C basiert?
Und ja, ich bin sicher, ObjC hat seine eigene Methode - ich bin einfach noch nicht kompetent genug, um zu wissen, was es ist :)
quelle
meta
Frage; Sollten sehr alte Fragen von regulären Benutzern zur Überprüfung markiert werden können?Aus der Antwort von @Adam Rosenfield geht hervor, dass die Formatierungszeichenfolge von
fscanf
wie folgt geändert wird:Es funktioniert in OSX-, Linux- und Windows-Zeilenenden.
quelle
Verwenden von Kategorien oder Erweiterungen, um unser Leben ein bisschen einfacher zu machen.
quelle
Ich fand die Antwort von @lukaswelte und den Code von Dave DeLong sehr hilfreich. Ich suchte nach einer Lösung für dieses Problem, musste aber
\r\n
nicht nur große Dateien analysieren\n
.Der geschriebene Code enthält einen Fehler, wenn mehr als ein Zeichen analysiert wird. Ich habe den Code wie folgt geändert.
.h Datei:
.m Datei:
quelle
Ich füge dies hinzu, weil alle anderen Antworten, die ich versucht habe, auf die eine oder andere Weise fehlgeschlagen sind. Die folgende Methode kann große Dateien, beliebig lange Zeilen sowie leere Zeilen verarbeiten. Es wurde mit tatsächlichem Inhalt getestet und entfernt Zeilenumbrüche aus der Ausgabe.
Gutschrift geht an @Adam Rosenfield und @sooop
quelle
Ich sehe, dass viele dieser Antworten darauf beruhen, die gesamte Textdatei in den Speicher zu lesen, anstatt sie einzeln zu belegen. Hier ist meine Lösung in nettem modernem Swift, bei der FileHandle verwendet wird, um die Auswirkungen auf den Speicher gering zu halten:
Beachten Sie, dass dadurch der Wagenrücklauf am Ende der Zeile erhalten bleibt. Abhängig von Ihren Anforderungen möchten Sie möglicherweise den Code anpassen, um ihn zu entfernen.
Verwendung: Öffnen Sie einfach ein Dateihandle für Ihre Zieltextdatei und rufen Sie
readLine
mit einer geeigneten maximalen Länge auf - 1024 ist Standard für Klartext, aber ich habe es offen gelassen, falls Sie wissen, dass es kürzer sein wird. Beachten Sie, dass der Befehl das Ende der Datei nicht überläuft. Daher müssen Sie möglicherweise manuell überprüfen, ob Sie ihn nicht erreicht haben, wenn Sie das gesamte Objekt analysieren möchten. Hier ist ein Beispielcode, der zeigt, wie Sie eine Datei unter öffnenmyFileURL
und bis zum Ende Zeile für Zeile lesen können.quelle
Hier ist eine schöne einfache Lösung, die ich für kleinere Dateien verwende:
quelle
Verwenden Sie dieses Skript, es funktioniert großartig:
quelle