Optimale Möglichkeit, eine komplette Datei mit fstream in eine Zeichenkette einzulesen?

8

Viele andere Beiträge, wie " Lesen Sie die ganze ASCII-Datei in C ++ std :: string "Erklären Sie, was einige Optionen sind, aber beschreiben Sie keine Vor- und Nachteile verschiedener Methoden in irgendeiner Tiefe. Ich möchte wissen, warum eine Methode einer anderen vorzuziehen ist?

All diese verwenden std::fstream , um die Datei in ein std::string einzulesen. Ich bin mir nicht sicher, was die Kosten und Vorteile jeder Methode sind. Nehmen wir an, dies ist für den häufigen Fall, in dem die gelesenen Dateien bekanntermaßen etwas kleiner sind, Speicher kann leicht unterzubringen, deutlich zu lesen eine Multi-Terrabyte-Datei in einen Speicher ist eine schlechte Idee, egal wie Sie es tun.

Der häufigste Weg nach ein paar Google-Suchen, eine ganze Datei in eine std :: string zu lesen, besteht darin, std::getline zu verwenden und nach jeder Zeile ein Zeilenumbruchzeichen hinzuzufügen. Das scheint mir unnötig, aber gibt es einen gewissen Leistungs- oder Kompatibilitätsgrund, dass dies ideal ist?

%Vor%

Eine andere Möglichkeit, die ich zusammensetze, ist, den getline-Begrenzer zu ändern, damit er nicht in der Datei enthalten ist. Das EOF-Zeichen scheint sich wahrscheinlich nicht in der Mitte der Datei zu befinden, so dass dies ein wahrscheinlicher Kandidat zu sein scheint. Dies schließt eine Umwandlung ein, so dass es mindestens einen Grund gibt, dies nicht zu tun, aber dies liest eine Datei sofort ohne Zeichenkettenverkettung. Vermutlich gibt es noch einige Kosten für die Trennzeichenprüfungen. Gibt es noch andere gute Gründe, dies nicht zu tun?

%Vor%

Der Cast bedeutet, dass auf Systemen, die std :: char_traits :: eof () als etwas anderes als -1 definieren, Probleme auftreten können. Ist dies ein praktischer Grund, dies nicht gegenüber anderen Methoden zu wählen, die std::getline und string::push_pack('\n') verwenden.

Wie vergleichen sich diese im Vergleich zu anderen Möglichkeiten, die Datei auf einmal zu lesen, wie in dieser Frage: Lesen Sie die gesamte ASCII-Datei in C ++ std :: string

%Vor%

Es scheint, das wäre das Beste. Es entlädt fast die gesamte Arbeit auf die Standardbibliothek, die für die gegebene Plattform stark optimiert werden sollte. Ich sehe keinen Grund für andere Prüfungen als die Gültigkeit des Streams und das Ende der Datei. Ist das ideal oder gibt es Unsichtbares, das unsichtbar ist?

Gibt der Standard oder die Details einer Implementierung Grund, eine Methode einer anderen vorzuziehen? Habe ich eine Methode verpasst, die sich unter den verschiedensten Umständen als ideal erweisen könnte?

Was ist eine einfache, idiomatische, leistungsfähigste und standardkonforme Methode zum Lesen einer ganzen Datei in std::string ?

BEARBEITEN - 2 Diese Frage hat mich veranlasst, eine kleine Reihe von Benchmarks zu schreiben. Sie sind MIT-Lizenz und auf github verfügbar unter: Ссылка

Schnellste - TellSeekRead und CTellSeekRead- Diese haben das System bieten eine einfache, um die Größe zu erhalten und liest die Datei auf einmal.

Schneller - Appline und Eof - Die Überprüfung von Zeichen scheint keine Kosten zu verursachen.

Fast - RdbufMove und Rdbuf - Der std :: move scheint keinen Unterschied in der Veröffentlichung zu machen.

Langsam - Iterator, BackInsertIterator und AssignIterator - Bei Iteratoren und Eingabeströmen stimmt etwas nicht. Die Arbeit ist großartig in Erinnerung, aber nicht hier. Das heißt, einige von diesen sind schneller als andere.

Ich habe jede bisher vorgeschlagene Methode hinzugefügt, einschließlich derjenigen in Links. Ich würde mich freuen, wenn jemand das auf Windows und mit anderen Compilern ausführen könnte. Ich habe derzeit keinen Zugriff auf eine Maschine mit NTFS und es wurde festgestellt, dass diese und Compilerdetails wichtig sein könnten.

