byte to std:string



  • Ich hab dir mal cpp-Tags spendiert.

    Anmerkung zu deinem Code: du hast für den Sting jetzt den Konstruktor string(char const*, size_type) benutzt. Ich würde eher den Konstruktor string(InputIterator, InputIterator) benutzen, das spart dir den Cast (den du vom Stil her lieber als C++-Cast ausführen solltest)



  • Eigentlich braucht man den temp. Puffer überhaupt nicht:

    void f()
    {
       CSerial serial;
       if (serial.Open(1, 9600))
       {
         int BytesRead = 0;
    
         while( BytesRead<=0 )
         {
            BytesRead = serial.ReadDataWaiting();
            Sleep(4000);
         }
         string buffer( BytesRead, 0 );
    
         BytesRead = serial.ReadData( &buffer[0], buffer.size() );
         buffer.resize( BytesRead );
       }
       else
       {
          cout << "Can not open comport\n";
       }
    }
    


  • DonDom schrieb:

    Wichtig ist allerdings, dass ich die Daten wie folgt vorliegen haben möche

    hexadecimal text to decode:
    532d522d52202d30342e3735202b30302e3030202020202020202020450d0a532d4b2d52202b30372e3737202b30372e3736202020202020202020450d0a

    Hallo DonDom,

    willst Du das wirklich? Das ist doch noch nicht das Ende der Verarbeitung dieser Daten.
    Wenn die Zeichen das darstellen ..

    DonDom schrieb:

    string code:
    S-R-R -04.75 +00.00 E
    S-K-R +07.77 +07.76 E

    .. dann ist doch der String S-R-R und die beiden folgenden Zahlen interessant und nicht eine irgendwie geartete Hexadezimaldarstellung.

    Das ist ein typisches IO-Problem, welches man in C++ sehr schön mit einem std::istream und einen angepassten std::streambuf realisieren kann.
    Die Kernidee dabei ist es, einen std::istream zu haben, aus dem die einzelnen Worte und Zahlen gelesen werden können.

    std::istream serialline( /*kommt später*/ ); // das ist der Inputstream, hier 'fallen' die Zeichen raus
        std::string prefix; // das Wort am Zeilenanfang
        char e; // das Zeichen am Ende
        double x, y; // und die beiden Zahlen dazwischen
        for( ; serialline >> prefix >> x >> y >> e; )  // alles einlesen, und wenn alles ok:
        {
            // mache irgendwas mit den eingelesenen Werten
            cout << "gelesen: " << prefix << " " << x << " " << y << endl;
            // usw. ...
        }
    

    Die Ausgabe dieses kleinen Programms könnte dann mit den von Dir genannten Daten so aussehen:

    gelesen: S-R-R -4.75 0
    gelesen: S-K-R 7.77 7.76
    

    Damit das funktioniert baue man um die Klasse CSerial einen eigenen Streambuf (hier SerialStreambuf) und verbinde ihn mit dem std::istream. Anschließen kann man die Zeichen dann lesen.

    #include <windows.h> // wg. Sleep
    #include "Serial.h" // siehe http://www.codeguru.com/Cpp/I-N/network/serialcommunications/article.php/c2503/
    #include <iostream>
    #include <stdexcept> // std::domain_error
    #include <streambuf>
    #include <string>
    #include <vector>
    
    class SerialStreambuf : public std::streambuf
    {
    public:
        explicit SerialStreambuf( double timeoutInSecs, int nPort = 2, int nBaud = 9600 )
            : std::streambuf()
            , m_device()
            , m_timeout( timeoutInSecs )
            , m_buf()
        {
            if( !m_device.Open( nPort, nBaud ) )
                throw std::domain_error( "Can not open Serial Port" );
        }
        ~SerialStreambuf()
        {
            m_device.Close();
        }
    
    protected:
        virtual int_type underflow()
        {
            int anzBytes;
            // --   Warte auf Zeichen am seriellen Port
            // Bem.: das ist Pollen, und Pollen ist suboptimal, aber das Interface von CSerial gibt nicht mehr her!
            const int Delta = 100; // alle 100ms == 0.1s nachfragen
            for( int wartezeit = int(m_timeout*1000); (anzBytes = m_device.ReadDataWaiting()) == 0 && wartezeit > 0;  wartezeit -= Delta )
                Sleep( Delta );
            if( anzBytes == 0 )
                return traits_type::eof(); // timeout
    
            // --   es stehen 'anzBytes' Zeichen an, also lesen
            m_buf.resize( anzBytes );
            const int len = m_device.ReadData( &m_buf[0], anzBytes );
            if( len <= 0 )
                return traits_type::eof(); // Lesefehler
            setg( &m_buf[0], &m_buf[0], &m_buf[0] + len );
            return traits_type::to_int_type( m_buf[0] );
        }
    
    private:
        // --   Kopieren ist nicht gestattet
        SerialStreambuf( const SerialStreambuf& );
        SerialStreambuf& operator=( const SerialStreambuf& );
    
        // --   Members
        CSerial m_device;
        double m_timeout;
        std::vector< char > m_buf;
    };
    
    int main()
    {
        using namespace std;
        SerialStreambuf port( 2.5/*Sekunden Timeout*/,  2 );
        istream serialline( &port ); // streambuf und istream verbinden
    
        string prefix;
        char e;
        double x, y;
        for( ; serialline >> prefix >> x >> y >> e; )
        {
            cout << "gelesen: " << prefix << " " << x << " " << y << endl;
            // usw. ...
        }
        return 0;
    }
    

    Im Prinzip war es das. Kommt es zu einem Timeout oder Lesefehler, liefert der streambuf eine EOF und der istream geht bei jedem weiteren Leseversuch in den Fehlerzustand.

    DonDom schrieb:

    Gernerell will ich die Zeichenkette verändern und in veränderter Form weiter senden. Hierbei ist es jedoch wichtig, dass der Ausgabe string identisch mit dem Eingabe string ist bzgl. nicht druckbarer Zeichen, da diese Zeichen vom Empfangenden Gerät benötigt werden.

    Als Bsp.

    das ASCII-Zeichen 9 also die "4" (hex 42) wird verändert zu "7" (hex 72)

    Du meinst: das 9.Zeichen in der Zeile "S-R-R -04.75 +00.00 E" ist eine '4' (Code 0x34); diese soll nach '7' geändert werden (Code 0x37). Schau Dir mal die ASCII-Tabelle an.

    DonDom schrieb:

    Außerdem bin ich bisher davon ausgangen, dass ich nur mit der hex. Darstellung sicher sein sämtliche Geräte voneinander unterscheiden zu können.

    Hier ist mir gar nicht klar, was Du damit meinst. Was sind das für 'Geräte'?

    DocShoe schrieb:

    Eigentlich braucht man den temp. Puffer überhaupt nicht:

    ...
         string buffer( BytesRead, 0 );    
         BytesRead = serial.ReadData( &buffer[0], buffer.size() );
    

    Du unterstellst hier, dass die Zeichen in einem std::string fortlaufend im Memory liegen. Das ist zwar oft der Fall, aber vom Standard nicht garantiert. Deshalb benutze ich auch in meiner Lösung einen std::vector - hier ist es faktisch immer so.

    Genau wie Du mit dem istream Lesen kannst, kannst Du mit einem ostream schreiben; ebenfalls mit einem angepassten streambuf. Falls Du daran interessiert bist, so frage noch mal nach und gebe das Format und ein Beispiel an, was da wie geschrieben werden soll.

    Gruß
    Werner



  • Hey erstmal danke für deine Mühen...
    Es handelt sich hierbei um medizischne Geräe die ihre Messwerte über die serielle Schnittstelle ausgeben. Andererseits gibt es aber auch Geräe die zur Weiterverarbeitung genau die Ausagbe des anderen Geräts erwarten.

    Da du fragst was ich vor habe.
    Gerät 1 (Sender)
    Gerät 2 (Sender)
    Geräte 3 (Empfänger)

    Gernerell will ich z.B. die Ausgabe von Gerät 1 oder 2 über ein Patternmatchin automatisch analysieren um welches Gerät es sich handelt und die Messwerte extrahieren.

    1. Datenempfangen
    2. Datenextrahieren
    3. Daten personenbezogen abspeichern
    4. Ausagbe in Gerät 3 konformen Sring ausgeben

    Da sowohl in der Ein wie auch im Ausgabe nicht Ascii-Zeichen vorkommen können habe ich gedacht es ist sinvoller alles als Hex zu behandeln und nur die relevanen Teile der Zeichenkette zu verändern (was ja die Sellen darstellen in dneen die Messwerte stehen).

    string a("\x53\x2d\x52\x2d\x52\x20\x2d\x30\x34\x2e\x37\x35\x20\x2b\x30\x30\x2e\x30\x30\x20\x20\x20\x20\x20\x20\x20\x20\x20\x45\x0d\x0a\x53\x2d\x4b\x2d\x52\x20\x2b\x30\x37\x2e\x37\x37\x20\x2b\x30\x37\x2e\x37\x36\x20\x20\x20\x20\x20\x20\x20\x20\x20\x45\x0d\x0a");
    	string b = "S-R-R -05.00 +00.00         E\x0d\S-K-R +07.70 +07.69         E\x0d\x0a";
    

    Außerdem will ich die Geräte + deren Objeke mit Boost serialisieren(XML). Klapp alls ganz gut bis auf die Zeichen \x0d\x0a 😞



  • \x0d\x0a

    Sind die Escape-Sequenzen "CR" "LF".
    Und genau diese sind mein Problem 🙂

    Sobald ich die Zeichenkette ausgabe und wieder einlese fehlt irgendwas 🙂

    Aber das Problem solle ich ja in den Griff bekommen indem ich die erhaltene Zeichenkette Zeilenweise Speicher...



  • DonDom schrieb:

    \x0d\x0a

    Sind die Escape-Sequenzen "CR" "LF".
    Und genau diese sind mein Problem 🙂

    Sobald ich die Zeichenkette ausgabe und wieder einlese fehlt irgendwas 🙂

    Aber das Problem solle ich ja in den Griff bekommen indem ich die erhaltene Zeichenkette Zeilenweise Speicher...

    Hallo DonDom,

    Ja - klar, das ist das Zeilenende wie es im DOS(Windows)-Umfeld üblich ist.
    Aber ich verstehe nicht, wo Dein Problem liegt. Das zeilenweise Speichern ist normal nie eine gute Idee, wenn Du auf den Zahlen noch Operationen vornehmen möchtest.

    Für die Umwandlung eines Zeilenvorschubs (Linefeed := LF := 0x0a) in eine Folge CR LF ist nach meinem Verständnis die Facette std::codecvt zuständig. Das wäre aber in Deinem Fall mit Kanonen auf Spatzen geschossen.
    Es reicht doch, bei der Ausgabe an 'Gerät 3' einfach ein CR (CR := Carriage Return := Wagenrücklauf) vor dem Zeilenende einzufügen. So was in der Art:

    const char CR = 0x0d;
        std::ostream out(...);
        out << "S-R-R " << x << " " << y << "   E" << CR << endl;
    

    Wegen des Formatierens der Zahlen, schaue Dir diesen Thread an.

    Gruß
    Werner

    PS.: habe heute voraussichtlich keine Zeit mehr, melde mich ggf. am Wochenende.



  • Danke. Das Problem ist insgesamt dass ich die Objekte, welche dann z.B. auch die Zeichen ketten fürs Patternmatching enthalten als XML abgepspeichert werden.

    Boost interpretiert die CR LF eben dann auch als "Linefeed " und "Carriage Return" und zerschießt dann meine XML... welche dann eben unschän aussieht.

    Ich bruache was in der Art

    \x0d = %CR%
    \x0a = %LF%

    Also eine Art Platzhalter der weder von Boost noch von der Ausgabe z.B. Konsole oder Editfeld fehlinterpreitert werden kann....

    Programm intern brauche ich diese Escapesquenzen ja nicht - nur fürs korrekte senden.

    Danke das beschreibt das Problem ganz gut. Suche gerade nach ner Lösung. Oder was hälst du davon?



  • Sowas dann ca:

    void substringreplace(string string,string search,string replace){
    	int found;
    	found=input.find(search);
    
    	while(found != -1){
    	input.replace(found,search.length(),replace);
    	found=input.find(search,found+1);
    	}
    }
    

    sring= string der veränder werden soll
    search = pattern das verändert werden soll
    replace = pattern das eingefügt werden soll

    z.b: \n\r mit §$ ersetzen... und am Ende eben anderstrum

    Das doch eignetlich eine akzeptable Lösung oder siehst du irgendwelche gefahren?



  • DonDom schrieb:

    Das doch eignetlich eine akzeptable Lösung oder siehst du irgendwelche gefahren?

    Hallo DonDom,

    eine unmittelbare Gefahr sehe ich da keine. Es ist eher so -
    Du hast eindeutig ein IO-Problem vor Dir. Die Lösung, die C++ da zur Verfügung stellt, sind std::istream und std::ostream und Co. Dein 'CR-LF-Problem' ist nur solange eines, solange Du die Zeichenfolge von der seriellen Schnittstelle 1:1 in eine XML-Datei schreiben willst, was nach meiner Erfahrung zum einen nicht üblich ist (es sind doch Personendaten) und zum anderen gar nicht notwendig ist.

    Ich weiß nichts über die Funktion und Protokolle von Gerät 1 bis 3, aber solche Geräte haben Schnittstellen, und die können sich ändern, bzw. sind versioniert. Sind Gerät 1 und 2 unmittelbar mit Gerät 3 'verknüpft' oder besteht die Möglichkeit, dass es einen neuen Typ von Gerät 3 (oder 1) gibt, der dann auch mit den verbleibenden Geräten 'funktionieren' muss.

    Was spricht denn gegen die - nach meiner Meinung offensichtlichen Lösung den Output von Gerät 1 oder 2 zu lesen - also wirklich die Daten zu interpretieren, diese ggf. in eine XML-Datei zu speichern - als das was sie sind - eine Information mit zwei Fließkommazahlen, die einer Person zugeordnet sind. Und später kann man diese aus der XML-Datei lesen, sie weiter verarbeiten und auch nach Gerät 3 schreiben. Letzteres natürlich unter Einhaltung des gerade gültigen Protokolls. Völlig unabhängig wo diese Daten einmal hergekommen sind.

    Speicherst Du das Protokoll mit ab, und versucht auf der Zeichenkette Manipulationen auszuführen (aus einer 4 eine 7 machen - was steckt da hinter?), dann sehe ich die Gefahr, dass bei der geringsten zukünftigen Änderung oder Erweiterung des Systems, zwanghaft dieser Protokollstring eingehalten wird, was unübersichtlich und aufwendig ist. Ich habe schon Systeme gesehen, die intern immer noch mit 'komischen' und völlig ungeeigneten Datenstrukturen operieren, und die Welt um sie herum hat sich bereits geändert und diese Datenstrukturen sind inzwischen überflüssig geworden, aber niemand traut sich ein Resdesign des Systems, weil keiner mehr versteht, was das eigentlich soll.

    Gruß
    Werner



  • Programmintern mache ich die von dir beschriebene Vorgehensweise.

    Ich Speicher Programmintern nur Reguläre ausdrücke auf diese Art und Weise.
    Also z.B.

    "S-R-R [+|-][0-9][0-9].[0-9][0-9] [+|-][0-9][0-9].[0-9][0-9]         E%%S-K-R [+|-][0-9][0-9].[0-9][0-9] [+|-][0-9][0-9].[0-9][0-9]         E%%"
    

    Diese benöige ich dann im Progammverlauf um mit Hilfe von Patternmatchin herauszufinden um welche Zeichenkette es sich handelt. Die Messwerte werden dann in einem von mir erstellten Datentyp gespeichert und per Boost Library serialisiert.

    Theoretisch könnte ich auch noch für die Leereichein ein "sichtbares" Zeichen einführen... 🙂

    Ist jetzt klarer warum ich wirklich eine Zeichenkette in meiner XML benötige?

    Danke für deine Unterstützung und deine Zeit. Wirklich Top


Anmelden zum Antworten