Einlesen, stoppen und Einlesen
-
Hallo,
ich habe in einer Textdatei Daten in folgendem Format:
unsigned int N: unsigned int X
N & X --> Zahlen im Wertebereich von unsigned int.
Die Dateien auszulesen ist kein Problem.
Aber ich möchte N und X in 2 seperaten Variablen speichern.
Ich habe mich schon informiert, und herausgefunden,
dass es zumindest möglich ist, bis zu einem bestimmten
Zeichen zu lesen. Wie kann ich denn alles einlesen, bis ein ":" kommt,
dann das eingelesene in einer Variable speichern und anscließend von dort aus [nach dem : ] weiterlesen?
-
unsigned int val1, val2; while (file >> val1 && file.get() == ':' && file >> val2) { std::cout << val1 << ' ' << val2 << '\n'; }
-
@Nathan: Was ist mit
"1 : 2"(beachte das Leerzeichen)?
-
Habe ich richtig verstanden, dein Format ist
N : X
N : X
...?
In dem Fall ist es ganz einfach:
Beide Datenreihen in bspw. einer std::map<unsigned, unsigned> oder einem std::vector<std::pair<unsigned, unsigned>> (oder gar einfach einem vector, wo N den Index von X darstellt) speichern.
Das Einlesen sähe dann so aus - die C++-Iostreams machen das schon ordentlich:#include <iostream> #include <vector> #include <fstream> #include <utility> template<char C> class Char { friend std::istream& operator>>( std::istream& is, Char const& c ) { if( std::istream::traits_type::to_char_type(is.get()) != C ) is.setstate( std::ios_base::failbit ); return is; } }; int main() { std::vector<std::pair<unsigned, unsigned>> mData; std::ifstream stream("Data.txt"); unsigned a, b; while( stream >> a >> Char<':'>() >> b ) mData.push_back({a, b}); }Edit: Mist, Nathan war schneller...

