frage zu ifstream



  • hoffe jemand hat eine idee zu folgendem problem:

    ich moechte eine text-datei einlesen via ifstream.
    hierbei moechte ich gerne wissen in welcher zeile ich mich gerade befinde.

    allerdings das durch

    ...
     ifstream inFile;
     inFile.open( "C:\\testfile.txt" );
     if (!inFile)
      return eErrFileOpen;
    
     // file could be opened .. let the parser do the rest 
     char c                  = 0;
     const ushort maxTokLen  = 100;
     char strTok[maxTokLen]; 
     memset(strTok,0,maxTokLen); 
     strTok[0]='\0';
     ulong cnt=0;
     while( ! (inFile.eof() || 
               inFile.bad() || 
               inFile.fail()) )
      {
    //  inFile.get(c);  
        inFile >> strTok; 
        doSomeThing(strTok);
    //    if( c=='\n' )
    //      cnt++;
      }
     inFile.close();
    

    Hierbei wird die datei geparst und fuers die analyse string-token-weise gelesen und komfortablerweise die newlines ueberlesen. allerdings fuer ich gerne
    wissen wollen in welcher zeile ich gerade einlese ohne dabei ein get(char) aufrufen zu muessen (file-pointer wird verschoben und muesste wieder rueckgesetzt werden etc.)

    hat jemand eine idee wie man das einlesen mit dem stream operator >> macht aber trotzdem herausbekommt in welcher zeile man gerade ist ohne ein get(char) !?!?!?

    vielen dank.


  • Mod

    Wenn Zeilen für dich wichtig sind, lies doch einfach zeilenweise ein, mittels getline. Die Zeile kannst du dann in einen stringstream packen, der die gleichen Operatoren kennt wie istream, wodurch du den Rest des Parsers unverändert lassen kannst*.

    *: Ich würde an deiner Stelle des rest des Parsers aber trotzdem nochmal überdenken. std::string ist dir bekannt? Und das hier:

    while( ! (inFile.eof() ||
               inFile.bad() ||
               inFile.fail()) )
      {
    

    Kann man auch so schreiben:

    while( inFile )
      {
    


  • pepe75 schrieb:

    hat jemand eine idee wie man das einlesen mit dem stream operator >> macht aber trotzdem herausbekommt in welcher zeile man gerade ist ohne ein get(char) !?!?

    Ja - die C++ iostreams sind sozusagen prädistiniert, solche Probleme zu lösen. Zwischen der Schnittstelle des von Dir verwendeten Streams (in Deinem Fall ifstream inFile) und dem darunter liegendem Streambuffer kann man einen Filter installieren durch den so zu sagen alle Zeichen einmal durch müsen. Dort zählst Du einfach die LFs.

    Ein solcher Filter könnte so aussehen:

    #include <iostream>
    
    class LineCounter : public std::streambuf
    {
    public:
        explicit LineCounter( std::istream& in )
            : m_in( in ) // istream merken, um später den Filter zu entfernen
            , m_target( in.rdbuf() )
            , m_nr( 1 ) // Start mit 1.Zeile
        {
            m_in.rdbuf( this );
        }
        ~LineCounter()
        {
            m_in.rdbuf( m_target ); // entfernt den Filter
        }
        int Nr() const { return m_nr; }
    
    protected:
        virtual int_type underflow()
        {
            return m_target->sgetc();
        }
        virtual int_type uflow()
        {
            static const int_type LF = traits_type::to_int_type( '\n' );
            const int_type m = m_target->sbumpc();
            if( traits_type::eq_int_type( m, LF ) )
                ++m_nr;
            return m;
        }
    private:
        // -- darf nicht kopiert werden, wg. Umschalten des Streambuf (s.o.)
        LineCounter( const LineCounter& );
        LineCounter& operator=( const LineCounter& );
    
        // --   Member
        std::istream& m_in;
        std::streambuf* m_target;
        int m_nr;
    };
    

    Die Anwendung sieht dann so aus, dass vor Zeile 7 Deines Programms einfach ein Objekt 'LineCounter' eingefügt wird.

    LineCounter lc( inFile );
    // file could be opened .. let the parser do the rest
    

    Ab da lässt sich mit Hilfe von

    int line = lc.Nr();
    

    jederzeit die Zeile abfragen, in der sich der Lesezeiger des Files gerade befindet.

    Wichtig: Der LineCounter muss 'out of Scope' gehen, d.h. gelöscht werden, bevor das File geschlossen wird. Im Destruktor des LineCounter wird der vorherige Streambuf des fstreams wieder hergestellt und das ist notwendig um das close auszuführen. Das erreichst Du am einfachsten dadurch, indem Du das close() in Zeile 24 weglässt. Das File wird in seinem Destruktor sowieso geschlossen und das geschieht dann erst sicher nach dem Destruktor von LineCounter.

    Übrigens würde ich Dir in Zeile 10 dringend einen std::string empfehlen.
    Das 'vorher abgefrage' vorm Lesen eines Wertes ist auch überflüssig. Besser erst lesen und dann fragen, ob es gut gegangen ist. Etwa:

    for( std::string strTok; inFile >> strTok; )
        {
            doSomeThing(strTok); // oder ggf.: doSomeThing( strTok.c_str() );
            cout << "aktuelle Zeile ist " << lc.Nr() << endl;
        }
    

    :xmas2: Werner


Anmelden zum Antworten