Datei zeilenweise einlesen



  • Wie mache ich in C++11 eine Schleife, die einen ifstream zeilenweise in einen string einliest, eine Bearbeitungsfunktion auf die Zeile im string anwendet, und dann in einen ofstream die bearbeitete Zeile ausgibt?

    Ich dachte eigentlich an getline , aber da muss man eine Länge angeben, ich will aber beliebig lange Zeilen haben.



  • Ich habe es jetzt selbst gelöst:

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <algorithm>
    using namespace std;
    
    int main()
    {
    	string filename;
    	cout << "Dateiname: ";
    	cin >> filename;
    
    	ifstream input(filename);
    	ofstream output(filename + ".txt");
    
    	size_t lines = 0;
    	while (!input.eof())
    	{
    		string line;
    		getline(input, line);
    
    		auto comment = find(line.begin(), line.end(), '%');
    		string new_line(line.begin(), comment);
    
    		output << new_line << "\r\n";
    
    		++lines;
    	}
    	cout << "Done! " << lines << " lines written.\n";
    	char c;
    	cin >> c;
    }
    

    Das Problem ist, dass er nach jeder Zeile jetzt eine zusätzliche Leerzeile macht. Wie behebe ich das?



  • OK, auch gelöst: Nicht "\r\n", sondern "\n" schreiben!



  • Deine Programmlogik ist aber falsch (in Pascal wäre sie richtig).

    Der Test auf eof gehört in C++ ans Ende, d.h. man muss erst versuchen zu lesen und kann erst danach sinnvoll auf eof testen. Stattdessen sollte man testen, ob der stream nach dem Lesen noch im guten Zustand ist (d.h. ob er im bool-Kontext true ist).

    Also sollte die Schleife so sein:

    ifstream input(filename);
    if (!input.is_open()) {
        // Fehler: "datei geht nicht auf"
    }
    string line;
    while (getline(input, line)) {
        // mach was mit line
    }
    if (!input.eof()) {
        // irgendwas ist schief gelaufen, hier
        // sollte ich am Ende der Datei sein
    }
    

    Des weiteren ist die Kopie der Zeile bis zum Suchzeichen unnötig. Schreib einfach den relevanten Bereich.



  • So?

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <algorithm>
    using namespace std;
    
    int main()
    {
    	string filename;
    	cout << "Dateiname: ";
    	cin >> filename;
    
    	ifstream input(filename);
    
    	if (!input.is_open())
    	{
    		cout << "Datei nicht gefunden!";
    		char c;
    		cin >> c;
    		return -1;
    	}
    
    	ofstream output(filename + ".txt");
    
    	size_t lines = 0;
    	string line;
    	while (getline(input, line))
    	{
    		auto comment = find(line.begin(), line.end(), '%');
    		string new_line(line.begin(), comment);
    
    		if (new_line.empty() && !line.empty())
    			continue;
    		output << new_line << "\n";
    
    		++lines;
    	}
    
    	if (!input.eof())
    	{
    		cout << "!input.eof()";
    	}
    
    	output.close();
    	input.close();
    	cout << "Done! " << lines << " lines written.\n";
    	char c;
    	cin >> c;
    }
    


  • wob schrieb:

    Des weiteren ist die Kopie der Zeile bis zum Suchzeichen unnötig. Schreib einfach den relevanten Bereich.

    Wie mache ich das?



  • *push*



  • Gibt mehrere Möglichkeiten. Auf die Schnelle fallen mit 4 ein:

    a) string_view statt string nehmen - du änderst den Substring ja nicht.

    b) Du kannst einfach bei cout.write die Länge zu schreibender Zeichen angeben:

    std::cout.write(line.c_str(), std::distance(line.begin(), comment));
    

    Alternativ kann man gleich mit string.find einen size_t bekommen für die Position des '%'. Allerdings muss man dann mit string::npos aufpassen.

    c) Du schreibst einfach an die Stelle des '%' ein '\0' und gibst den c_str aus. Das geht hier, weil du Textdateien hast, in denen kein '\0' vorkommt.

    d) Solange Zeichen lesen + schreiben, bis '%' gefunden. Danach ein is.ignore(numeric_limits<streamsize>::max(), '\n') .

    Deinen Check auf komplette Kommentarzeilen musst du bei b) bis d) dabei jeweils anpassen.

    Das wird bei dir (vermutlich kleine Textdateien) aber wahrscheinlich alles keinen relevanten Unterschied zu deiner letzten Version machen.



  • Ich bevorzuge die Variante a) mit dem string_view . Leider gibt es den in VC++2015 noch nicht, oder?



  • Wie mache ich das mit boost::string_view ?


Anmelden zum Antworten