C++ Programm bricht nach Zeilen einlesen ab

starflighter

Cadet 3rd Year
Registriert
Juni 2013
Beiträge
39
Hallo,
ich versuche gerade ein Programm zu schreiben, welches aus einer beliebig großen (String)Datei die Anzahl der Zeilen zählt, daraufhin ein zeilenweises Array erstellen soll.

Ich habe es zuerst mit einem String Vektor probiert, aber dort ging es nur wortweise, für mich damit leider unbrauchbar.

Auf jeden Fall ist es jetzt so, dass sich nach dem Zählen der Zeilen das Programm beendet, nur ich kann mir nicht erklären wieso?

Kann mich jemand wieder auf den rechten Weg bringen :rolleyes: ??

Code:
#include <iostream>
#include <fstream>
#include <string>


using namespace std;

int main()
{


    string sZeile;
    int iAnzahl = 0;
    int iZaehler = 0;

    //Datei mit Pfad
    ifstream ifsDatei("/home/user/Schreibtisch/abc.txt");

    if (!ifsDatei)
    {
      cout << "Erstmal nen gueltigen Pfad, sonst wird das NIX!" << endl;
      return 1;
    }

    else
    {
        //Zeilen zählen
        while(getline(ifsDatei, sZeile))
        {
        iAnzahl++;
        }

        //Array deklarieren
        string saTextarray[iAnzahl];

        //Datei zeilenweise in Array einlesen
        while (getline(ifsDatei, sZeile))
        {
            for (iZaehler=0; iZaehler<iAnzahl; iZaehler++)
            {
                saTextarray[iZaehler] = sZeile;
                //cout << saTextarray[iZaehler]<<endl;
            }
        }
    }
  //Programm beenden
  return 0;
}

Danke und Gruß
starflighter, der C++ Anfänger :D
 
Wenn du Dateien einliest, dann verwendet die ifstream-Klasse intern eine Variable, die angibt, wo du im Moment gerade einlesen willst.
Wenn du z.B. die ersten 20 Bytes eingelesen hast, dann wird diese Variable auf den Wert 20 gesetzt. Beim nächsten read-Befehl (oder getline) wird dann ab der Stelle weitergelesen.

Beim Zeilen zählen liest du schon die komplette Datei ein. D.h., die Leseposition ist jetzt also "ganz hinten" bei deiner Datei. Wenn du jetzt erneut mit getline() einlesen willst, dann kannst du natürlich nichts mehr einlesen (gibt ja nichts mehr zum lesen in der Datei).

Die Lösung ist, die Leseposition manuell auf 0 zurückzusetzen. Dazu hat die istream-Klasse (Die Basisklasse von ifstream) eine Methode namens "seekg": http://www.cplusplus.com/reference/istream/istream/seekg/
 
Deine Deklaration des Arrays ist falsch. Mit dem Ausdruck
Code:
string array[10];
Kannst du nur arrays deklarieren, deren Grösse schon dann bekannt ist, wenn dein C++ compiler deinen Code übersetzt. In deinem Fall steht dort aber die Variable "iAnzahl". Deren Wert kenn der Compiler aber beim übersetzen gar nocht nicht... Darum erhältst du einen runtime error, wenn dein Programm bei der Deklaration des Arrays angelangt ist.

Du musst das Array also dynamisch alloziieren. Das funktioniert so:

Code:
 string* array = NULL;
array = new string[VARIABLE];

array ist nun ein Pointer, der auf den ersten Eintrag des Arrays zeigt. Kann genau gleich verwendet werden wie ein klassisches Array.
 
Oh, das hab ich ganz übersehen. Das kommt ja auch noch dazu.

Anstatt aber ein Array mit new anzulegen (und das delete nicht zu vergessen!) würde ich lieber einen std::vector verwenden:
Code:
std::vector<std::string> array
Dann brauchst du auch den Umweg mit den Anzahl der Zeilen nicht, weil der std::vector automatisch mitwächst. Außer man hat sehr große Textdateien mit vielen Zeilen. Wenn man dann die Anzahl der Zeilen kennt, kann man entsprechend viel Platz reservieren, sodass sich der vector nicht ständig vergrößern (und damit alle Daten umkopieren) muss.
 
Nach dem C++ Standard muss die Größe eines Arrays zur Compilezeit bekannt sein.
Kann der Compiler die Größe nicht bestimmen, bricht dieser die Übersetzung mit einer Fehlermeldung ab.

Aufgrund der Pfadangabe vermute ich jedoch, dass unter Linux mit GCC übersetzt wird.
GCC bietet eine Reihe von C und C++ Erweiterungen.
Eine davon ist Arrays of Variable Length und erlaubt die Bestimmung der Arraygröße zur Laufzeit.

Daher funktioniert die Festlegung der Arraygröße im vorliegenden Programmcode.

Möchte man nur Standard C und C++ programmieren, gibt der GCC mit dem Parameter -pedantic eine Warnung bei Verwendung von Spracherweiterungen aus.
 