Wie messen wir Einfachheit und Idiomatik, wie messen wir diese objektiv? Die Einfachheit scheint machbar zu sein, vielleicht benutzt man eine Linie LOCs und eine zyklomatische Komplexität, aber wie idiomatisch etwas erscheint, ist rein subjektiv.

    
Sqeaky 23.08.2015, 18:21
quelle

3 Antworten

3
  

Was ist eine einfache, idiomatische, beste Leistung und Standard   konforme Art, eine ganze Datei in eine std :: string zu lesen?

Das sind ziemlich widersprüchliche Bitten, die am wahrscheinlichsten den anderen vermindern. einfacherer Code wird nicht der schnellste oder idiomatischste sein.

nachdem ich dieses Gebiet für eine Weile erkundet habe, bin ich zu einigen Schlüssen gekommen:
1) die größte Ursache für die Leistungseinbußen ist die IO-Aktion selbst - je weniger IO-Aktionen - desto schneller der Code
2) Speicherzuweisungen auch ziemlich teuer, aber nicht so teuer wie die IO-Karte 3) lesen als binär ist schneller als lesen als text
4) mit der OS-API wird wahrscheinlich schneller sein als C ++ - Streams 5) std::ios_base::sync_with_stdio beeinflusst die Aufführung nicht wirklich, es ist eine urbane Legende.

Die Verwendung von std::getline ist wahrscheinlich nicht die beste Wahl, wenn aus diesen Gründen Leistung erforderlich ist: N IO-Aktionen und N Zuweisungen für N Zeilen werden durchgeführt.

Ein Kompromiss, der schnell, Standard und elegant ist, besteht darin, die Dateigröße zu ermitteln, den gesamten Speicher auf einmal zuzuordnen und dann die Datei gleichzeitig zu lesen:

%Vor%

Verschieben Sie den Inhalt, um nicht benötigte Kopien zu vermeiden.

    
David Haim 24.08.2015 15:56
quelle
2

Diese Website hat eine gute Vergleich auf mehrere verschiedene Methoden dafür. Die eine, die ich derzeit verwende, ist:

%Vor%

Wenn Ihre Textdateien durch Zeilenumbrüche getrennt sind, bleiben sie erhalten. Wenn Sie das zum Beispiel entfernen möchten (was in den meisten Fällen mein Fall ist), können Sie einfach einen Aufruf an etwas wie

hinzufügen %Vor%     
LLLL 24.08.2015 16:03
quelle
1

Es gibt zwei große Schwierigkeiten mit Ihrer Frage. Erstens schreibt der Standard keine bestimmte Implementierung vor (ja, fast alle haben mit der gleichen Implementierung begonnen, aber sie haben sie im Laufe der Zeit modifiziert, und der optimale I / O-Code für NTFS wird sich beispielsweise von dem optimalen unterscheiden I / O-Code für ext4), so ist es möglich (wenn auch etwas unwahrscheinlich), dass ein bestimmter Ansatz auf einer Plattform am schnellsten ist, aber nicht auf einer anderen. Zweitens gibt es eine kleine Schwierigkeit bei der Definition von "optimal"; Ich nehme an, du meinst "Schnellster", aber das ist nicht unbedingt der Fall.

Es gibt Ansätze, die idiomatisch und perfekt in C ++ sind, aber kaum eine wunderbare Leistung bringen. Wenn es Ihr Ziel ist, mit einem einzelnen std::string zu enden, ist std::getline(std::ostream&, std::string&) sehr wahrscheinlich langsamer als nötig. Der Aufruf std::getline() muss nach dem '\n' suchen, und Sie werden gelegentlich das Ziel std::string neu zuweisen und kopieren. Trotzdem ist es lächerlich einfach und leicht zu verstehen. Das könnte aus Sicht der Wartung optimal sein, vorausgesetzt, Sie benötigen nicht die absolut schnellste Leistung, die möglich ist. Dies ist auch ein guter Ansatz, wenn Sie nicht gleichzeitig die gesamte Datei in einem riesigen std::string benötigen. Sie werden sehr sparsam mit der Erinnerung sein.

Ein Ansatz, der wahrscheinlich effizienter ist, ist die Manipulation des Lesepuffers:

%Vor%

Ich persönlich verwende wahrscheinlich std::fopen() und std::fread() (und std::unique_ptr<FILE> ), weil Sie zumindest unter Windows eine bessere Fehlermeldung erhalten, wenn std::fopen() fehlschlägt als beim Erstellen von Dateistreamobjekt schlägt fehl. Ich halte die bessere Fehlermeldung für einen wichtigen Faktor bei der Entscheidung, welcher Ansatz optimal ist.

    
Max Lybbert 24.08.2015 02:46
quelle

Tags und Links