-
Satan schrieb:
@Nathan: Was ist mit
"1 : 2"(beachte das Leerzeichen)?Die Leerzeichen werden hier vom intern erzeugten
std::istream::sentryObjekt bei allen formatierten und unformatierten Extraktionen entfernt - solange dassskipwsFlag gesetzt ist (was es standardmäßig ist).
Kurz: Die werden ignoriert.
-
Sone schrieb:
die C++-Iostreams machen das schon ordentlich
Jetzt mach bitte noch den Check, dass jede Zeile vom Typ "<Leerzeichen><Zahl><Leerzeichen>:<Leerzeichen><Zahl><Leerzeichen>" sein muss, also insb. "1\n:2" nicht erlaubt wird.
Sone schrieb:
Satan schrieb:
@Nathan: Was ist mit
"1 : 2"(beachte das Leerzeichen)?Die Leerzeichen werden hier vom intern erzeugten
std::istream::sentryObjekt bei allen formatierten und unformatierten Extraktionen entfernt - solange dassskipwsFlag gesetzt ist (was es standardmäßig ist).
Kurz: Die werden ignoriert.Nur schade, dass das sentry bei istream.get() auf noskipws gesetzt wird.
Kurz: Die werden ausgelesen.
-
Sone schrieb:
Satan schrieb:
@Nathan: Was ist mit
"1 : 2"(beachte das Leerzeichen)?Die Leerzeichen werden hier vom intern erzeugten
std::istream::sentryObjekt bei allen formatierten und unformatierten Extraktionen entfernt - solange dassskipwsFlag gesetzt ist (was es standardmäßig ist).
Kurz: Die werden ignoriert.Nö
int main() { string test( "1: 2" ); istringstream file( test ); unsigned int val1, val2; while (file >> val1 && file.get() == ':' && file >> val2) { std::cout << val1 << ' ' << val2 << '\n'; } }
-
@out: Schwer von Begriff? Nimm bitte mal
string test( "1 :2" );
-
Sone schrieb:
if( std::istream::traits_type::to_char_type(is.get()) != C )Wieso muss bei dir eigentlich alles immer so kompliziert aussehen?
Satan schrieb:
@out: Schwer von Begriff? Nimm bitte mal
string test( "1 :2" );Was damit? Das geht nicht, hab ich doch gesagt?
-
Danke [an alle, insbesondere Nathan].
Eigentlich habe ich mich verschrieben beim Format:
statt N: X meine ich N:X, ist aber ganz egal.
Container etc.. habe ich noch nicht kennengelernt, kommt aber bald.
Jetzt habe ich mal Verständnisfragen:unsigned int val1, val2; while (file >> val1 && file.get() == ':' && file >> val2) { std::cout << val1 << ' ' << val2 << '\n'; }Im Schleifenkopf von While kommt nur ne Bedingung,
ich versteht nicht was die Bedingung prüft:while (file >> val1 && file.get() == ':' && file >> val2)">>" ist der "rechtsverschiebungsoperator", der den linken operanden um den wert vom rechten operanden nach rechts im RAM verschiebt?
undfile.get() == ':'prüft, ob mein ifstream-Objekt ein : eingelesen hat?
-
@out: Sorry, hab nicht richtig gelesen.
Btw,
if( std::istream::traits_type::to_char_type(is.get()) != C )ist völlig verkehrt. Man müsste C zu einem int_type promoten und dann eq_int_type aufrufen.
-
Satan schrieb:
@out: Sorry, hab nicht richtig gelesen.
Ok (sollte für sone sein, damit er es kopieren kann und sieht, dass es nicht geht)
file >> val1Nein, mit "rechtschieben" hat das hier nichts zu tun. Das hier ist der "extraction operator". Hierbei wird eine Ganzzahl eingelesen und sofort darauf wird geprüft, ob das einlesen erfolgreich war (2 Fliegen mit einer Klatsche schlagen). Dieser Operator verwirft standardmäßig Whitespaces.
file.get()Liest einfach das nächste im Stream stehende Zeichen. (z.B. Leerzeichen oder Doppelpunkt, je nachdem, was eben das nächste Zeichen ist)
-
Satan schrieb:
@Nathan: Was ist mit
"1 : 2"(beachte das Leerzeichen)?Das Format war N:X, wie der TE später korrigierte.
Bei einem Format N:X erwarte ich auch nur Zahl, Zeichen ':', Zahl. Notfalls schiebt man noch ws zwischen.
-
Satan schrieb:
Nur schade, dass das sentry bei istream.get() auf noskipws gesetzt wird.
Kurz: Die werden ausgelesen.Na jut. Stimmt, das hätte ich wissen müssen - ist schließlich unformatiert. Hatte in Erinnerung dass es eben erst das nächste "signifikante" Zeichen ausliest.
Ich weiß gar nicht, wieso ich überhaupt Traits benutzt hab'. Hab beim schreiben noch gedacht, ich hab den char-Typ generisch gemacht - hab ich gar nicht... (?)
Daher etwas einfacher:#include <iostream> #include <vector> #include <utility> #include <sstream> template<char C> class Char { friend std::istream& operator>>( std::istream& is, Char const& c ) { if( is.get() != C ) is.setstate( std::ios_base::failbit ); return is; } }; int main() { std::vector<std::pair<unsigned, unsigned>> mData; std::istringstream stream("4 : 5\n" "78: 9"); unsigned a, b; while( stream >> a >> std::ws >> Char<':'>() >> b ) mData.push_back({a, b}); for( auto const& p : mData ) std::cout << p.first << ':' << p.second << '\n'; }Soll das Format tatsächlich so akribisch genau geparst werden, muss man halt nur die Leerzeichen entfernen.
Das ginge so:std::istream& spaces( std::istream& is ) { std::istream::sentry s(is, true); if( s ) while( is.rdbuf()->sgetc() == ' ' ) is.rdbuf()->sbumpc(); return is; } // .... unsigned a, b; while( stream >> std::noskipws >> a >> spaces >> Char<':'>() >> spaces >> std::noskipws >> b ) { mData.push_back({a, b}); stream >> Char<'\n'>(); }
-
Satan schrieb:
Btw,
if( std::istream::traits_type::to_char_type(is.get()) != C )ist völlig verkehrt. Man müsste C zu einem int_type promoten und dann eq_int_type aufrufen.
Ne, es ist - wie ich bei einem überraschten zweiten Blick sah - überflüssiger Blödsinn. Man kann natürlich direkt vergleichen. Ich dachte wie gesagt, ich hätte Char vom Zeichentyp unabhängig gemacht, da wären Traits vielleicht notwendig gewesen.
-
Ich habe mich wieder informiert, doch nichts verstädnlich/nützliches gefunden.
gibt es einen paramter bei getline, oder eine methode bei ifstream, die es mir erlaubt nur duie letzte zeile auszulesen.
ich habe auch schon die zeilenanzahl ausgelesen.
-
Mapper schrieb:
gibt es einen paramter bei getline, oder eine methode bei ifstream, die es mir erlaubt nur duie letzte zeile auszulesen.
Wenn du die Zeilenzahl hast, und bis zur letzten Zeile vorrücken willst, dann nimm
istream::ignore.
Von da wie gewohnt lesen.
-
Ich habe hier mal einen ganz anderen Weg, um die letzte Zeile einer Datei zu lesen:
int main() { string test( "abc\ndef\nghi\nhallo welt!" ); istringstream file( test ); string last_line; file.seekg( 0, std::ios_base::end ); file.unget(); for( char c=0; file>>std::noskipws>>c && c!='\n'; last_line.push_back(c) ) { file.unget(); file.unget(); } std::reverse( last_line.begin(), last_line.end() ); cout << last_line << '\n'; }Was haltet ihr davon? Ist das ineffizient? Falls ja, was wäre ein effizienter Weg, die letzte Zeile zu lesen?
-
Ich hätte soetwas verwendet:
#include <iostream> #include <string> int main() { std::istream file(std::cin.rdbuf()); std::string last_line; while (std::getline(file, last_line)) ; std::cout << last_line; }Aber ich glaube, das ist zu banal. :p
Außerdem ist outs Lösung effizienter.Edit: Das reverse Lesen ist aber schon ein wenig unhandlich. Sone, schreib' mal nen streambuf der rückwärts liest.

-
#include <iostream> #include <sstream> #include <iterator> int main() { std::istringstream file( "abc\nbcd\nhallo welt!" ); file.seekg( 0, std::ios_base::end ); char c; do c = file.rdbuf()->sungetc(); while( c != '\n' && c != std::char_traits<char>::eof() ); std::string last_line; if( c == '\n' ) last_line.assign( ++std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() ); std::cout << last_line << '\n'; }
Edit:
Nathan schrieb:
Sone, schreib' mal nen streambuf der rückwärts liest.

Ohne Witz, ich hab das nicht gelesen! Ich hab das gerade erst entdeckt!