So ich habe es editiert, allerdings bricht es immernoch nach dem Einlesen der Zeilen ab.
Ich probiere es auch nochmal mit einem Vekor, allerdings werden hier (Jahres-)Logs eingelesen, welche groß werden können. Ist hier ein Vektor überhaupt sinnvoll?

Code:
#include <iostream>
#include <fstream>
//#include <stdexcept>
#include <string>


using namespace std;

int main()
{


    string sZeile;
    int iAnzahl = 0;
    int iZaehler = 0;

    //Datei mit Pfad
    ifstream ifsDatei("C:\\Windows\\bma\\install.ini");

    if (!ifsDatei)
    {
      cout << "Erstmal nen gueltigen Pfad, sonst wird das NIX!" << endl;
      return 1;
    }

    else
    {
        //Zeilen zählen
        while(getline(ifsDatei, sZeile))
        {
        iAnzahl++;
        //cout << iAnzahl << endl;
        }

        //Array deklarieren
        string* array = NULL;
        array = new string[iAnzahl];

        //pointer zuruecksetzen
        ifsDatei.seekg(0, ios::beg);

        //Datei zeilenweise in Array einlesen
        while (getline(ifsDatei, sZeile))
        {
            for (iZaehler=0; iZaehler<iAnzahl; iZaehler++)
            {
                array[iZaehler] = sZeile;
                cout << array[iZaehler]<<endl;
            }
        }
    }
  //Programm beenden
  return 0;
}

Danke und Gruß
starflighter
 
Streamobjekte verwenden intern Statusflags, um den Zustand ihres Datenstroms abzubilden.
Nach dem Erreichen des Endes einer Datei wird das sogenannte EndOfFile-Bit gesetzt.
Danach brechen alle weiteren Operationen auf dem Streamobjekt ab.

Um das Streamobjekt wieder verwenden zu können, musst du die Statusflags zurücksetzen.
Das geht mit ifsDatei.clear(); Dein Code sieht dann wie folgt aus:
Code:
    #include <iostream>
    #include <fstream>
    #include <string>
     
    using namespace std;
     
    int main()
    {
        string sZeile;
        int iAnzahl = 0;
        int iZaehler = 0;
     
        //Datei mit Pfad
        ifstream ifsDatei("test.cpp");
     
        if (!ifsDatei)
        {
            cout << "Erstmal nen gueltigen Pfad, sonst wird das NIX!" << endl;
            return 1;
        }
        else
        {
            //Zeilen zählen
            while(getline(ifsDatei, sZeile))
            {
                iAnzahl++;
            }
            
            //Statusflags zurücksetzen           <--------------- HIER
            ifsDatei.clear();
            
            //pointer zuruecksetzen
            ifsDatei.seekg(0, ios::beg);
            
            //Array deklarieren
            string* array = NULL;
            array = new string[iAnzahl];
     
            //Datei zeilenweise in Array einlesen
            while (getline(ifsDatei, sZeile))
            {
                array[iZaehler++] = sZeile;
            }
            
            for (iZaehler=0; iZaehler<iAnzahl; iZaehler++)
            {
                cout << array[iZaehler]<<endl;
            }
        }
        //Programm beenden
        return 0;
    }
 
Zuletzt bearbeitet:
Und wenn du new verwendest, dann brauchst du auch das passende delete bzw. delete[]! new und delete sollte man in der Regel NIE in C++ verwenden. Wenn man schon dynamisch Speicher braucht, dann nimmt man einen std::shared_ptr oder std::unique_ptr. Ich würde mit std::vector und reserve arbeiten:
Code:
std::vector<std::string> array;
array.reserve(iAnzahl); // Reservert iAnzahl Einträge -> Man spart sich späteres rumkopieren
...
array.push_back(sZeile);
Das ist idiomatischer C++-Code, kein new und delete.
 
stwe schrieb:
Ich würde mit std::vector und reserve arbeiten

Prinzipiell ja, aber in diesem Fall würde ich gleich beim ersten Mal Einlesen den Vector befüllen und nicht vorher die Zeilenanzahl bestimmen. Dadurch spart man sich das zweite Mal Einlesen der Datei.
Eine Datei ein zweites Mal einlesen ist viel langsamer als wenn gelegentlich der Vector erweitert wird.

Code:
#include <vector>
#include <iostream>

int main() {

  std::vector<int> vector;
  
  for(int i = 0; i < 1000; ++i) {
    std::cout << vector.capacity() << std::endl;
    vector.push_back(i);
  }

}

In dem obigen Testcode werden 1000 Elemente in den Vector geschrieben.
Auf meinem System wird der Vector genau 11 mal vergrößert, in den Schritten 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024.

Edit: Gibt man dem Vector mit vector.reserve(100); eine Anfangsgröße vor, verkleinert sich die Anzahl der Erweiterungen des Vectors auf die vier Schritte: 100, 200, 400, 800, 1600.
 
Zuletzt bearbeitet:

Ähnliche Themen

Zurück
Oben