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 aus

    vector<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;
    }
    

  • Mod

    #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.


  • Mod

    .. 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


  • Mod

    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?


  • Mod

    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.


  • Mod

    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.


Log in to reply