String zersplitten
-
Hallo,
Ich hab folgendes Problem ich lese eine Datei ein die so aussieht
10 ANTARCTICA 0.000 0.000
12 ALGERIA 0.000 0.000
16 AMERICAN SAMOA 0.000 0.000
geht auch nur leider verschiebt sich alles wenn in dem ländernamen ein leerzeichen ist. hat wer ne Idee?
Meine Funktion sieht so ausvector<country> read(string filename) { string line; country s; istringstream lin; vector<country> table; ifstream myfile(filename.c_str()); if (myfile.is_open()) { myfile.ignore(numeric_limits<streamsize>::max(), '\n'); // erste Zeile überspringen myfile.ignore(numeric_limits<streamsize>::max(), '\n'); myfile.ignore(numeric_limits<streamsize>::max(), '\n'); while (getline(myfile, line)) { lin.clear(); lin.str(line); lin >> s.isonum >> s.countryname >> s.requirement >> s.consumptive; table.push_back(s); } myfile.close(); } else{ cerr << "Unable to open file: " << filename << endl; exit (EXIT_FAILURE); } return table; }
-
#include <iostream> struct country { unsigned A; std::string name; float B, C; }; std::istream& operator>>( std::istream& is, country& c ) { is >> c.A >> std::ws; c.name.clear(); for( char ch; is.get(ch) && (ch >= 'A' && ch <= 'Z' || ch == ' ') ; ) c.name.push_back( ch ); c.name.erase( c.name.find_last_not_of(' ') + 1 ); return is >> c.B >> c.C; } #include <vector> std::vector<country> parse( std::istream& is ) { std::vector<country> rval; for( country tmp; is >> tmp; ) rval.emplace_back( std::move(tmp) ); return rval; } #include <sstream> int main() { std::istringstream stream{ R"(10 ANTARCTICA 0.000 0.000 12 ALGERIA 0.000 0.000 16 AMERICAN SAMOA 0.000 0.000)" }; for( auto i : parse(stream) ) std::cout << i.name << '\n'; }
Edit: Die Schleife lies sich natürlich stark vereinfachen (bzw. verkürzen).
-
Hallo Onubub,
ich habe einen ähnlichen Weg gewählt wie Arcoth, nur mit dem Unterschied, dass ich alles als Zeichen eines Landes akzeptiere außer eben Zeichen die in einem nummerischen Ausdruck vorkommen.
Das Hauptprogramm ist noch strait forward:
#include <fstream> #include <iostream> #include <string> struct Entry { int nr_; std::string land_; double x_,y_; }; std::istream& operator>>( std::istream& in, Entry& e ); std::ostream& operator<<( std::ostream& out, const Entry& e ); int main() { using namespace std; ifstream file("input.txt"); if( !file.is_open() ) { cerr << "Error open file" << endl; return -2; } for( Entry e; file >> e; ) cout << e << endl; if( file.eof() ) cout << "Ok" << endl; return 0; }
Die Einlese- und Ausgaberoutine für einen Eintrag (Entry):
template< typename E, typename Traits, typename Pred > std::basic_istream< E, Traits >& get_text( std::basic_istream< E, Traits >& in, std::basic_string< E, Traits >& txt, Pred end ); std::istream& operator>>( std::istream& in, Entry& e ) { const std::ctype< char >& ctype = std::use_facet< std::ctype< char > >( in.getloc() ); return get_text( in >> e.nr_, // Nummer lesen e.land_, [&ctype]( char c )->bool { return ctype.is( std::ctype_base::digit, c ) || c == '.' || c == '-' || c == '+'; } ) // Land bis Zahlenzeichen >> e.x_ >> e.y_; } std::ostream& operator<<( std::ostream& out, const Entry& e ) { return out << e.nr_ << ") [" << e.land_ << "] " << e.x_ << ";" << e.y_; }
Die Hauptarbeit steckt jetzt in der Funktion '
get_text
', die einen Text liest, bis eine Bedingung erfüllt ist, die über das mitgegebene Prädikat 'pred
' vorgegeben ist.template< typename E, typename Traits, typename Pred > std::basic_istream< E, Traits >& get_text( std::basic_istream< E, Traits >& in, std::basic_string< E, Traits >& txt, Pred end ) { std::basic_istream< E, Traits >::sentry ok( in ); if( ok ) { std::ios_base::iostate state = std::ios_base::goodbit; try { std::basic_string< E, Traits > tx; std::basic_string< E, Traits >::size_type trim_pos = 0; const std::ctype< E >& ctype = std::use_facet< std::ctype< E > >( in.getloc() ); for( Traits::int_type m = in.rdbuf()->sgetc(); ; m = in.rdbuf()->snextc() ) { if( Traits::eq_int_type( m, Traits::eof() ) ) { state |= std::ios_base::eofbit; break; } const E c = Traits::to_char_type( m ); if( end( c ) ) break; tx.append( 1, c ); if( !ctype.is( std::ctype_base::space, c ) ) trim_pos = tx.length(); } tx.erase( trim_pos ); txt = std::move( tx ); } catch( ... ) { state |= std::ios_base::badbit; if( in.exceptions() & std::ios_base::badbit ) throw; } in.setstate( state ); } return in; }
Ausgabe bei dem von Dir angegebenen Beispiel ist:
10) [ANTARCTICA] 0;0 12) [ALGERIA] 0;0 16) [AMERICAN SAMOA] 0;0 Ok
Gruß
Werner
-
Danke euch beiden ich teste gerade Arcoth version doch move kann er nicht finden?
Ich nutze MinGW.
error: 'class std::vector<country>' has no member named 'emplace_back'
error: 'move' was not declared in this scope
In function 'std::vector<country> read(std::string)':
'auto' will change meaning in C++0x; please remove it [-Wc++0x-compat]
'i' does not name a type
-
Wenn der halbwegs aktuell ist musst du noch den C++11 Modus einschalten. Der Parameter ist -std=c++0x
-
Ja danke, hat geklappt.
Ich hab nur das Problem jetzt das er wenn er 12 ALGERIA 2317065.750 5709.162 sowas einließt das nur 317065.750 die eingelesen wird
-
Onubub schrieb:
Ja danke, hat geklappt.
Ich hab nur das Problem jetzt das er wenn er 12 ALGERIA 2317065.750 5709.162 sowas einließt das nur 317065.750 die eingelesen wird
.. dann probiere doch mal meine Variante.
-
.. dann probiere doch mal meine Variante.
Deine Variante ist etwas overengineered... aber das weißt du doch?
Der Fehler ist schnell gefunden, das erste Zeichen das nicht in den Namen passt wird mitextrahiert.
Der überladene Operator muss angepasst werden:
std::istream& operator>>( std::istream& is, country& c ) { is >> c.A >> std::ws; c.name.clear(); // Edit: Für die Schleife muss ich mich entschuldigen. for( char ch = is.peek(); is && (ch >= 'A' && ch <= 'Z' || ch == ' ') ; ch = is.peek() ) { c.name.push_back( ch ); is.ignore(); } c.name.erase( c.name.find_last_not_of(' ') + 1 ); return is >> c.B >> c.C; }
-
die bekomme ich gar nicht zum laufen da bekomm ich 100 fehler
-
Onubub schrieb:
die bekomme ich gar nicht zum laufen da bekomm ich 100 fehler
Das ist äußerst merkwürdig, denn diese Funktion ist mit allen Standards kompatibel. Was für Fehler bekommst du denn?
Edit: Er meinte natürlich Werners Variante...
-
Ja genau.
Deine Variante läuft jetzt einwandfrei! Danke.
Ich hab jetzt noch ne andere Datei da kommen statt Leerzeichen , Leerzeichen und dann tabs und dann leerzeichen. gibts da ne möglichkeit das das Programm beides kann?
-
Onubub schrieb:
die bekomme ich gar nicht zum laufen da bekomm ich 100 fehler
Es fehlen ein paar typename
template< typename E, typename Traits, typename Pred > std::basic_istream< E, Traits >& get_text( std::basic_istream< E, Traits >& in, std::basic_string< E, Traits >& txt, Pred end ) { typename std::basic_istream< E, Traits >::sentry ok( in ); if( ok ) { std::ios_base::iostate state = std::ios_base::goodbit; try { std::basic_string< E, Traits > tx; typename std::basic_string< E, Traits >::size_type trim_pos = 0; const std::ctype< E >& ctype = std::use_facet< std::ctype< E > >( in.getloc() ); for( typename Traits::int_type m = in.rdbuf()->sgetc(); ; m = in.rdbuf()->snextc() ) {
-
Onubub schrieb:
Ja genau.
Deine Variante läuft jetzt einwandfrei! Danke.
Ich hab jetzt noch ne andere Datei da kommen statt Leerzeichen , Leerzeichen und dann tabs und dann leerzeichen. gibts da ne möglichkeit das das Programm beides kann?
Meine overingenierte Variante kann das sofort, wenn Du die
typename
's noch einfügst, so wie camper es beschrieben hat.camper schrieb:
Onubub schrieb:
die bekomme ich gar nicht zum laufen da bekomm ich 100 fehler
Es fehlen ein paar typename
.. das ist das Kreuz mit dem Compiler vom Visual Studio, der ist da großzügig.
-
Auf die Erfinder und Standardisierer der C++ Streams warten ein paar ganz besondere Qualen in der Hölle.
-
Moment mal! Das kann meine Variante auch, man muss es nur so anpassen:
for( char ch = is.peek(); is && ch != '+' && ch != '-' && ch != '.' && !std::isdigit(ch) ; ch = is.peek() ) { c.name.push_back( ch ); is.ignore(); }
Edit: Das war die falsche.