Problem beim Einlesen einer Datei: string in Variable



  • Hallo,

    ich habe ein Problem beim Einlesen einer Datei.
    Die Datei sieht folgendermaßen aus:

    # Irgendein Text
    # Irgendein Text
    # Irgendein Text
    #
    #  a        b         c          d         e
    #
    # Fall1
      100.00    123.456   0.000000   0.1234    off
    #
    # Fall2
      99.00     234.567   0.000000   0.2345    off
    # Fall3
      102.00    345.678   0.000000   0.3456    off
    # Fall4
      98.00     456.789   0.000000   0.4567    on
    # Fall5
      104.00    567.890   0.000000   0.5678    off
    

    Zuerst soll genau die Zeile gefunden werden, die "on" enthält.
    Dann sollen die einzelnen Werte in dieser Zeile a, b usw. zugewiesen werden, so dass ich mit den Werten weiterrechnen kann.
    Also a = 98.00, b = 456.789, c = 0.000000 und d = 0.4567.

    Ich hab jetzt schon mal folgendes probiert:

    #include<iostream>
    #include<fstream>
    #include<sstream>
    #include<string>
    using namespace std;
    
    double getAbc()
    {
    string line;
    string a_, b_, c_, d_;
    double a, b, c, d;
    
    ifstream abc;
    abc.open ("Dateiname");
    	if (!abc)
    	cout<<"Fehler"<<endl;
    
      while (getline(abc, line))
      {
         if (line.find(" on ") != string::npos)
         {
         abc >> a_ >> b_ >> c_ >> d_;
         stringstream sstr;
         sstr << a_;
         sstr >> a;
         sstr << b_;
         sstr >> b;
         sstr << c_;
         sstr >> c;
         sstr << d_;
         sstr >> d;
         }
      }
    abc.close();
    return a;
    return b;
    return c;
    return d;
    }
    

    Das Programm findet die richtige Zeile, aber dann scheint es so nicht zu funktionieren.
    Kann mir da jemand weiterhelfen?

    Vielen Dank!



  • Du solltest Dir Zeit nehmen und ein Paar Tutorials durcharbeiten.
    Eine Funktion kann nicht einfach mehrere Werte zurück geben.
    Nach dem return a werden die anderen returns niemals ausgeführt.

    Wenn Du mehrere Werte zurück geben willst, dann brauchst Du eine Struktur, Container o.ä.

    Weil ich lange Weile hab:

    bool findvalues(vector<double> &v, const string &filename)
    {
    	ifstream in(filename.c_str());
    	if(in.is_open())
    	{
    		string line;
    		while(!in.eof())
    		{
    			getline(in, line);
    			if(!line.empty() && line[0] != '#')
    			{
    				stringstream ss;
    				string e;
    				double a, b, c, d;
    				ss << line;
    				ss >> a >> b >> c >> d >> e;
    				if(e == "on")
    				{
    					v.push_back(a);
    					v.push_back(b);
    					v.push_back(c);
    					v.push_back(d);
    					return true;
    				}
    			}
    		}
    
    		in.close();
    	}
    
    	return false;
    }
    
    int main(int argc, char* argv[])
    {
    	vector<double> values;
    	if(findvalues(values, "test.txt"))
    	{
    		std::copy(values.begin(), values.end(), ostream_iterator<double>(cout, " "));
    	}
    
    	cin.get();
    	return 0;
    }
    

    P.S.: die benötigten Header darfst Du selber rausfinden 😉



  • Hallo,

    Eine Fehlerbeschreibunge wäre nich schlecht. 🙂 aber mal sehen.
    Nach dem getline liest du nochmal vom File. da ist er aber schon in der nächsten Zeile wo keine Zahlen mehr stehen. Versuchs mal so

    if (line.find(" on ") != string::npos)
    {
      stringstream sstr(line);
      sstr >> a >>b >> c >> d;
    }
    


  • Weil auch ich gerade Zeit hatte, mein Lösungsvorschlag. Ansonsten kann ich mich meinen Vorrednern nur anschließen:

    #include<iostream>
    #include<fstream>
    #include<sstream>
    #include<string>
    using namespace std;
    
    struct ABCD {
        double a;
        double b;
        double c;
        double d;
    };
    
    bool hasOn(const string& line);
    ABCD getABCDfromFile(const string& filename);
    
    int main(int argc, char** args)
        {
            ABCD foo = getABCDfromFile("test.txt");
    
            cout    << " a: " << foo.a
                    << " b: " << foo.b
                    << " c: " << foo.c
                    << " d: " << foo.d
                    << endl;
    
            return 0;
        }
    
    ABCD getABCDfromFile(const string& filename)
        {
            ifstream infile(filename.c_str());
            string line;
            ABCD abcd;
    
            while(getline(infile, line)) {
                if(hasOn(line)) {
                    stringstream stream(line);
                    stream >> abcd.a >> abcd.b >> abcd.c >> abcd.d;
                }
            }
    
            return abcd;                
        }
    
    bool hasOn(const string& line)
        { return (line.find(" on") != string::npos); }
    

    Ich habe mich hier für ein struct (und damit gegen einen std::vector<double>) entschieden, da die Struktur der Daten in der Textdatei bekannt ist (zu sein scheint) - ich hoffe einfach mal, dass ich es mit der Refaktorisierung nicht all zu sehr auf die Spitze getrieben, aber das hilft mir beim Denken.

    Grüße aus dem S(ch)auerland

    Heiko



  • Wenn du deiner struct noch einen operator>> spendierst geht das Einlesen noch leichter.



  • Braunstein schrieb:

    Wenn du deiner struct noch einen operator>> spendierst geht das Einlesen noch leichter.

    Ich bin noch nicht so lange dabei, deswegen interessiert mich der Lösungsansatz nun. Mit dem Überladen von Operatoren im Kontext der Streams habe ich mich bis dato noch gar nicht beschäftigt. Sieht aber vielversprechend aus.

    Hier mein Ansatz:

    Prototyp:

    void operator>>(istream& in, ABCD& out);
    

    Definition:

    void operator>>(istream& in, ABCD& out)
        { in >> out.a >> out.b >> out.c >> out.d; }
    

    Aufruf:

    stream >> abcd;
    

    Im Grunde war das nur ein Schuss ins Blaue - das Programm funktioniert zumindest, wie erwünscht 😃 .

    Grüße aus dem S(ch)auerland aus einer Wolkenlücke

    Heiko



  • Fast, die Signatur ist etwas anders.

    istream& operator>>(istream& in, ABCD& out);
    

    Jetzt kann man die Operatoren auch verketten. 🙂



  • <klugscheiss>Was voraussetzt, dass die Funktionsdefinition eben auch wieder eine Referenz auf istream zurück gibt...:</klugscheiss>

    istream& operator>>(istream& in, ABCD& out)
    	{
    		in >> abcd.a >> abcd.b >> abcd.c >> abcd.d;
    		return in;
    	}
    

    ... man, schon wieder was gelernt ... 💡



  • bwbg schrieb:

    <klugscheiss>Was voraussetzt, dass die Funktionsdefinition eben auch wieder eine Referenz auf istream zurück gibt...:</klugscheiss>

    Wieso hier <klugscheiss>Modus? Das war doch klar. 🙂



  • 😉 dann auch streamoperator << überladen ... man sollte auch vorher gucken ob die Zeile mit # anfängt ... das sollen ja Kommentare sein 😉

    #include <iostream>
    #include <fstream>
    #include <sstream>
    #include <string>
    
    struct values 
    {
    	double a;
    	double b;
    	double c;
    	double d;
    	values(double a = 0, double b = 0, double c = 0, double d = 0) : a(a), b(b), c(c), d(d) {}
    };
    
    std::istream& operator>>(std::istream& in, value& val)
    {
    	in >> a >> b >> c >> d;
        return in;
    } 
    
    std::ostream& operator<<(std::ostream& in, value& val)
    {
    	in << a << " "  << b << " " << c << " " << d;
        return in;
    } 
    
    values get_values_f(const std::string&);
    
    int main()
    {
    	std::cout << get_values_f("data.abc") << std::endl;
    }
    
    values get_values_f(const std::string& filename)
    {
    	std::ifstream infile(filename.c_str());
    	std::string line;
    
    	while (std::getline(infile, line)) 
    	{
    		if (line.at(0) != '#' && line.find(" on ") != std::string::npos)
    		{
    			std::ostringstream ss(line);
    			values data;
    			ss  >> data;
    			return data;
    		}
    	}
    	return values();
    }
    

    ...



  • 1.) getline ist doof
    2.) was ist eigentlich, wenn in '# Irgendein Text' ein "on" vorkommt?

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <iterator>    // istream_iterator
    #include <functional>  // mem_fun_ref
    #include <limits>      // numeric_limits
    #include <algorithm>   // find_if
    
    struct ABCD {
        double a;
        double b;
        double c;
        double d;
        bool m_on;
    
        bool On() const { return m_on; }
    };
    
    // --  Kommentar überlesen
    std::istream& comment( std::istream& in )
    {
        using namespace std;
        if( in >> ws )  // ggf. letztes Zeilenende überlesen
        {
            // --   falls das nächste Zeichen ein '#' ist, überlese die Zeile
            typedef istream::traits_type traits_type;
            while( traits_type::eq_int_type( in.peek(), traits_type::to_int_type('#') ) )
                in.ignore( numeric_limits< streamsize >::max(), '\n' );
        }
        return in;
    }
    
    // -- Einlesen von ABCD
    std::istream& operator>>( std::istream& in, ABCD& abcd )
    { 
        ABCD out;
        std::string on_off;
        if( in >> comment >> out.a >> out.b >> out.c >> out.d >> on_off )
        {
            if( on_off == "on" || on_off == "off" )
            {
                out.m_on = (on_off == "on");
                abcd = out; // lesen ok; wert übernehmen
            }
            else    // Lesefehler; 5. Wert ist weder 'on' noch 'off'
                in.setstate( std::ios_base::failbit );
        }
        return in;
    }
    // --   Ausgeben von ABCD
    std::ostream& operator<<( std::ostream& out, const ABCD& abcd )
    {
        return out << abcd.a << " " << abcd.b << " " << abcd.c << " " << abcd.d;
    }
    
    int main()
    {
        using namespace std;
        ifstream quelle( "test.txt" );
        if( !quelle.is_open() )
        {
            cerr << "Fehler beim Oeffnen der Datei" << endl;
            return -1;
        }
        istream_iterator< ABCD > i = find_if( istream_iterator< ABCD >( quelle ), istream_iterator< ABCD >(),
            mem_fun_ref( &ABCD::On ) );
        if( i != istream_iterator< ABCD >() )
            cout << "Gefunden: " << *i << endl;
        return 0;
    }
    

    Gruß
    Werner



  • Zu 2.) Dann ist line.at(0) == '#' ...

    Hast aber natürlich recht ... so geht es auch ... ist aber etwas umständlicher ... hast natürlich noch nen bissel mehr Fehlerbehandlung drin ...


Anmelden zum Antworten