Textdatei einlesen und verarbeiten



  • Hallo Forum,

    ich bin neu hier und programmiere sonst in Java.

    Nun wollte ich mit C++ Dateien einlesen und den Inhalt verarbeiten. Folgendes Problem: in einer Textdatei stehen Koordinaten für Punkte im Raum:

    #Point 6
    Location 19.5 19.5 29.23470688
    	-0.2045922279 0 0
    	0 0.2045922279 0
    	0 0 -0.4410593808
    

    Das ist zum Beispiel ein Punkt, dessen Koordinaten nach dem Wort 'Location' kommen. In der Datei gibt es prinzipiell beliebig viele solcher Punktblöcke, getrennt mit einem (!) Leerzeichen. Der Aufbau der Blöcke ist immer so wie oben beschrieben.

    Nun wollte ich aus der ganzen Datei alle Punkte mir ihren Koordinaten einlesen. Nach etwas mehr als einem Tag habe ich folgende Lösung. Elegant sieht vielleicht anders aus, deshalb hier meine Frage, wie ginge es denn eleganter? Danke schonmal 😉 Man will ja dazu lernen 😃

    void qvc::Skeleton::Load(const char* fileName)  
    {
      std::ifstream in (fileName);
      if(!in.is_open())
      {
        std::cout << "Could not read file at " << fileName << " for loading data" << std::endl;
        return;
      }
    
      char lineBuffer[256];
      char* lineToken;
    
      for(int i = 0; i < 6 && in.good(); i++) // Am Anfang der Datei stehen noch sechs Kopfzeilen, also einfach überspringen
      {
        in.getline(lineBuffer, 256);
      }
    
      while(in.good())
      {
        in.getline(lineBuffer, 256, '\n');
        if(!IsPointLine(lineBuffer))
        {
          continue;
        }
    
        in.getline(lineBuffer, 256, ' ');
        in.getline(lineBuffer, 256, ' ');
        float x = (float)strtod(lineBuffer, &lineToken);
    
        in.getline(lineBuffer, 256, ' ');
        float y = (float)strtod(lineBuffer, &lineToken);
    
        in.getline(lineBuffer, 256, ' ');
        float z = (float)strtod(lineBuffer, &lineToken);
    
        std::cout << "(" << x << "," << y << "," << z << ")" << std::endl;
    
        qvc::Point cp(x,y,z);
    
        this->criticalPoints->push_back(&cp);
      }
    
      in.clear();
      in.close();
    }
    
    bool qvc::Skeleton::IsPointLine(const char* line)
    {
      if(strlen(line) > 9)
      {
        return false;
      }
    
      int i = 0;
    
      if(line[i++] != '#') return false;
      if(line[i++] != 'P') return false;
      if(line[i++] != 'o') return false;
      if(line[i++] != 'i') return false;
      if(line[i++] != 'n') return false;
      if(line[i++] != 't') return false;
    
      return true;
    }
    


  • void qvc::Skeleton::Load(const char* fileName) // alles ungetestet
    {
      std::ifstream in (fileName);
      if(!in.is_open()) {
        std::cerr << "Could not read file at " << fileName << " for loading data" << std::endl;
        return;
      }
      const std::streamsize max_size = std::numeric_limits<std::streamsize>::max(); // benötigt #include <limits>
      // Am Anfang der Datei stehen noch sechs Kopfzeilen, also einfach überspringen
      for (int i = 0; i < 6 && in.good(); i++, in.ignore(max_size, '\n'));
      for (std::string buffer(std::getline(in)); in.good(); buffer=std::getline(in)) {
        if (buffer.compare(0, 6, "#Point")) // Funktionalität von !IsPointLine(lineBuffer)
          continue;
        float x, y, z;
        in >> x >> y >> z; // ignoriert Leerzeichen
        std::cout << "(" << x << "," << y << "," << z << ")" << std::endl;
        criticalPoints->push_back(new/*?*/ qvc::Point(x,y,z));
      }
      // in.clear() und in.close() werden automatisch aufgerufen (dank RAII)
    }
    

    Ist zwar ungetestet, müsste aber funktionieren.
    Folgende zwei Zeilen von dir verstehe ich nicht ganz:

    qvc::Point cp(x,y,z); // eine neue Variable, von der am Ende des Durchlaufs der Destruktor aufgerufen wird und sie zerstört
    this->criticalPoints->push_back(&cp); // hier übergibst du eine Referenz auf ein temporäres Objekt
    // das this ist übrigens unnötig
    

    Frage: wie sieht deine Klasse denn aus?

    class Skeleton {
    public:
      void Load(const char *filename);
    private:
      std::vector<Point *> *criticalPoints;
    };
    

    Falls ja, dann rate ich dir dringend auf die hässlichen Pointer zu verzichten. Denn du musst für jeden Zeiger einen Destruktor aufrufen, was du vermutlich vergessen hast und damit ein Speicherleck erzeugt hast. Ausserdem ist das dauernde dereferenzieren unnötig langsam. Ein

    std::vector<Point> criticalPoints;
    

    reicht völlig aus, dann kannst du bei meinem Code auf Zeile 17 das " new/*?*/ " rausnehmen.


Anmelden zum Antworten