2009-10-13 1 views
50

Ich verwende normalerweise stringstream, um in In-Memory-String zu schreiben. Gibt es eine Möglichkeit, in einen Zeichenpuffer im Binärmodus zu schreiben? Betrachten Sie den folgenden Code ein:Gibt es binäre Speicher Streams in C++

stringstream s; 
s << 1 << 2 << 3; 
const char* ch = s.str().c_str(); 

Der Speicher bei ch wird wie folgt aussehen: 0x313233 - der ASCII-Codes des Zeichens 1, 2 und 3. Ich bin nach einem Weg, um die binären Werte selbst zu schreiben. Das heißt, ich möchte 0x010203 im Speicher haben. Das Problem ist, dass ich in der Lage sein möchte, eine Funktion zu schreiben

void f(ostream& os) 
{ 
    os << 1 << 2 << 3; 
} 

Und entscheiden, welche Art von Stream zu verwenden. Etwas wie dieses:

mycharstream c; 
c << 1 << 2 << 3; // c.data == 0x313233; 
mybinstream b; 
b << 1 << 2 << 3; // b.data == 0x010203; 

Irgendwelche Ideen?

+1

Das ist hex, nicht binär. Warum kannst du nicht 0x01, 0x02 usw. schreiben, obwohl ... das sind eigentlich echte ASCII-Zeichen. – jrockway

+1

Er möchte, dass der Inhalt des Speichers (die tatsächlichen Bytes) 0x010203 (dezimal 66051) ist, nicht die Zeichenfolge "0x010203". – KeithB

+1

Ich habe die Frage geändert. Hoffe es ist jetzt klarer. – FireAphis

Antwort

5

Nun, verwenden Sie nur Zeichen, keine ganzen Zahlen.

s << char(1) << char(2) << char(3); 
30

Zum Lesen und Binärdaten Ströme, einschließlich stringstreams, verwendet, um die Read() und write() Mitgliedsfunktionen zu schreiben. Also

unsigned char a(1), b(2), c(3), d(4); 
std::stringstream s; 
s.write(reinterpret_cast<const char*>(&a), sizeof(unsigned char)); 
s.write(reinterpret_cast<const char*>(&b), sizeof(unsigned char)); 
s.write(reinterpret_cast<const char*>(&c), sizeof(unsigned char)); 
s.write(reinterpret_cast<const char*>(&d), sizeof(unsigned char)); 

s.read(reinterpret_cast<char*>(&v), sizeof(unsigned int)); 
std::cout << std::hex << v << "\n"; 

Dies gibt 0x4030201 auf meinem System.

Edit: Um diese Arbeit transparent zu machen mit der Steck- und Zieh Operatoren (< < und >>), Ihre beste Wette es eine abgeleitete streambuf zu schaffen, die das Richtige tut, und übergeben Sie, dass, was auch immer Bäche möchten Sie benutzen.

+0

Es beantwortet definitiv den ersten Teil der Frage, aber gibt es eine Möglichkeit, die Einfügung immer gleich aussehen zu lassen (d. H. S << a), aber die innere Datendarstellung unterscheidet sich je nach Art des Streams? – FireAphis

+0

Ihr eigener streambuf kann das nicht; Die Formatierung erfolgt in den (nicht-virtuellen) istream- und ostream-Methoden, und das Ergebnis sieht der streambuf vor. –

+0

Die Frage zeigt tatsächlich das In-Memory-Ergebnis '0x010203', während dies wahrscheinlich '0x00000001 0x00000002 0x00000003' (unter der Annahme von' sizeof (int) == 4') ergibt. – MSalters

1

überladen einige ungewöhnliche Operatoren funktioniert ziemlich gut. Hier unten I choosed < = zu überlasten, da sie die gleiche links nach rechts Assoziativität als < < und hat irgendwie einen genauen Blick-and-Feel hat ...

#include <iostream> 
#include <stdint.h> 
#include <arpa/inet.h> 

using namespace std; 

ostream & operator<= (ostream& cout, string const& s) { 
    return cout.write (s.c_str(), s.size()); 
} 
ostream & operator<= (ostream& cout, const char *s) { 
    return cout << s; 
} 
ostream & operator<= (ostream&, int16_t const& i) { 
    return cout.write ((const char *)&i, 2); 
} 
ostream & operator<= (ostream&, int32_t const& i) { 
    return cout.write ((const char *)&i, 4); 
} 
ostream & operator<= (ostream&, uint16_t const& i) { 
    return cout.write ((const char *)&i, 2); 
} 
ostream & operator<= (ostream&, uint32_t const& i) { 
    return cout.write ((const char *)&i, 4); 
} 

int main() { 
    string s("some binary data follow : "); 

    cout <= s <= " (machine ordered) : " <= (uint32_t)0x31323334 <= "\n" 
     <= s <= " (network ordered) : " <= htonl(0x31323334) ; 
    cout << endl; 

    return 0; 
} 

Es gibt mehrere Nachteile sind :

  • die neue Bedeutung von < = können Leser verwirren oder zu unerwarteten Ergebnissen führen:

    cout <= 31 <= 32; 
    

    wird das gleiche Ergebnis wie

    cout <= (31 <= 32); 
    
  • die Endianess werden beim Lesen des Codes nicht gibt nicht eindeutig erwähnt, wie im obigen Beispiel dargestellt.

  • es kann nicht einfach mischen mit < <, weil es nicht auf die derselben Gruppe Vorrang gehört.Normalerweise verwende ich Klammer zu klären, so wie:

    (cout <= htonl(a) <= htonl(b)) << endl; 
    
+2

Das ist ein cooler Proof of Concept, aber beachte, dass die überladenen Operatoren von C++ als böse betrachtet werden, weil sie * dies * erlauben. Die nicht-offensichtliche Überladung von '<<' ist nur gerechtfertigt, weil es sich um eine ** Standard ** Überladung handelt. Es sollten keine neuen Hacky-Überladungen erfunden werden und die Überladung selbst sollte mit großer Sorgfalt verwendet werden. – cubuspl42

0

Für diesen Anwendungsfall am besten implementiert, um einen „rohen Verschiebungs-Operators“:

template <typename T, class... StreamArgs> 
inline std::basic_ostream<StreamArgs...> & 
operator <= (std::basic_ostream<StreamArgs...> & out, T const & data) { 
     out.write(reinterpret_cast<char const *>(&data), sizeof(T)); 
     return out; 
} 

Legen Sie es irgendwo bequem und es verwendet, wie dies :

std::cout <= 1337 <= 1337ULL <= 1337. <= 1337.f; 

Vorteile:

  • verkettbar
  • automatische sizeof()
  • Arrays und Struktur/Klasseninstanzen nimmt auch

Nachteile:

  • unsicher für Nicht-POD-Objekte: Lecks Zeiger und Polsterung
  • Ausgabe ist plattformspezifisch: Padding, Endianess, Integer-Typen
0

Sie können so etwas mit Vorlagen tun. Zum Beispiel:

Es könnte einige Aufräumarbeiten verwenden, aber es ist funktional. Zum Beispiel:

//writing 
std::ofstream f = /*open a file*/; 
int a = 5, b = -1, c = 123456; 
f << bits(a) << bits(b) << bits(c); 

//reading 
std::ifstream f2 = /*open a file*/; 
int a, b, c; 
f >> bits(a) >> bits(b) >> bits(c);