16 Bit Binärzahl in Datei ausgeben/C++ Entsprechung für C# ushort



  • Liebe Community,

    ich versuche, einen C# code in C++ zu portieren. Es sollen Daten, die von einem FPGA kommen, in binärform in eine Datei geschrieben werden.
    Die Daten haben in C# den Datentyp ushort und dafür finde ich keine Entsprechung in C++. Soweit ich es verstehe, repräsentiert ushort eine 16 Bit Binärzahl.
    Ich habe es schon mit unsigned short und ushort in C++ versucht, aber da werden die Daten als Integer weggeschrieben und nicht binär.
    Das gleiche passiert mit char16_t und wchar_t. Ich habe auch versucht, die Daten beim Schreiben in die Datei zu formatieren mit std::ios::binary. Die Datei sah aber anders aus als die vom C# Programm erzeugte.

    Ich kann nicht den ganzen Code hier posten, deshalb etwas aus dem Zusammenhang die C# Funktion zum Wegschreiben der Daten:

            private void AppendToBinDataFile(Sample currentsample)
            {
                if (_BinDataStream != null)
                {
                    if (currentsample.ID == (int)DataID.Unproc || currentsample.ID == (int)DataID.Maxloc)
                    {
                        _BinDataStream.Write((ushort)Constants.SND_CMD_ID_SOP);
                        _BinDataStream.Write((ushort)currentsample.number);
                        _BinDataStream.Write((ushort)currentsample.sample);
                        _BinDataStream.Write((ushort)currentsample.max_FEE.x);
                        _BinDataStream.Write((ushort)currentsample.max_FEE.y);
                       
                        if (currentsample.ID == (int)DataID.Unproc)
                        {
                            foreach (ushort i in currentsample.data)
                            {
                                _BinDataStream.Write(i);
                            }
                            
                        }
                        _BinDataStream.Write((ushort)Constants.SND_CMD_ID_EOP);
                    }
                    _BinDataStream.Flush();
                }
            }
    
    

    Aus dem Constants header:

    public const byte SND_CMD_ID_SOP = 0xAA
    

    currentsample.sample, .number, .max_FEE.x und .max_FEE.y sind als int deklariert. currentsample.data ist ein ushort array.

    Ich wäre sehr dankbar, wenn mir jemand weiterhelfen kann. Ich bin C++ Anfänger und diese Datentyp-Sachen sind für mich eine Herausforderung. Im Grunde suche ich so etwas wie den Datentyp unsigned char, aber mit 16 Bit statt 8 Bit.



  • @astro_programmer sagte in 16 Bit Binärzahl in Datei ausgeben/C++ Entsprechung für C# ushort:

    ushort und dafür finde ich keine Entsprechung in C++

    unsigned short

    Und wenn du sicher gehen willst, dass es genau 16 Bit sind, benutze std::uint16_t aus <cstdint>

    Siehe auch https://en.cppreference.com/w/cpp/types/integer



  • Danke für deine Antwort. Das Problem ist, dass ich die Daten in Binärform brauche. Wenn ich die C# Funktion übernehme und ushort durch unsigned short ersetze, bekomme ich eine Datei mit Dezimalzahlen, keine Binärdatei.



  • Nimm unsigned short und std::fstream. Die notation in C++ ist etwas gewöhnungsbedürftig, da streams mit Streamoperatoren arbeiten. Diese Operatoren sind für alle Basisdatentypen überladen und es könnte in deinem Fall etwa so aussehen:

    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <string>
    
    struct Point
    {
        unsigned short x = 0;
        unsigned short y = 0;
    
        Point() = default;
    };
    
    struct Sample
    {
        unsigned short number = 0;
        unsigned short sample = 0;
        Point max_FEE;
        std::vector<unsigned short> Data; // Frage: ist die Anzahl der Daten konstant? Dann ist std::array vermutlich eher geeignet
    };
    
    void write_sample( std::string const& filename, Sample const& sample )
    {
       std::ofstream ofs( filename, std::fstream::out | std::fstream::bin ); // Datei im Schreibmodus für Binärdaten öffnen  
       ofs << sample.number
           << sample.sample
           << sample.max_FEE.x
           << sample.max_FEE.y;
           // weitere Daten schreiben
    }
    

    Edit:
    Lesefunktion zur Schribfunktion umgebaut.



  • Ich glaube, dass @astro_programmer genau das umgekehrte will von dem, was @DocShoe verstanden hat - also schreiben statt lesen und in Binärform statt in Dezimalform.

    Ich vermute also, dass du, @astro_programmer, https://en.cppreference.com/w/cpp/io/basic_ostream/write suchst.

    Außerdem wäre es hilfreich, wenn du den Code, den du jetzt in C++ hast, zeigen könntest.



  • @astro_programmer sagte in 16 Bit Binärzahl in Datei ausgeben/C++ Entsprechung für C# ushort:

    Wenn ich die C# Funktion übernehme und ushort durch unsigned short ersetze, bekomme ich eine Datei mit Dezimalzahlen, keine Binärdatei.

    Das ist eine Sache der Ausgabefunktion.
    Du hast wohl cout benutzt. Das ist aber für die (menschen-)lesbare Variante - also Dezimalzahlen (oder Ähnliches) - da.



  • @DirkB sagte in 16 Bit Binärzahl in Datei ausgeben/C++ Entsprechung für C# ushort:

    Du hast wohl cout benutzt. Das ist aber für die (menschen-)lesbare Variante - also Dezimalzahlen (oder Ähnliches) - da.

    std::cout.write((char*)&short_val, sizeof short_val);
    

    funktioniert.



  • @wob sagte in 16 Bit Binärzahl in Datei ausgeben/C++ Entsprechung für C# ushort:

    Ich glaube, dass @astro_programmer genau das umgekehrte will von dem, was @DocShoe verstanden hat - also schreiben statt lesen und in Binärform statt in Dezimalform.

    gefixt



  • Vielen Dank, @all ! Wahrscheinlich muss ich die Ausgabe wirklich anders formattieren. Ich probiere eure Vorschläge mal aus. Hier mein C++ code:

    struct Maximum
        {
            int x;
            int y;
        };
    
    
    struct Sample
        {
            int ID;
            int number;
            int sample;
            Maximum max_SW;
            Maximum max_FEE;
            std::vector<unsigned short> data; //resize later
            std::vector<unsigned char> rawdata;
        };
    
    Sample CurrentSample;
    
    std::ofstream BinDataFile;
    
    void AppendToBinDataFile(Sample currentsample)
        {
            if (currentsample.ID == (int)REC_CMD_ID_DATA_UNPROC || currentsample.ID == (int)REC_CMD_ID_DATA_MAXLOC)
            {
                BinDataFile << (unsigned short)SND_CMD_ID_SOP;
                BinDataFile << (unsigned short)currentsample.number;
                BinDataFile << (unsigned short)currentsample.sample;
                BinDataFile << (unsigned short)currentsample.max_FEE.x;
                BinDataFile << (unsigned short)currentsample.max_FEE.y;
               
                if (currentsample.ID == (int)REC_CMD_ID_DATA_UNPROC)
                {
                    for(std::size_t i = 0; i < currentsample.data.size(); i++)
                    {
                        BinDataFile << currentsample.data[i];
                    }
                    
                }
                BinDataFile << (unsigned short)SND_CMD_ID_EOP;
            }
        }
    
    
    

    Ist das Problem, dass ich den << Operator benutze?



  • Ja, damit werden die Zahlen lesbar (als String) ausgegeben.
    Du mußt die write-Funktion zur binären Ausgabe benutzen.

    PS: Der Code von @DocShoe ist also auch diesbzgl. falsch...
    PPS: @DirkB: Du meinst wohl eher

    Du hast wohl << benutzt.



  • @astro_programmer sagte in 16 Bit Binärzahl in Datei ausgeben/C++ Entsprechung für C# ushort:

    Ist das Problem, dass ich den << Operator benutze?

    Ja, siehe mein Post von oben. Link von da:
    https://en.cppreference.com/w/cpp/io/basic_ostream/write

    Zitat aus der verlinkten Seite:

    Example
    This function may be used to output object representations, i.e. binary output
    (Beispielcode ist auch da)



  • @astro_programmer

    Ich hätte schwören können, dass es einen bin Manipulator für streams gibt, aber da habe ich mich wohl getäuscht. Die Streamoperatoren funktionieren dann nicht, selbst wenn die Datei im Binary Modus geöffnet wurde. Du musst die Daten dann tatsächlich mit write schreiben, was durch die Konvertierung erschwert wird. Das hier wäre mein erster Ansatz, erfordert allerdings etwas Verständnis über die Funktionsweise von Operatorüberladung

    #include <iostream>
    #include <fstream>
    
    std::ostream& stream_binary( std::ostream& os, unsigned short val )
    {
        os.write( &val, sizeof( val );
        return os;
    }
    
    void write_sample( std::string const& filename, Sample const& sample )
    {
        ofstream ofs( filename, std::fstream::out | std::fstream::bin );
        ofs << stream_binary( sample.number )
            << stream_binary( sample.sample )
            << stream_binary( sample.max_FEE.x )
            << stream_binary( sample.max_FEE.y );
          if( !sample.Data.empty() )
          {
             os.write( &sample.Data[0], sample.Data.size() * sizeof( char ) );
          }
    }


  • @DocShoe sagte in 16 Bit Binärzahl in Datei ausgeben/C++ Entsprechung für C# ushort:

    Ich hätte schwören können, dass es einen bin Manipulator für streams gibt, aber da habe ich mich wohl getäuscht. Die Streamoperatoren funktionieren dann nicht, selbst wenn die Datei im Binary Modus geöffnet wurde.

    Das sorgt nur dafür, dass, das logische Zeilenende '\n' beim Lesen oder Schreiben in das unter dem jeweiligen Betriebssystem verwendete Zeilenende übersetzt wird (Edit: und eine Behandlung des EOF-Zeichen '\0x1A'). Siehe https://en.cppreference.com/w/cpp/io/c/FILE#Binary_and_text_modes

    Also immer binary verwenden, außer man hat Text in Zeilen.

    std::ostream& stream_binary( std::ostream& os, unsigned short val )

    Finde ich so ein bisschen schwierig, weil sich stream_binary nach was generischem anhört, bei dir aber alles in unsigned short gewandelt wird.

    ofstream ofs( filename, std::fstream::out | std::fstream::bin );

    Ein ofstream ist schon automatisch out. (und ist es nicht std::ios::binary?) Das brauchst du nicht nochmal anzugeben.



  • @wob

    Das stream_binary heisst schon absichtlich so, langfristig kann man sowas auch zu

    template<typename T>
    std::ostream stream_binary( std::ostream& os, T const& val )
    {
        os.write( &val, sizeof( val ) );
        return os;
    }
    

    umbauen.

    Nur dann braucht man Typüberprüfungen für T, und ich wollte TE hier keine SFINAE Konstrukte um die Ohren hauen.

    Zu den ostream-Flags:
    Ja, ich weiß, dass ostream Objekte standardmäßig das out-flag gesetzt haben. Ich weiß nur nicht, ob die den Parameter ergänzen, wenn man den Konstruktor mit Namen und Flags benutzt, oder ob der Parameter 1:1 übernommen wird. Daher habe ich ihn explizit gesetzt.



  • Hmmm... Von den Fehler in deinem Code mal abgesehen (mindestens: reinterpret_cast<char*> fehlt & stream_binary kann nicht einfach so eine Funktion sein - woher soll denn operator<< wissen, dass diese Fkt nun << gerufen werden soll und nicht das Ergebnis in die Datei gehen soll) - ich habe kein Problem mit write. Denn mit write sieht man ja gleich, dass hier rohe Daten geschrieben werden.



  • @wob

    Jau, ich lass das. Ohne weitere Konstrukte wird das nix, und ich wil das hier auch nicht ausufern lassen. Nur eine einfacher write Aufruf wird´s nicht werden, da die Daten als int vorliegen und als unsigned short geschrieben werden sollen.



  • So schwer ist das nicht, einen eigenen Stream-Manipulator (mit Parametern) zu schreiben - man muß dazu nur eine Klasse entwickeln, s.a. Writing Your Own Stream Manipulators.

    Einen leicht anderen Ansatz (mit eigener Binärstream-Klasse binary_ostream_wrapper) habe ich in Force stream << >> to interpret as binary gefunden.



  • @Th69

    Ne, schwierig ist das nicht. Jedenfalls nicht, wenn man sich etwas Zeit nimmt und das nicht nur im Forum ungetestet runtertippt. Aber dazu habe ich atm weder Zeit noch Lust.



  • So, jetzt habe ich das endlich ausprobiert. Nochmal vielen Dank an alle für's mit Überlegen und die hilfreichen Antworten! 🙂 Ich habe es so gemacht wie @manni66 vorgeschlagen hat und es funktioniert. Meine Funktion sieht jetzt so aus:

    void AppendToBinDataFile(Sample currentsample)
        {
            if (currentsample.ID == (int)REC_CMD_ID_DATA_UNPROC || currentsample.ID == (int)REC_CMD_ID_DATA_MAXLOC)
            {
                unsigned short SND_CMD_ID_SOP_ushort = (unsigned short)SND_CMD_ID_SOP;
                unsigned short SND_CMD_ID_EOP_ushort = (unsigned short)SND_CMD_ID_EOP;
                unsigned short number_ushort = (unsigned short)currentsample.number;
                unsigned short sample_ushort = (unsigned short)currentsample.sample;
                unsigned short maxFEEx_ushort = (unsigned short)currentsample.max_FEE.x;
                unsigned short maxFEEy_ushort = (unsigned short)currentsample.max_FEE.y;
                BinDataFile.write((char*)&SND_CMD_ID_SOP_ushort, sizeof(SND_CMD_ID_SOP_ushort));
                BinDataFile.write((char*)&number_ushort, sizeof(number_ushort));
                BinDataFile.write((char*)&sample_ushort, sizeof(sample_ushort));
                BinDataFile.write((char*)&maxFEEx_ushort, sizeof(maxFEEx_ushort));
                BinDataFile.write((char*)&maxFEEy_ushort, sizeof(maxFEEy_ushort));
               
                if (currentsample.ID == (int)REC_CMD_ID_DATA_UNPROC)
                {
                    for(std::size_t i = 0; i < currentsample.data.size(); i++)
                    {
                        unsigned short data_ushort = (unsigned short)currentsample.data[i];
                        BinDataFile.write((char*)&data_ushort, sizeof(data_ushort));
                    }
                    
                }
                BinDataFile.write((char*)&SND_CMD_ID_EOP_ushort, sizeof(SND_CMD_ID_EOP_ushort));
            }
            BinDataFile.flush();
        }
    
    

Anmelden zum Antworten