Konvertierungsproblem std::string nach char*



  • Hallo zusammen,

    folgender Beispielcode ist gegeben:

    void fu(std::ostream& os) 
    {
            std::ostringstream oss;
            /*oss wird befüllt
              wie genau ist nicht wichtig, siehe weiter untern
            */       
            //Größe als Headerinformation ermitteln
            uint64_t sz = oss.str().size();
            //"Header"  mit Längenangabe vorne weg
            os.write(reinterpret_cast<char*>(&sz), sizeof(sz));
            //eigentliche Daten hintendran, fix auf "ZZZ" macht die gleichen Probleme
            os << "ZZZ" ;//oss.str();
    
    }
    
    std::ostringstream tx;
    fu(tx); 
    weiter_verarbeiten(tx.str());
    

    Bishier ist alles korrekt, wenn ich mir tx.str() ausgeben ist alles so wie erwartet. Allerdings wird innerhalb der Funtkion "weiter_verarbeiten" ein char*
    benötigt.

    Jedoch habe ich, wenn ich c_str() nutze nur noch den Header.

    void weiter_verarbeiten(std::string s)
    {
       std::cout << s << std::endl; //alles gut, header + daten
       std::cout << s.c_str() << std::endl; //nur noch der header
    }
    

    Das Problem ist, der Header muss so fix sein, da er an anderen stellen entsprechend so erwartet wird. Die Verwendung des char* ist auch unumgänglich.

    Ich hoffe das Problem ist verdeutlicht und jemand kann mir erklären, warum dieses verhalten auftritt und noch besser, wie ich das lösen könnte. Besten Dank im Voraus.



  • robert.S schrieb:

    Ich hoffe das Problem ist verdeutlicht und jemand kann mir erklären, warum dieses verhalten auftritt und noch besser, wie ich das lösen könnte. Besten Dank im Voraus.

    Verdacht: Da ist ein "\0" im String. char* ist 0-terminiert.



  • std::cout.write(s.c_str(), s.size());



  • unskilled schrieb:

    std::cout.write(s.c_str(), s.size());

    Dann erhalte ich wieder den kompletten String.

    Wenn ich das einbaue:

    uint64_t sz = oss.str().size();
    std::ostringstream temp;
    //"Header"  mit Längenangabe vorne weg
    temp.write(reinterpret_cast<char*>(&sz), sizeof(sz));
    temp << "ZZZ";
    os.write(temp.str().c_str(), temp.str().size());
    

    Habe ich dann wieder nur den Header bei c_str()...

    asc schrieb:

    Verdacht: Da ist ein "\0" im String. char* ist 0-terminiert.

    Hatte ich auch überlegt, aber dürfte doch eigentlich nicht sein?


  • Mod

    robert.S schrieb:

    asc schrieb:

    Verdacht: Da ist ein "\0" im String. char* ist 0-terminiert.

    Hatte ich auch überlegt, aber dürfte doch eigentlich nicht sein?

    Wieso nicht? Klingt sogar sehr wahrscheinlich, dass ein uint64_t Nullbytes enthalten sollte, besonders ganz am Anfang. Dadurch würde dann ein C-String genau nach deinem Header terminiert.



  • dann zeig doch das stück, wo der fehler auftritt...

    #include <iostream>
    #include <sstream>
    
    using namespace std;
    
    int main() {
    
    uint64_t sz = 123; 
    
    std::ostringstream temp; 
    temp.write(reinterpret_cast<char*>(&sz), sizeof(sz)); 
    temp << "ZZZ"; 
    
    std::cout.write(temp.str().c_str(), temp.str().size());
    }
    

    macht, was es soll.



  • Wie greift deine Funktion weiter_verarbeiten() denn auf den Parameter zu?
    Entweder die Funktion erwartet einen nullterminierten String oder aber sie kann mit den Binärdaten umgehen (und interpretiert die ersten Bytes als Längenangabe).
    Mit

    std::cout << s.c_str();
    

    darauf zuzugreifen ist dann natürlich sinnlos.



  • unskilled schrieb:

    dann zeig doch das stück, wo der fehler auftritt...

    #include <iostream>
    #include <sstream>
    
    using namespace std;
    
    int main() {
    
    uint64_t sz = 123; 
    
    std::ostringstream temp; 
    temp.write(reinterpret_cast<char*>(&sz), sizeof(sz)); 
    temp << "ZZZ"; 
    
    //Ja, auf der Konsole ist die Ausgabe wie gewollt.
    std::cout.write(temp.str().c_str(), temp.str().size());
    //Die Weiterverarbeitung geht aber nur mit einem char * und daher
    //fehlt alls nach dem header bei:
    std::cout << temp.str().c_str() << std::endl;
    
    }
    

    Auf der Konsole, ja, aber die Prozesskette verlangt ein char* und da bleibt mittselst c_str() nur noch der header (siehe oben).

    Es sind tatsächlich \0-er drin, die ich entfernt habe. Damit habe ich theoretisch das was ich wollte, kann dann aber den header nicht mehr zurück-casten...

    Denke da muss grundlegend an dem Aufbau was geändert werden, aber darauf habe ich keinen einfluss...

    Th69 schrieb:

    Wie greift deine Funktion weiter_verarbeiten() denn auf den Parameter zu?

    Grob gesagt wird die ganze Zeichenkette "so wie sie ist" weggespeichert (boost, shared memory, so wie in der Antwort in http://stackoverflow.com/questions/4278627/c-boostinterprocess-simple-application/4540790#4540790) um damit später was zu machen (header extrahieren und rest-daten verarbeiten)



  • Die Weiterverarbeitung geht aber nur mit einem char *

    dann wird die länge nicht binär davor geschrieben.



  • unskilled schrieb:

    Die Weiterverarbeitung geht aber nur mit einem char *

    dann wird die länge nicht binär davor geschrieben.

    Hatte ich jetzt auch probiert. Dabei ist allerdings aufgellen, dass nach dem Header ebenfalls Daten mit reinterpret_cast drin stehen können, was dann ebenfalls zu Problemen führt.

    Das "Problem" liegt dabei offenbar im shared memory:

    #include <iostream>
    #include <string>
    #include <boost/interprocess/shared_memory_object.hpp>
    #include <boost/interprocess/containers/vector.hpp>
    #include <boost/interprocess/allocators/allocator.hpp>
    #include <boost/interprocess/managed_shared_memory.hpp>
    #include <boost/interprocess/containers/string.hpp>
    #include <sstream>
    #include <bitset>
    using namespace boost::interprocess;
    typedef boost::interprocess::allocator<char, boost::interprocess::managed_shared_memory::segment_manager> CharAllocator;
    typedef boost::interprocess::basic_string<char, std::char_traits<char>, CharAllocator> my_string;
    
    void fu(std::ostream& os)
    {
            std::ostringstream oss;
            oss << "testx";
            uint64_t sz = oss.str().size();
            os << "112";
            os.write(reinterpret_cast<char*>(&sz), sizeof(sz));
            os << "ACA13" ;
    }
    
    int main()
    {
    
        std::ostringstream tx;
        fu(tx);
        const char* x = tx.str().c_str();
    
       //Ausgabe wie erwartet nur zum Teile, da \0-terminiert
       std::cout << x << std::endl;
       //unter Angabe der Größe, die mir jederzeit bekannt ist, kann
       //ich trotzdem wieder auf den kompletten Inhalt zugreifen, das wäre der idelazustand
       std::cout.write(x, tx.str().size());
    
      managed_shared_memory mshm(open_or_create, "smem",  65536 );
      //den Inhalt in den shared memory schreiben
      mshm.find_or_construct<my_string>( "test2" )(tx.str().c_str(),mshm.get_segment_manager());
    
      //und wieder auslesen
      std::pair<my_string* , size_t > p= mshm.find<my_string>("test2");
      //wie erwartet, nur bis \0:
      std::cout<< "shm: "<<p.first->c_str()<<std::endl;
      //das klappt aber nicht mehr, auch wenn ich weiß, wie lang die eigentliche Zeichenkette ist...:
      std::cout.write(p.first->c_str(), tx.str().size());
    
       shared_memory_object::remove("smem");
       return 0;
    
    }
    


  • Hatte ich jetzt auch probiert. Dabei ist allerdings aufgellen, dass nach dem Header ebenfalls Daten mit reinterpret_cast drin stehen können, was dann ebenfalls zu Problemen führt.

    das glaube ich nicht.
    deshalb schreibst du ja ganz vorn die länge hin.

    das hier sieht falsch aus:
    std::cout.write(p.first->c_str(), tx.str().size());
    ->
    std::cout.write(*p.first, p.first->size());

    aber mit doku lesen und tut angucken könnte man da sicher besser vorwärts kommen als jmd zu fragen, der zu faul ist, sich da jetzt reinzulesen... 😉



  • unskilled schrieb:

    das glaube ich nicht.
    deshalb schreibst du ja ganz vorn die länge hin.

    Das habe ich sehr wohl getan, bis ich dann, wie geschrieben, festgestellt habe, dass es auch vorkommen kann, dass danach auch noch 0\-Zeichen drin vorkommen können. Deshalb die Anpassung in meinem besipiel, wo das ganze jetzt mittendrin ist.

    unskilled schrieb:

    das hier sieht falsch aus:
    std::cout.write(p.first->c_str(), tx.str().size());
    ->
    std::cout.write(*p.first, p.first->size());

    Das funktioniert leider, nicht, da *p.first ja ein "my_string" ist und std::cout damit nichts anzufangen weiß

    unskilled schrieb:

    aber mit doku lesen und tut angucken könnte man da sicher besser vorwärts kommen als jmd zu fragen, der zu faul ist, sich da jetzt reinzulesen... 😉

    Ursprünglich dachte ich ja, das es ein anderes Problem ist. Nun stellt sich ja heraus, dass es im Zusammenhang mit dem shared memory ist. Die Doku dazu ist bei boost schon sehr bescheiden, daher die Hoffnung, das vielleicht einer hier im Forum mal ein ähnliches Problem hatte.

    Meine temporäre Zwischenlösung sieht erstmal so aus, dass ich die \0er durch anderes, sonst nicht vorkommendes Zeichen ersetzen, und nach dem auslesen aus dem shared memory wieder die \0er einzufügen.

    Sobald ich eine "ordentliche" Lösung dafür habe melde ich mich hier, für alle die es interessiert 😉



  • Habe jetzt eine funktionierende Variante gefunden: Mittels char-Vector im shared Memory kann man lösen:

    #include <iostream>
    #include <string>
    #include <boost/interprocess/shared_memory_object.hpp>
    #include <boost/interprocess/containers/vector.hpp>
    #include <boost/interprocess/allocators/allocator.hpp>
    #include <boost/interprocess/managed_shared_memory.hpp>
    
    using namespace boost::interprocess;
    typedef boost::interprocess::allocator<char, boost::interprocess::managed_shared_memory::segment_manager> CharAllocator;
    typedef boost::interprocess::vector<char, CharAllocator> my_vector;
    
    void fu(std::ostream& os)
    {
            std::ostringstream oss;
            oss << "testx";
            uint64_t sz = oss.str().size();
            os << "112";
            os.write(reinterpret_cast<char*>(&sz), sizeof(sz));
            os << "ACA13" ;
    }
    
    int main()
    {
        //Testoutput erzeugen
        std::ostringstream tx;
        fu(tx);
    
        std::string save = tx.str();
        managed_shared_memory mshm(open_or_create, "smem2",  65536 );
        //char-vector Erzeugen/finden
        my_vector* mv=   mshm.find_or_construct<my_vector>( "test2" )(mshm.get_segment_manager());
        mv->clear();
        //String in char-vector kopieren
         for(int i=0; i<save.size(); ++i)
            mv->push_back(save.at(i));
    
        //aulesen aus shared  memory
        std::string result = "";
        std::pair<my_vector* , size_t > p= mshm.find<my_vector>("test2");
        for(int i=0; i<p.first->size(); ++i)
            result.append(1, p.first->at(i) );
    
        //Kontrolle
        std::cout << "Original  : " << save<< std::endl;
        std::cout << "Shared Mem: " << result <<std::endl;
    
        shared_memory_object::remove("smem2");
        return 0;
    
    }
    

Log in to reply