Pfad des ifstream Objects mit dem dieses Instanziiert wurde



  • Besteht eine Möglichkeit, innerhalb einer Funktion die als Parameter std::ifstream& is hat, nachträglich den Pfad heraus zu finden, der im konstruktor-/ bzw. open() verwendet wurde?

    Möchte dabei nicht auf filesystem o.ä. zurückgreifen





  • mh danke, es geht darum dasss ich operator>> überladen möchte und mit verschiedenen files eine std::map befüllen möchte. Allerdings ist der key-type( ein std::string) nicht Bestandteil der jeweiligen files sondern wird erst zur Laufzeit vergeben



  • Wenn Deinen letzten Beitrag irgendjemand versteht, der nicht ausgerechnet Du selbst bist, fress ich einen Besen.

    XY Problem?



  • du hast Recht, tut mir leid 😕

    Ich versuche es anders, wenn ich nachher Zeit finde



  • Ich versuchs mal verständlich zu erklären. Ich habe diverse files mit Messpunkten deren Pfade sind in.

    std::vector<std::string> FileNames_;
    

    Diese möchte ich gemäß ihres Namens in eine

    std::unordered_map<std::string, std::vector<Point3D>> pointClouds_;
    

    einlesen möchte. Meine Klasse Point3D sieht dabei so aus

    struct Point3D
    {
      double x;
      double y;
      double z;
    
      friend std::istream& operator>>(std::istream& is, Point3D& point)
      {
        return is >> point.x >> point.y >> point.z;
      }
    };
    

    Die einlesende Funktion

    void
    Parser::ExtractFromFiles()
    {
      for (auto const& filename : FileNames_) {
        std::ifstream inputFile{ filename };
        assert(inputFile.is_open() && !inputFile.fail());
    
          std::copy(
                   std::istream_iterator<std::pair<size_t, std::vector<Point3D>>>{
                   inputFile },
                   {}, 
                   std::inserter(pointClouds, std::end(pointClouds)));
    
        }
      }
    

    dazu habe ich zweimal operator>> überladen

    std::istream&
    operator>>(std::istream& is, std::vector<Point3D>& cloud_t)
    {
      for (std::string buffer; std::getline(is, buffer);) {
        std::stringstream lidarPoint{ buffer };
        std::copy(std::istream_iterator<Point3D>{ lidarPoint }, {},
                  std::back_inserter(cloud_t));
      }
      return is;
    }
    
    std::istream&
    operator>>(std::istream& is, std::pair<std::string, std::vector<Point3D>>& pair)
    {
      return is >> pair.first >> pair.second;
    }
    

    Nun habe ich zwei wesentliche Fragen

    • wie kann ich den filenamen als key in die map einlesen? In der Überladung
    std::istream&
    operator>>(std::istream& is, std::pair<size_t, std::vector<Point3D>>& pair)
    

    wird momentan ja fälschlicherweise das erste Element des eingelesenen Files als Key gespeichert

    • der Code funktioniert nicht. Es werden clouds befüllt, aber die Map ist am Ende leer, was übersehe ich?


  • #include <algorithm>
    #include <iterator>
    #include <fstream>
    #include <initializer_list>
    #include <vector>
    #include <string>
    #include <unordered_map>
    
    struct Point3D
    {
    	double x;
    	double y;
    	double z;
    };
    
    std::istream& operator>>(std::istream& is, Point3D & point)
    {
    	return is >> point.x >> point.y >> point.z;
    }
    
    class Parser
    {
    	std::vector<std::string> FileNames_;
    	std::unordered_map<std::string, std::vector<Point3D>> pointClouds_;
    
    public:
    	Parser(std::initializer_list< std::string > filenames) : FileNames_{filenames} {}
    	void ExtractFromFiles();
    };
    
    void Parser::ExtractFromFiles()
    {
    	for (auto const& filename : FileNames_) {
    
    		std::ifstream inputFile{ filename };
    		std::copy(std::istream_iterator<Point3D>{inputFile}, std::istream_iterator<Point3D>{}, std::back_inserter(pointClouds_[filename]));
    	}
    }
    
    int main()
    {
    
    	Parser p{ "1.txt", "2.txt", "3.txt" };
    	p.ExtractFromFiles();
    }
    


  • Hey Sewing, arbeitest du da mit klassischen Punktwolken aka 3D Messpunkte von Laserscannern o.ä.?

    Kennst du die PCL (http://www.pointclouds.org/)? Ich habe das Gefühlt, dass dir die Bibliothek evt. einiges an Arbeit sparen könnte.



  • vielen lieben Dank!

    Das heißt die Methode, die ich verwendet habe, wäre eher geeignet für den Fall, dass der key auch in den files steht und nicht wie hier schon vorher feststeht ja?

    std::map hat ja kein push_back, weshalb ich nicht darauf gekommen bin, dass man das umgehen kann, indem man den mapped-type, dh. std::vector push_backed, indem man schon im Funktionsaufruf von back_inserter den mapped type übergibt. Genial...

    verwendet std::istream_iterator by-default '\n' als delimiter oder wieso liest er immer genau einen Punkt ein?

    allerdings muss ja mein Point3D hierfür wegen operator[] default constructible sein...

    ich wüsste dennoch gerne, was an meinem code falsch war...



  • Schlangenmensch schrieb:

    Hey Sewing, arbeitest du da mit klassischen Punktwolken aka 3D Messpunkte von Laserscannern o.ä.?

    Kennst du die PCL (http://www.pointclouds.org/)? Ich habe das Gefühlt, dass dir die Bibliothek evt. einiges an Arbeit sparen könnte.

    es sind tatsächlich .pcd files ; )

    nur will ich vermeiden, dieses monster als Abhängigkeit zu verwenden, und da ich nur ein paar files einlesen muss, wollte ich mir da anderweitig behelfen ; )



  • Sewing schrieb:

    Das heißt die Methode, die ich verwendet habe, wäre eher geeignet für den Fall, dass der key auch in den files steht und nicht wie hier schon vorher feststeht ja?

    Ich glaube Du denkst zu kopmpliziert:

    void Parser::ExtractFromFiles()
    {
        for (auto const& filename : FileNames_) {
    
            std::ifstream inputFile{ filename };
            std::string pointCloudName;
            inputFile >> pointCloudName;
            std::copy(std::istream_iterator<Point3D>{inputFile}, std::istream_iterator<Point3D>{}, std::back_inserter(pointClouds_[pointCloudName]));
        }
    }
    

    Sewing schrieb:

    std::map hat ja kein push_back, weshalb ich nicht darauf gekommen bin, dass man das umgehen kann, indem man implizit den std::vector push_backed, indem man schon im funktionsaufruf von back_inserter den mapped type übergibt. Genial...

    Hä? pointClouds_[filename] aus Zeile 38 ist ein std::vector<Point3D> ... da wird garnichts implizit "push_backed".

    Sewing schrieb:

    ich wüsste dennoch gerne, was an meinem code falsch war...

    ich wüsst nicht wo anfangen 😕



  • habs meine formulierung im vorhergehenden post angepasst.

    hatte das zunächst so gelöst

    for (auto const& filename : FileNames_) {
    
          std::ifstream inputFile{ filename };
          assert(inputFile.is_open() && !inputFile.fail());
    
                std::istringstream istr;
                std::vector<Point3D> cloud;
                for (std::string buffer{}; std::getline(inputFile, buffer);) {
                  std::stringstream lidarPoint{ buffer };
    
          std::copy(std::istream_iterator<Point3D>{ lidarpoint },
                    std::istream_iterator<Point3D>{},
                    std::back_inserter(cloud));
        }
              pointClouds_.emplace(filename, cloud);
      }
    

    weil ich dachte, es sei notwendig explizit Zeile für Zeile des Files einzulesen, offenbar ist es das nicht...

    generell hatte ich versucht das Beispiel aus dem Buch hier anzuwenden

    [url=http://666kb.com/i/dr6ge245c98e4xjkt.jpg] 1
    [/url]
    [url=http://666kb.com/i/dr6gefgq9xxz182u5.jpg] 2
    [/url]
    3



  • sewing schrieb:

    verwendet std::istream_iterator by-default '\n' als delimiter oder wieso liest er immer genau einen Punkt ein?

    std::istream_iterator verwendet den für Deinen Punkt3D definierten operator>>(std::istream&,Point3D&) ... darin steht geschrieben, wie ein Punkt eingelesen wird.

    sewing schrieb:

    allerdings muss ja mein Point3D hierfür wegen operator[] default constructible sein...

    Nicht wegen std::unordered_map<...>::operator[] sondern wegen std::istream_iterator<Point3D> .



  • Sewing schrieb:

    std::ifstream inputFile{ filename };
          assert(inputFile.is_open() && !inputFile.fail());
    

    Wer hat dir denn für diesen Fall "assert" beigebracht? Mit assert kann man Programmlogik überprüfen, die IMMER wahr sein soll. Dateihandling kann auch mal fehlschlagen und hat mit programminterner Logik nichts zu tun. Wenn du im Release-Modus kompilierst, sind assert-Checks NICHT mehr enthalten!

    Siehe http://www.cplusplus.com/reference/cassert/assert/:
    "[assert] is designed to capture programming errors, not user or run-time errors"



  • und wozu taugt dann das Beispiel in den von mir referenzierten Bildern?

    verstehe den Unterschied nicht so ganz. Dort wird ja auch operator>> zweifach überladen

    und dann würde ich noch gerne wissen, wie ich statt std::copy std::transform verwenden kann. wenn ich das ersetze und als 4. parameter beispielsweise ein leeres lambda []{} verwende bekomme ich

    /usr/include/c++/7/bits/stl_algo.h:4306: error: no match for call to ‘(Parser::ExtractFromFiles()::<lambda()>) (const Point3D&)’
      *__result = __unary_op(*__first);
                  ~~~~~~~~~~^~~~~~~~~~
    


  • Was heißt leeres Lambda? Deine Lambda Funktion muss natürlich ein Parameter vom Typ const Point3D& erwarten, sonst passt das nicht.



  • Schlangenmensch schrieb:

    Was heißt leeres Lambda? Deine Lambda Funktion muss natürlich ein Parameter vom Typ const Point3D& erwarten, sonst passt das nicht.

    hatte zunächst []{} als lambda verwendet und dann

    std::transform(std::istream_iterator<Point3D>{ inputFile },
                    std::istream_iterator<Point3D>{},
                    std::back_inserter(pointClouds[cloudName]), [](const Point3D& p){});
    

    ... muss natürlich als return nen Point3D machen 🙄



  • [](const Point3D& p){};
    

    erwartet ein Point3D als Übergabeparameter und gibt nichts zurück.

    Mit Rückgabe sähe das so aus:

    [](const Point3D& p)->Point3D{return Point3D};
    

    //Edit: mit Rückgabewert muss das Lambda natürlich auch ein entsprechendes return Statement haben...



  • danke, hatte mich nur gewundert, wieso

    std::transform(std::istream_iterator<Point3D>{ inputFile }, 
                    std::istream_iterator<Point3D>{}, 
                    std::back_inserter(pointClouds[cloudName]), [](const Point3D& p){});
    

    nicht funktioniert



  • Schlangenmensch schrieb:

    Mit Rückgabe sähe das so aus:

    [](const Point3D& p)->Point3D{return Point3D};
    

    //Edit: mit Rückgabewert muss das Lambda natürlich auch ein entsprechendes return Statement haben...

    Wobei: ich gebe in der Regel den Rückgabetyp nicht explizit an, denn er ergibt sich ja sowieso aus dem return-Statement des Lambdas. (und wenn das Lambda so groß und kompliziert ist, dass es einen expliziten Returntyp braucht, ist in sehr wahrscheinlich zu viel Code im Lambda, das möglichst sehr simpel gehalten werden sollte)


Anmelden zum Antworten