Datei Rückwärts lesen
-
ifstream file("abc.txt"); file.seekg(0,ios::end);
So kann ich den Dateizeiger ja auf das Ende der Datei setzen aber wie kann ich jetzt Rückwärts lesen?
Ich wollte möglichst nicht den ganzen Dateiinhalt im Speicher sichern und dann den Speicher vom Ende aus durchsuchen...
-
mich wuerde spontan der Use case interessieren.
-
Posix bietet hier mmap an, damit dürfte das ziemlich einfach und performant gehen.
Ansonsten mit seekg in Chunks arbeiten, erst die letzten 20MB auslesen, dann die vorletzten, etc.
-
Ganz pauschal: Wenn du einfach die komplette Datei Rückwärts in einem String haben willst, dann nimmst du den Streambuf und schiebst in den String nacheinander mit
sungetc
rein.#include <iostream> #include <sstream> int main() { std::istringstream stream("abcdefg"); stream.seekg( 0, std::ios_base::end ); std::streambuf* rdbuf = stream.rdbuf(); std::string str; typedef std::istringstream::traits_type traits; traits::int_type i; while( !traits::eq_int_type( i = rdbuf->sungetc(), traits::eof() ) ) str.push_back( traits::to_char_type(i) ); std::cout << str; }
Aber wie ich jetzt verstehe, wird bei diesem Vorgehen ja genau das gemacht - die Datei wird komplett in den Speicher geladen, was du ja nicht willst. Dann bietet sich die Version mit Chunks an.
-
Sone schrieb:
Ganz pauschal: Wenn du einfach die komplette Datei Rückwärts in einem String haben willst, dann nimmst du den Streambuf und schiebst in den String nacheinander mit
sungetc
rein.Ganz pauschal: NEIN!
-
volkard schrieb:
NEIN!
Nein? Willst du es lieber direkt mit dem
istream
? Ist doch aber irre lahm, wenn intern noch einsentry
instantiiert wird usw.std::istringstream stream("abcdefg"); stream.seekg( 0, std::ios_base::end ); std::string str; while( stream.unget().good() ) str.push_back( stream.peek() ); std::cout << str;
Edit:
std::reverse_copy
geht ja leider nicht.
-
Sone schrieb:
volkard schrieb:
NEIN!
Nein?
Lies doch damit mal eine Datei rückwärts und zeige sie zweichenweise an.
-
@Sone: Wenn schon, dann mit einem eigenen streambuf
class backbuf : public std::streambuf { std::streambuf* sb; public: explicit backbuf(std::streambuf *sb) : sb(sb) { sb->sungetc(); } protected: virtual int_type underflow() { return sb->sgetc(); } virtual int_type uflow() { return sb->sungetc(); } };
Nur leider überschreibt ifstream nicht
pbackfail
, wie hier nötig.Da du offensichtlich zu viel Zeit hast, wäre es eine nette Übung für dich, einen
pbackfail_aware_ifstream
zu schreiben.
-
Sollte nicht
filebuf pbackfail
überschreiben, wenn überhaupt?
-
Sone schrieb:
Sollte nicht
filebuf pbackfail
überschreiben, wenn überhaupt?Ja, das war eine Sprachungenauigkeit.
-
unget (oder die s-Variante) funktioniert doch nur, wenn vorher bereits alles gelesen wurde. D.h. hier würde man im Prinzip alles zwei Mal lesen. Zudem ist nicht garantiert, dass unget beliebig lange funktioniert.
Attempts to decrease the current location in the stream by one character, making the last character extracted from the stream once again available to be extracted by input operations.
Wenn die Datei klein ist, würde ich sie einfach komplett in einen string lesen und über reverse_iterator durchlaufen. Ist zwar nicht super und liest auch alles doppelt, aber eben einfach und fehler-unanfällig.
Man könnte sich auch überlegen, ob man einen Container nutzt, der sehr schnell am Anfang einfügen kann. Dann könnte man jedes get mit einem insert in diesen Container verbinden. Wenn man von Anfang an die Dateigröße kennt, könnte man einen entsprechenden string resizen und eben entsprechend der Position einfügen. Doch auch hier muss man alles auf einmal lesen und... gewinnt vermutlich nichts.
-
Ok, jetzt reicht es mir. Ich faile nicht nochmal mit Streams. Das Meisterbuch ist in einer Woche da, dann werde ich Stream-Experte.