Textdatei einlesen und damit weiterrechnen



  • Hallo Leute,
    ich habe ein Problem und hoffe, dass ihr mir helfen könnt.
    Ich habe so eine Textdatei:
    0.1080 1.0781
    0.1090 1.5938
    0.1100 2.2500
    0.1110 2.8125
    0.1120 3.3750
    0.1130 3.9844
    0.1140 4.5938
    0.1150 5.0625
    0.1160 5.5313
    0.1170 6.0000
    0.1180 6.3750
    0.1190 6.8438
    0.1200 7.2188
    0.1210 7.6875
    0.1220 8.0625
    0.1230 8.4844
    0.1240 8.6250
    0.1250 8.9531
    0.1260 9.0938
    0.1270 9.1875
    0.1280 9.5156
    0.1290 9.5156
    0.1300 9.6563
    0.1310 9.7031
    0.1320 9.8438
    0.1330 9.8438
    0.1340 9.9375
    0.1350 9.9844
    0.1360 9.9844
    0.1370 10.0313
    0.1380 10.0781
    0.1390 10.0313
    0.1400 10.1250
    0.1410 10.0781
    0.1420 10.1250
    0.1430 10.0313
    0.1440 10.0313
    0.1450 10.0781
    0.1460 10.1250
    0.1470 10.0781
    0.1480 10.0781
    0.1490 10.0313
    0.1500 10.1250
    0.1510 10.0781
    0.1520 10.1250
    0.1530 10.0781
    0.1540 10.0313
    0.1550 9.9844
    0.1560 9.9375
    0.1570 10.1250
    0.1580 10.0313
    0.1590 10.0313

    ich will die Zahlen einlesen und auf der rechten Seite immer aus 3 Zahlen die untereinander stehen den Mittelwert bilden. Z. B.: (1.0781+1.5938+2.2500)/3
    und dann von den nächsten 3 Zahlen den Mittelwert bilden. Jedoch hab ich keine Ahnung wie ich das machen soll....


  • Mod



  • Sry, achte ab jetzt darauf



  • Kleiner Denkanstoß:

    Dieser Code-Ausschnitt ließt deine Datei ein und parst die Werte. Damit kann man dann anschließend weiterarbeiten. Ich hoffe es hilft dir etwas, aber für mehr fehlt mir gerade die Zeit.

    #include <iostream>
    #include <vector>
    #include <string>
    #include <fstream>
    
    /*
     * [in] Eingabe-String
     * [out] die getrennten Wörter der einegebenen Zeichenkette
     * [in] der delimiter (in deinem Fall ein Leerzeichen " ")
     */
    void parseString(const std::string &string, std::vector<std::string> &tokens, const std::string &delimiter)
    {
    	size_t lastPos;
    	size_t currentPos;
    
    	tokens.clear();
    
    	lastPos = string.find_first_not_of(delimiter, 0); 		// skip delimiters at beginning
    	currentPos = string.find_first_of(delimiter, lastPos);	// find first non-delimiter
    
    	while (std::string::npos != currentPos || std::string::npos != lastPos)
    	{
    		tokens.push_back(string.substr(lastPos, currentPos - lastPos));	// add a found token to the vector
    		lastPos = string.find_first_not_of(delimiter, currentPos);		// skip delimiter
    		currentPos = string.find_first_of(delimiter, lastPos);			// find next non-delimiter
    	}
    	return;
    }
    
    int main()
    {
    	std::vector<std::string> textFile;
    	std::string fileLine;
    	std::ifstream file;
    
    	// Datei öffnen und einlesen
    	file.open("meineDatei.txt");
    	if (file.is_open() == true)
    	{
    		while (std::getline(file, fileLine))
    		{
    			textFile.push_back(fileLine);
    		}
    		file.close();
    	}
    
    	// die einzelnen Werte parsen und ausgeben
    	for (std::string::size_type i = 0; i < textFile.size(); i++)
    	{
    		std::cout << "Zeile: " << i << "   ";
    		parseString(textFile.at(i), tokens, " ");
    		for (std::string::size_type j = 0; j < tokens.size(); j++)
    		{
    			std::cout << "Wert " << j+1 << ": " << tokens.at(j) << "   ";
    		}
    		cout << std::endl;
    	}
    }
    

    Ausgabe:

    Zeile: 0   Wert 1: 0.1080   Wert 2: 1.0781   
    Zeile: 1   Wert 1: 0.1090   Wert 2: 1.5938   
    Zeile: 2   Wert 1: 0.1100   Wert 2: 2.2500 
    ....
    

    das ist jetzt quick-and-dirty. Die Werte könnte man dann in float wandeln und damit weiter arbeiten

    viele Grüße,
    SBond



  • Du könntest z.B. einen boost::circular_buffer der Größe 3 nehmen und da die rechten Zahlen reinpushen. Sobald du mindestens drei Zahlen drin hast, einfach nach dem push_back accumulate(buffer.begin(), buffer.end(), 0.0)/buffer.size() rechnen und fertig.



  • Hallo,

    die Lösungen die dir bisher angeboten wurden, schauen bereits über den Tellerrand hinaus.

    Was aber hindert dich daran, diese Variante zu wählen? (vermutlich auch besser verständlich für den ersten Anlauf)

    std::ifstream f("bla.txt");
    
    if(!f.is_open()) {
      std::cerr << "Datei laesst sich nicht oeffnen!" << std::endl;
      return;
    }
    
    while( !f.eof() ) {
      float a = 0;
      float b = 0;
      float c = 0;
    
      f >> a >> b >> c;
    
      float v = (a+b+c)/3.;
    }
    

    Viele Grüße



  • Hallo __jb__,

    das geht so nicht aus mehreren Gründen:

    Es stehen immer 2 Zahlen in einer Zeile, aber nur von den hinteren soll der Durschnitt berechnet werden.

    Man muss also etwas wie f >> dummy >> wert machen und dann den jeweiligen Wert behalten, da man ihn für bis zu 3 Berechnungen braucht.

    Außerdem ist das while (!f.eof()) ein Antipattern in C++, weil eof erst nach nicht erfolgtem Lesen wegen Fileende gesetzt wird, nicht davor! Also einlesen, dann prüfen. Und normalerweise einfach mit if (stream_variable) testen, es könnte auch was anderes als eof passiert sein.



  • @SBond: Warum zuerst als String einlesen? Besser die Werte gleich als das was sie sind einlesen: Double/Float. Zudem müssen nicht zuerst alle Werte in einen Vector gespeichert werden, sondern es können immer 3 Paare verarbeitet werden (wobei die eine Häfte dann nicht benötigt wird).



  • Es gibt natürlich mehrere und auch bessere Möglichkeiten als meine. Ich neige meistens dazu Textdateien zeilenweise als std::string einzulesen. Es geht bestimmt auch effizienter.

    Allerdings sollte der Threadsteller auch etwas Eigenleistung zeigen. Ich weiß noch nicht mal wie weit sein verständnis geht.



  • #include <fstream>
    #include <vector>
    #include <iostream>
    
    struct ValuePair
    {
    	ValuePair()
    	{}
    
    	ValuePair(double fValue, double sValue)
    		: first(fValue), second(sValue)
    	{}
    
    	double first;
    	double second;
    };
    
    std::istream& operator>>(std::istream& in, ValuePair& vp)
    {
    	in >> vp.first >> vp.second;
    
    	return in;
    }
    
    int main()
    {
    	std::ifstream file("test.txt");
    	std::vector<ValuePair> values;
    
    	if(file)
    	{
    		ValuePair vPair;
    		while(file >> vPair)
    		{
    			values.push_back(vPair);
    		}
    	}
    
    	for(const auto& value : values)
    	{
    		std::cout << value.first << " " << value.second << "\n";
    	}
    }
    


  • Vielen dank für die zahlreichen Antworten, ihr habt mir wirklich sehr weiter geholfen. 🙂
    Ich werde es dann morgen weiter versuchen und euch dann die Ergebnisse mitteilen.

    Mfg



  • SBond schrieb:

    Ich neige meistens dazu Textdateien zeilenweise als std::string einzulesen.

    Ja, dazu neigen viele. Das macht es nicht besser.

    wob schrieb:

    Außerdem ist das while (!f.eof()) ein Antipattern in C++, weil eof erst nach nicht erfolgtem Lesen wegen Fileende gesetzt wird, nicht davor! Also einlesen, dann prüfen.

    So ist es! while(!eof) ist so ziemlich in jeder Programmiersprache falsch.



  • Zu "getline ist doof":

    Ich benutze auch gerne getline. Denn dann hat man die volle Kontrolle darüber, wie eine Zeile denn nun wirklich ausgesehen hat.

    Im gegebenen Beispiel waren pro Zeile 2 Zahlen gespeichert. Wenn ich hier nur >> benutze, dann bekomme ich nicht mit, wenn 2x einer der beiden Werte fehlt.

    Wie würde ich diese Datei als fehlerhaft erkennen, wenn ich mit >> einlese? Es besteht die Gefahr, dass ich dann (5,7), (8,9) als Paare erkenne statt richtig (5, fehlt!!), (7,8) und (9, fehlt!!).

    1 2
    3 4
    5
    7 8
    9
    10 11
    

    Gut, irgendwie gehts bestimmt, aber oft ist es leichter, die ganze Zeile zu lesen und dann auf Gültigkeit zu prüfen, ggf. sogar mit einer Regexp, wenn es kompliziert ist zu sagen, wann denn nun eine Zeile fehlerhaft ist.

    Ich persönlich finde, dass es oft mit >> schwieriger ist, eine Datei richtig zu lesen als mit getline + nachgelagertem Parsen der Zeile. Ich lasse mich hier aber gerne belehren.



  • Ich hab mal ne frage auf den Beitrag von streams.
    ganz unten steht ja:

    for(const auto& value : values)
    {
    std::cout << value.first << " " << value.second << "\n";
    }

    ich versteh den Ausdruck nicht wirklich, weil ich bisher nur "normale" for-Schleifen hatte und der gibt mir an dass value initialisiert werden muss aber weiß nicht wie 😕



  • Law D Ace schrieb:

    der

    Soso, der

    Law D Ace schrieb:

    gibt mir an dass value initialisiert werden muss

    Hier ist kein Deutschunterricht. Niemand will wissen, ob du die indirekte Rede beherrschst. Copy&Paste!

    Dein Compiler muss mindestend den C++11 Standard beherrschen.



  • Mit "der" meinte ich eigentlich Visual C++ und ich meinte auch, dass ich kein const auto& value... hatte. Ich hatte bisher nur normale for-Schleifen wie z.B.: for(i=0;i<=10;i++)
    Sorry, falls du es falsch verstanden hast.



  • Hallo wob,

    wob schrieb:

    Ich benutze auch gerne getline. Denn dann hat man die volle Kontrolle darüber, wie eine Zeile denn nun wirklich ausgesehen hat.

    Im gegebenen Beispiel waren pro Zeile 2 Zahlen gespeichert. Wenn ich hier nur >> benutze, dann bekomme ich nicht mit, wenn 2x einer der beiden Werte fehlt.

    Wie würde ich diese Datei als fehlerhaft erkennen, wenn ich mit >> einlese? Es besteht die Gefahr, dass ich dann (5,7), (8,9) als Paare erkenne statt richtig (5, fehlt!!), (7,8) und (9, fehlt!!).

    1 2
    3 4
    5
    7 8
    9
    10 11
    

    hier wird ein Zeilenende (LF) als formatierendes Element benutzt. istream unterscheidet aber nicht zwischen einem Space und LF. Das sind alles White-Space-Character.

    Damit ist das von Dir vorgebrachte Beispiel auch das klassische Argument für(!) getline . Das Thema ist hier schon mehrfach dran gewesen; siehe zum Beispiel https://www.c-plusplus.net/forum/p1940874#1940874 und https://www.c-plusplus.net/forum/336498-full.
    Klar ist hier getline eine pragmatische Wahl - wohin gegen ich mich wirklich wehre ist dieser Automatismus: Textdatei lesen -> getline -> string auseinander nehmen.

    wob schrieb:

    Gut, irgendwie geht's bestimmt, aber oft ist es leichter, die ganze Zeile zu lesen und dann auf Gültigkeit zu prüfen, ggf. sogar mit einer Regexp, wenn es kompliziert ist zu sagen, wann denn nun eine Zeile fehlerhaft ist.

    Ich persönlich finde, dass es oft mit >> schwieriger ist, eine Datei richtig zu lesen als mit getline + nachgelagertem Parsen der Zeile. Ich lasse mich hier aber gerne belehren.

    Ich meine, das ist nur eine Frage der Gewöhnung bzw. des Themas, mit dem man sich eben beschäftigt. Viele Leute, die sich selbst als Anfänger bezeichnen, kennen sich manchmal ziemlich gut mit den Methoden von std::string aus. Hintergrund ist, dass sie eben aus dem String der Zeile, dann alles mit string-Methoden raus holen.
    Ich dagegen muss die Methoden von std::string immer nachschlagen, weil ich sie fast nie benötige, merke ich sie mir auch nicht. Man findet halt das 'einfach', mit dem man sich intensiver beschäftigt.

    Gruß
    Werner



  • Law D Ace schrieb:

    for(const auto& value : values)
    {
    std::cout << value.first << " " << value.second << "\n";
    }

    ich versteh den Ausdruck nicht wirklich, ...

    range-based-for-loops sind neu seit C++11. Ganz kurz in deutsch:http://www.heise.de/developer/artikel/C-11-auch-ein-Stimmungsbild-1345406.html?artikelseite=3 oder ausführlicher auf englisch: http://en.cppreference.com/w/cpp/language/range-for

    Gruß
    Werner



  • Hi Werner,

    wie geht's eigentlich dem angedachten Proposal? 😉


Log in to reply