Speziell interessiert mich istream& getline ( istream& is, string& str );
. Gibt es eine Option für den ifstream-Konstruktor, um ihn anzuweisen, alle Newline-Codierungen unter der Haube in '\ n' zu konvertieren? Ich möchte in der Lage sein, getline
alle Zeilenenden anzurufen und ordnungsgemäß zu behandeln.
Update : Zur Verdeutlichung möchte ich in der Lage sein, Code zu schreiben, der fast überall kompiliert wird und Eingaben von fast überall entgegennimmt. Einschließlich der seltenen Dateien mit '\ r' ohne '\ n'. Minimierung von Unannehmlichkeiten für Benutzer der Software.
Es ist einfach, das Problem zu umgehen, aber ich bin immer noch gespannt, wie man im Standard alle Textdateiformate flexibel handhaben kann.
getline
Liest eine vollständige Zeile bis zu einem '\ n' in eine Zeichenfolge ein. Das '\ n' wird aus dem Stream verbraucht, aber getline nimmt es nicht in die Zeichenfolge auf. Das ist bisher in Ordnung, aber es könnte ein '\ r' kurz vor dem '\ n' geben, das in die Zeichenfolge aufgenommen wird.
In Textdateien gibt es drei Arten von Zeilenenden : '\ n' ist die herkömmliche Endung auf Unix-Computern, '\ r' wurde (glaube ich) auf alten Mac-Betriebssystemen verwendet, und Windows verwendet ein Paar, '\ r'. gefolgt von '\ n'.
Das Problem ist, dass getline
das '\ r' am Ende der Zeichenfolge verbleibt.
ifstream f("a_text_file_of_unknown_origin");
string line;
getline(f, line);
if(!f.fail()) { // a non-empty line was read
// BUT, there might be an '\r' at the end now.
}
Bearbeiten Vielen Dank an Neil für den Hinweis, dass f.good()
ich das nicht wollte. !f.fail()
ist was ich will.
Ich kann es manuell selbst entfernen (siehe Bearbeiten dieser Frage), was für die Windows-Textdateien einfach ist. Aber ich mache mir Sorgen, dass jemand eine Datei einspeist, die nur '\ r' enthält. In diesem Fall gehe ich davon aus, dass getline die gesamte Datei verbraucht, da es sich um eine einzelne Zeile handelt!
.. und das berücksichtigt nicht einmal Unicode :-)
.. Vielleicht hat Boost eine gute Möglichkeit, jeweils eine Zeile aus einem beliebigen Textdateityp zu verwenden?
Bearbeiten Ich verwende dies, um mit den Windows-Dateien umzugehen, aber ich habe immer noch das Gefühl, ich sollte es nicht müssen! Und dies wird nicht für die '\ r'-only-Dateien verzweigen.
if(!line.empty() && *line.rbegin() == '\r') {
line.erase( line.length()-1, 1);
}
Antworten:
Wie Neil betonte, "sollte die C ++ - Laufzeit korrekt mit der Konvention zum Beenden von Zeilen für Ihre spezielle Plattform umgehen."
Menschen verschieben jedoch Textdateien zwischen verschiedenen Plattformen, sodass dies nicht gut genug ist. Hier ist eine Funktion, die alle drei Zeilenenden ("\ r", "\ n" und "\ r \ n") behandelt:
Und hier ist ein Testprogramm:
quelle
t
leer ist, bevor das Eofbit eingestellt wird? Sollte dieses Bit nicht gesetzt werden, unabhängig davon, ob andere Zeichen eingelesen wurden?std::get_line
ignoriert wird. Ich habe den folgenden Code im Fall eof verwendet, um dasstd::get_line
Verhalten zu emulieren :is.setstate(std::ios::eofbit); if (t.empty()) is.setstate(std::ios::badbit); return is;
Die C ++ - Laufzeit sollte korrekt mit der Endline-Konvention für Ihre bestimmte Plattform umgehen. Insbesondere sollte dieser Code auf allen Plattformen funktionieren:
Wenn Sie mit Dateien von einer anderen Plattform arbeiten, sind natürlich alle Wetten ungültig.
Als die beiden häufigsten Plattformen (Linux und Windows) , die beide Linien mit einem Newline - Zeichen beenden, mit dem Windows mit einem Wagenrücklauf vorhergehenden ,, können Sie das letzte Zeichen des untersuchen
line
Zeichenfolge in dem obigen Code zu sehen , ob es ist ,\r
und wenn ja Entfernen Sie es, bevor Sie Ihre anwendungsspezifische Verarbeitung durchführen.Sie könnten sich beispielsweise eine Funktion im getline-Stil zur Verfügung stellen, die ungefähr so aussieht (nicht getestet, Verwendung von Indizes, Substraten usw. nur für pädagogische Zwecke):
quelle
safegetline
ist ein wichtiger Teil einer Lösung. Wenn dieses Programm unter Windows kompiliert wird, muss ich die Datei dann auch im Binärformat öffnen? Erlauben Windows-Compiler (im Textmodus), dass sich '\ n' wie '\ r' '\ n' verhält?ifstream f("f.txt", ios_base :: binary | ios_base::in );
Lesen Sie die Datei im BINARY- oder im TEXT- Modus? In TEXT - Modus das Paar Wagenrücksetz / Zeilenvorschub, CRLF wird interpretiert als TEXT Ende der Zeile oder Zeilenendezeichen, aber in BINARY holen Sie ONE Byte zu einem Zeitpunkt, der bedeutet , dass entweder Zeichen MUSTignoriert und im Puffer belassen werden, um als weiteres Byte abgerufen zu werden! Wagenrücklauf bedeutet in der Schreibmaschine, dass das Schreibmaschinenauto, in dem der Druckarm liegt, die rechte Kante des Papiers erreicht hat und zur linken Kante zurückgeführt wird. Dies ist ein sehr mechanisches Modell, das der mechanischen Schreibmaschine. Dann bedeutet der Zeilenvorschub, dass die Papierrolle ein wenig nach oben gedreht wird, sodass das Papier in der Lage ist, eine weitere Zeile zu schreiben. Soweit ich mich erinnere, bedeutet eine der niedrigen Ziffern in ASCII, dass Sie ein Zeichen ohne Eingabe nach rechts bewegen, das tote Zeichen, und natürlich bedeutet \ b Rücktaste: Bewegen Sie das Auto um ein Zeichen zurück. Auf diese Weise können Sie Spezialeffekte wie Basiswert (Typ Unterstrich), Durchgestrichen (Typ Minus) hinzufügen, unterschiedliche Akzente annähern, aufheben (Typ X), ohne eine erweiterte Tastatur zu benötigen. Nur durch Einstellen der Position des Fahrzeugs entlang der Linie vor dem Eingeben des Linienvorschubs. Sie können also ASCII-Spannungen in Byte-Größe verwenden, um eine Schreibmaschine automatisch zu steuern, ohne dass sich dazwischen ein Computer befindet. Wenn die automatische Schreibmaschine eingeführt wird,AUTOMATISCH bedeutet, dass, sobald Sie die äußerste Kante des Papiers erreicht haben, das Auto nach links zurückkehrt UND der angewendete Zeilenvorschub, dh das Auto wird automatisch zurückgegeben, wenn sich die Rolle nach oben bewegt! Sie benötigen also nicht beide Steuerzeichen, sondern nur eines, die \ n, die neue Zeile oder den Zeilenvorschub.
Dies hat nichts mit Programmierung zu tun, aber ASCII ist älter und HEY! Es sieht so aus, als hätten einige Leute nicht nachgedacht, als sie anfingen, Textsachen zu machen! Die UNIX-Plattform setzt eine elektrische automatische Maschinenmaschine voraus. Das Windows-Modell ist vollständiger und ermöglicht die Steuerung mechanischer Maschinen, obwohl einige Steuerzeichen auf Computern immer weniger nützlich sind, wie das Glockenzeichen 0x07, wenn ich mich recht erinnere ... Einige vergessene Texte müssen ursprünglich mit Steuerzeichen erfasst worden sein für elektrisch gesteuerte Schreibmaschinen und es verewigte das Modell ...
Tatsächlich wäre die richtige Variante, nur den Zeilenvorschub \ r einzuschließen, wobei der Wagenrücklauf unnötig ist, dh automatisch, daher:
wäre der korrekteste Weg, um alle Arten von Dateien zu behandeln. Beachte jedoch , dass \ n in TEXT - Modus ist eigentlich das Byte - Paar 0x0d 0x0A, aber 0x0d IST nur \ r \ n \ r enthält in TEXT - Modus jedoch nicht in BINARY , so \ n und \ r \ n oder äquivalent sind ... sollte sein. Dies ist eine sehr grundlegende Verwirrung in der Branche, eine typische Trägheit der Branche, da die Konvention darin besteht, auf ALLEN Plattformen von CRLF zu sprechen und dann in verschiedene binäre Interpretationen zu fallen. Genau genommen sind Dateien, die NUR 0x0d (Wagenrücklauf) als \ n (CRLF oder Zeilenvorschub) enthalten, in TEXT fehlerhaftModus (Schreibmaschinenmaschine: einfach das Auto zurückgeben und alles durchstreichen ...) und sind ein nicht zeilenorientiertes Binärformat (entweder \ r oder \ r \ n bedeutet zeilenorientiert), sodass Sie nicht als Text lesen sollen! Der Code sollte möglicherweise mit einer Benutzermeldung fehlschlagen. Dies hängt nicht nur vom Betriebssystem ab, sondern auch von der Implementierung der C-Bibliothek, was die Verwirrung und mögliche Variationen erhöht ... (insbesondere für transparente UNICODE-Übersetzungsebenen, die einen weiteren Artikulationspunkt für verwirrende Variationen hinzufügen).
Das Problem mit dem vorherigen Code-Snippet (mechanische Schreibmaschine) ist, dass es sehr ineffizient ist, wenn nach \ r keine \ n Zeichen stehen (automatischer Schreibmaschinentext). Dann wird auch der BINARY- Modus angenommen, in dem die C-Bibliothek gezwungen ist, Textinterpretationen (Gebietsschema) zu ignorieren und die bloßen Bytes weiterzugeben. Es sollte keinen Unterschied in den tatsächlichen Textzeichen zwischen beiden Modi geben, nur in den Steuerzeichen. Daher ist das Lesen von BINARY im Allgemeinen besser als der TEXT- Modus. Diese Lösung ist für BINARY effizientModus typische Windows OS-Textdateien unabhängig von Variationen der C-Bibliothek und ineffizient für andere Plattformtextformate (einschließlich Webübersetzungen in Text). Wenn Sie Wert auf Effizienz legen, müssen Sie einen Funktionszeiger verwenden, einen Test für \ r vs \ r \ n Zeilensteuerelemente durchführen, wie Sie möchten, und dann den besten getline-Benutzercode in den Zeiger auswählen und von dort aus aufrufen es.
Ich erinnere mich übrigens, dass ich auch einige \ r \ r \ n Textdateien gefunden habe ... die sich in zweizeiligen Text übersetzen lassen, so wie es einige gedruckte Textkonsumenten noch benötigen.
quelle
Eine Lösung wäre, zuerst alle Zeilenenden zu suchen und durch '\ n' zu ersetzen - genau wie z. B. Git dies standardmäßig tut.
quelle
Abgesehen davon, dass Sie Ihren eigenen benutzerdefinierten Handler schreiben oder eine externe Bibliothek verwenden, haben Sie kein Glück. Am einfachsten ist es, zu überprüfen, ob
line[line.length() - 1]
nicht '\ r' ist. Unter Linux ist dies überflüssig, da die meisten Zeilen mit '\ n' enden, was bedeutet, dass Sie ein gutes Stück Zeit verlieren, wenn sich dies in einer Schleife befindet. Unter Windows ist dies ebenfalls überflüssig. Was ist jedoch mit klassischen Mac-Dateien, die mit '\ r' enden? std :: getline würde für diese Dateien unter Linux oder Windows nicht funktionieren, da '\ n' und '\ r' '\ n' beide mit '\ n' enden, sodass nicht mehr nach '\ r' gesucht werden muss. Offensichtlich würde eine solche Aufgabe, die mit diesen Dateien funktioniert, nicht gut funktionieren. Dann gibt es natürlich die zahlreichen EBCDIC-Systeme, die die meisten Bibliotheken nicht in Angriff nehmen können.Die Suche nach '\ r' ist wahrscheinlich die beste Lösung für Ihr Problem. Wenn Sie im Binärmodus lesen, können Sie nach allen drei gemeinsamen Zeilenenden suchen ('\ r', '\ r \ n' und '\ n'). Wenn Sie sich nur für Linux und Windows interessieren, da Mac-Zeilenenden im alten Stil nicht mehr lange verfügbar sein sollten, suchen Sie nur nach '\ n' und entfernen Sie das nachfolgende Zeichen '\ r'.
quelle
Wenn bekannt ist, wie viele Elemente / Zahlen jede Zeile hat, könnte man eine Zeile mit zB 4 Zahlen als lesen
Dies funktioniert auch mit anderen Zeilenenden.
quelle