Zeilen zählen einer Datei



  • Ich hab folgendes Problem. Ich möchte zufällig eine bestimmte Zeile aus einer Datei haben und diese mit einer Methode zurükgegeben. Damit ich zufällig eine Zeile auswählen kann, muß ich vorher die Anzahl der Zeilen zählen lassen, da der Inhalt der Datei variabel bleiben soll.(es können also mal mehr, mal weniger zeilen sein)
    Wenn ich eine bestimmte Zeile auswähle, gibt er mir auch etwas zurück. Sobald ich die Schleife einbaue, in der er die Zeilen zählen soll bricht mir das Programm ohne richtiger Fehlermeldung ab. Windows sagt mir die Programm-EXE funktioniert nicht mehr.

    Hier zur Analyse den Code:

    char *Mannschaft::readNameOfFile(){
    	srand ( TimeSeed()+zaehler++);
    	char zeile[250];
    	char *name; //Hilfsvariable für return
    	int i=1; //Zählvarible zum höchzählen ausgewählterZeile
    	int zz=1; //Zeilenzähler
    	int znr=0; //Random errechnete Zeilennummer
    	char datei[] = "Data/spielernamen.txt";
    	ifstream eingabe(datei, ios::in); //datei zum lesen öffnen
    	if (eingabe.good()){
    		while (!eingabe.eof()){
    			eingabe.getline(zeile,250);
    			zz++; //Zeilen zählen
    			}
    		eingabe.seekg(0L, ios::beg); //An stream(File)-Anfang springen
    		znr=1 + ( rand() % zz);	//Randomzeile auswählen (aus max Anzahl Zeilen)	
    		while (!eingabe.eof()){
    			eingabe.getline(zeile,250);
    			i++;
    			if (i==znr+1){
    				name = new char[strlen(zeile)+1];
    				strcpy(name, zeile);
    				eingabe.seekg(0L, ios::end); //ans File-Ende springen
    			}
    		}		
    	}
    	else cout << "\ndateifehler oder datei nicht gefunden!" << endl;
    	return name;
    }
    

    wenn noch irgendwas benötigt wird, gebt bescheid. Vermutlich ist es was einfaches und ich komm wieder nicht drauf. 😞

    Schöne Grüße



  • //...
    std::ifstream reader(datei);
    size_t lines = 0;
    while(reader.ignore(200, '\n'))
    {
        ++lines;
    }
    //...
    

    ifstream::eof() macht nicht, was Du erwartest. Es kann sein, dass über das Dateiende hinaus gelesen wird.



  • #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    using namespace std;
    
    vector<string> lines;
    ifstream in(datei);
    istream_iterator<string> iin(in), ieof();
    copy(iin, ieof, back_inserter(lines));
    
    cout << lines.size();
    


  • Michael E. schrieb:

    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    using namespace std;
    
    vector<string> lines;
    ifstream in(datei);
    istream_iterator<string> iin(in), ieof();
    copy(iin, ieof, back_inserter(lines));
    
    cout << lines.size();
    

    Kannst du das vielleicht erkäutern? 😕



  • Michael E. schrieb:

    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    using namespace std;
    
    vector<string> lines;
    ifstream in(datei);
    istream_iterator<string> iin(in), ieof();
    copy(iin, ieof, back_inserter(lines));
    
    cout << lines.size();
    

    Wo ist hier end() und begin() ?

    Etwas sehr umständlich und unübersichtlich, dafür das wir nur ide zeilenanzahl in einer Datei wollen..



  • Michael E. schrieb:

    #include <vector>
    #include <iostream>
    #include <algorithm>
    #include <iterator>
    using namespace std;
    
    vector<string> lines;
    ifstream in(datei);
    istream_iterator<string> iin(in), ieof();
    copy(iin, ieof, back_inserter(lines));
    
    cout << lines.size();
    

    Das zählt aber nicht, sondern liest ein.



  • Jesus Christ schrieb:

    Kannst du das vielleicht erkäutern? 😕

    copy aus <algorithm>: http://cplusplus.com/reference/algorithm/copy/
    istream_iterator aus <iterator>: http://cplusplus.com/reference/std/iterator/istream_iterator/

    Und damit dürfte auch schon klar sein, wo hier begin und end versteckt wird, denn iin ist ein Iterator, der auf den Anfang der Datei zeigt, und ieof ist ein Iterator, der hinter die Datei zeigt.

    Ach ja, back_inserter: http://cplusplus.com/reference/std/iterator/back_inserter/

    end_begin schrieb:

    Etwas sehr umständlich und unübersichtlich, dafür das wir nur ide zeilenanzahl in einer Datei wollen..

    Ne, der OP braucht die Zeilen ja nachher noch, die liegen jetzt alle getrennt im Vektor.

    Tachyon schrieb:

    Das zählt aber nicht, sondern liest ein.

    Es tut sogar beides und beides braucht der OP (jaja, es sei denn, man greift für jede einzelne, nachher benötigte Zeile wieder auf die Datei zurück).


  • Administrator

    @Michael E.,
    Dein Code liest nicht die Zeilen ein, sondern die Wörter. Lies mal den Link, welchen du selber hier reingestellt hast:
    http://www.cplusplus.com/reference/std/iterator/istream_iterator/

    Dort steht es wunderbar:

    ... it extracts an element (with >>) from the stream.

    Wenn man ein Element std::string über >> einliest, liest man nur ein Wort und nicht die ganze Zeile ein.

    Es gibt daher eine einfache und eine komplexere Methode:

    std::string line;
    std::ifstream reader("...");
    std::vector<std::string> lines;
    
    while(std::getline(reader, line))
    {
      lines.push_back(line);
    }
    

    Man kann auch auf den std::vector lines verzichten, falls man an den Zeilen nicht interessiert ist und nur über einen Counter zählen möchte.

    Die komplexere:

    template<typename CharT, typename TraitsT = std::char_traits<CharT>, typename AllocT = std::allocator<CharT> >
    struct Line
    {
      typedef std::basic_string<CharT, TraitsT, AllocT> StringType;
    
      StringType data;
    
      operator StringType const&() const
      {
        return data;
      }
    }
    
    template<typename CharT, typename TraitsT, typename AllocT>
    std::basic_istream& operator >>(std::basic_istream<CharT, TraitsT>& in, Line<CharT, TraitsT, AllocT>& line)
    {
      return std::getline(in, line.data);
    }
    
    // ...
    
    std::vector<std::string> lines;
    std::ifstream in("...");
    std::istream_iterator<Line<char> > iin(in), ieof;
    std::copy(iin, ieof, back_inserter(lines));
    

    Kann man natürlich vereinfach und erweitern. Hat mehr Möglichkeiten. Im gesamten für diesen Fall aber wohl überdimensioniert 😉

    Grüssli



  • Dravere schrieb:

    @Michael E.,
    Dein Code liest nicht die Zeilen ein, sondern die Wörter.

    Das stimmt, tut mir leid, falsch in Erinnerung.

    Aber warum wird hier so gekonnt ignoriert, dass zumindest ein Teil der Zeilen nachher gebraucht wird?


  • Administrator

    Michael E. schrieb:

    Aber warum wird hier so gekonnt ignoriert, dass zumindest ein Teil der Zeilen nachher gebraucht wird?

    Wo wird hier gekonnt ignoriert? Es war eine Person und zwar der Tachyon, der dir mit den Zeilen widersprochen hat.
    Und ich denke mal, dass Tachyon sich am Titel orientiert hat und so die Aufgabenstellung wahrgenommen hat. Es kann durchaus seine Gründe haben, wieso man die Zeilen nicht speichern möchte, wenn man nur eine davon möchte. Zum Beispiel bei verdammt grossen Files, wo man nicht den ganzen Hauptspeicher zumüllen will, bzw. womöglich nicht mal genügend Platz hat im Hauptspeicher.

    Ich zweifle zwar ein wenig daran, dass dies hier wirklich ein Problem ist. 😉

    Grüssli



  • Ich wollte nur eine zufällige Zeile, den Rest brauchte ich nicht.

    Ich hab nach Vorschlägen von Dravere und Beistand eines Kollegen diese Variante genommen und dies funktionierte dann. Danke an alle nochmal. 🙂

    int zz =0;
    	string line;
    	ifstream reader(dateiname);
    	while(std::getline(reader, line)){
    		zz++;
    	}
    

Log in to reply