[Streams] Verschlüsselung - Welche Facette?
-
Hallo!
Ich will für I/O-Streams eine Funktionalität für Verschlüsselung bereitstellen.
Also habe ich mir überlegt, dass man vielleicht einfach eine eigene Facette aus einer Standardfacette ableitet, nur finde ich keine Facette, die sich um die Eingabe von char/wchar_t kümmert (etwa sowas wie char_put, aber es gibt nur money_put, num_put etc.).Anschließend will ich dann per xalloc() und iword() eigene Flags einstellen, um für eigene Manipulatoren verschiedene Verschlüsselungsalgorithmen bereitzustellen.
TL;DR: Ziel soll sowas sein:
char* raw_data = get_raw_data(); std::stringstream ss; ss.imbue(std::locale(std::locale(), new crypt_facet<char>)); ss << base64 << raw_data; // ließ base64-verschlüsselte Daten std::string other_crypt; ss << xor("4hsgsu4") << decrypted; // XOR-verschlüssele Daten mit 4hsgsu4 und schreibe in other_cryptGibt es da eine Facette wie num_get oder num_put, wo ich einfach do_get() und do_put() so modifiziere, wie ich es brauche?
-
Also ich würde es ja auf Ebene des Streambuf implementieren, da kommen alle Zeichen vorbei. Die facets sind ja eher dafür da, bestimmte Zeichenfolgen zu interpretieren. Das gibt's hier auch im Forum schon fertig von mir für XOR-Verschlüsselung.
-
Jodocus schrieb:
Gibt es da eine Facette wie num_get oder num_put, wo ich einfach do_get() und do_put() so modifiziere, wie ich es brauche?
Hallo Jodocus,
die einzige Facette, die dafür in Frage kommt, ist std::codecvt.
Du musst nur darauf achten, dass der konkrete Streambuf diese Facette auch benutzt. Das Defaultverhalten ist: er tut es nicht. In der Praxis tut es ein std::basic_filebuf<> aber meistens schon. Schau Dir die Implementierung der imbue-Methode des jeweiligen Streams an.Im Zweifel kannst Du auch einen eigenen streambuf zwischen Stream und vorhandenen Streambuf schalten, der die Facette nutzt oder eben gleich selbst die Codierung vornimmt. Wie der von SeppJ:
SeppJ schrieb:
Also ich würde es ja auf Ebene des Streambuf implementieren, da kommen alle Zeichen vorbei. Die facets sind ja eher dafür da, bestimmte Zeichenfolgen zu interpretieren. Das gibt's hier auch im Forum schon fertig von mir für XOR-Verschlüsselung.
Der Link zum Thread ist in diesem Fall auch hilfreich: <http://www.c-plusplus.net/forum/p2228469#2228469>
Gruß
Werner
-
Schade, ich dachte, dass das ein interessanter Bereich für Locales gewesen sei. Aber jetzt werde ich mich wohl an eine Definition mit Streampuffern machen und hoffe, dass ich trotzdem so ein Interface mit Manipulatoren hinbekomme.
Danke
-
Jodocus schrieb:
Schade, ich dachte, dass das ein interessanter Bereich für Locales gewesen sei. Aber jetzt werde ich mich wohl an eine Definition mit Streampuffern machen und hoffe, dass ich trotzdem so ein Interface mit Manipulatoren hinbekomme.
hallo Jodocus,
Das würde ich so nicht sagen. Wie schon erwähnt, mit einem fstream, der ja einen basic_filebuf bedient, sollte es gehen. Ich habe das Beispiel von SeppJ mal abgeändert:
#include "XOR_coder.h" #include <algorithm> // swap #include <fstream> #include <string> #include <iostream> int main() { using namespace std; const char KEY = 'x'; { // -- Schreiben ofstream out("test.dat", ios_base::binary ); out.imbue( locale( out.getloc(), new XOR_coder< char >( KEY ) ) ); out << "Hallo Welt!\n"; for( int i0=1, i1=0; i1 < 100; i0 += i1, swap( i0, i1 ) ) out << i1 << "\n"; } { // -- ohne XOR-Coder lesen ifstream in("test.dat", ios_base::binary ); if( !in.is_open() ) { cerr << "Fehler beim Oeffnen der Datei" << endl; return -2; } cout << "Datei Rohdaten:" << endl; int idx = 0; for( istreambuf_iterator< char > i(in.rdbuf()); i != istreambuf_iterator< char >(); ++i, ++idx ) { if( idx > 0 && idx %8 == 0 ) cout << endl; cout << (isprint( *i, cout.getloc() )? *i: '.'); } cout << endl; } { // -- mit XOR-Coder lesen ifstream in("test.dat", ios_base::binary ); if( !in.is_open() ) { cerr << "Fehler beim Oeffnen der Datei" << endl; return -2; } cout << "\nDatei decodiert:" << endl; in.imbue( locale( in.getloc(), new XOR_coder< char >( KEY ) ) ); string ueberschrift; if( getline( in, ueberschrift ) ) { cout << ueberschrift << endl; for( int zahl; in >> zahl; ) cout << zahl << " "; cout << endl; } if( !in.eof() ) cerr << "Fehler beim Lesen" << endl; } }In den Zeilen 13 und 43 wird jeweils eine neue Locale mit der codecvt XOR_coder eingehängt. Die Ausgabe dieser kleinen Demo ist:
Datei Rohdaten: 0....X/. ..YrHrIr IrJrKrMr @rIKrJIr KLrMMr@A r Datei decodiert: Hallo Welt! 0 1 1 2 3 5 8 13 21 34 55 89Manipulatoren brauchst Du da keine.
Und hier die Klasse XOR_coder:
// Datei: XOR_coder.h #ifndef CPLUSPLUSFORUM_XOR_CODER_H_ #define CPLUSPLUSFORUM_XOR_CODER_H_ #include <locale> // std::codecvt template< typename T > class XOR_coder : public std::codecvt< T, T, std::mbstate_t > { typedef std::codecvt< T, T, int > base_type; public: explicit XOR_coder( T key, std::size_t refs = 0 ) : base_type( refs ) , key_( key ) {} protected: virtual result do_out( state_type& /*state*/, const intern_type* from, const intern_type* from_end, const intern_type*& from_next, extern_type* to, extern_type* to_end, extern_type*& to_next ) const { for( ; from != from_end && to != to_end; ++from, ++to ) *to = *from ^ key_; from_next = from; to_next = to; return ok; } virtual result do_in( state_type& /*state*/, const extern_type* from, const extern_type* from_end, const extern_type*& from_next, intern_type* to, intern_type* to_end, intern_type*& to_next ) const { for( ; from != from_end && to != to_end; ++from, ++to ) *to = *from ^ key_; from_next = from; to_next = to; return ok; } virtual result do_unshift( state_type& /*state*/, extern_type* to, extern_type* /*to_end*/, extern_type*& to_next) const { to_next = to; return ok; } virtual int do_encoding() const /*noexcept*/ { return 1; } virtual bool do_always_noconv() const /*noexcept*/ { return false; } private: T key_; }; #endif // CPLUSPLUSFORUM_XOR_CODER_H_Das funktioniert unter VS2010; unter VS2005 würde es nicht gehen, da dort die mitgelieferte basic_filebuf-Implementierung buggy ist.
Gruß
Werner@Edit: 'ios_base::binary' hinzugefügt, sonst kann es zu Fehlern kommen.
-
Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
-
> Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
Wie meinst du das?
@ Werner Salomon: Ja, so ähnlich habe ich es mir vorgestellt. Aber ich kann nun mal nicht immer einen filebuf gebrauchen. Leider finde ich in den Referenzen nirgends, ob und wann ein Streampuffer eine bestimmte Facette nutzt. Was mache ich z.B., wenn ich mit einem stringbuf arbeiten will? Stringbuf scheint keine nützliche Facette für diesen Zweck zu benutzen.
-
Jodocus schrieb:
Ja, so ähnlich habe ich es mir vorgestellt. Aber ich kann nun mal nicht immer einen filebuf gebrauchen. Leider finde ich in den Referenzen nirgends, ob und wann ein Streampuffer eine bestimmte Facette nutzt. Was mache ich z.B., wenn ich mit einem stringbuf arbeiten will? Stringbuf scheint keine nützliche Facette für diesen Zweck zu benutzen.
Bei stringbuf ist lt. Standard überhaupt keine Facette garantiert. Warum auch? schreibt doch eh' nur in den Speicher.
Ansonsten bleibt immer noch der Vorschlag von SeppJ - mit dem geht es in jeden Streambuf.cooky451 schrieb:
Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
Bei einer XOR-Verschlüsselung ??

keine Ahnung wie Du das meinst.Ich habe in meinem Listing oben noch das binary hinzugefügt. Sonst kann es bei bestimmten Byte-Konstellationen zu Fehlern bei der Windows-internen Code-Konvertierung kommen. Dies führt dann letztlich zu Lesefehlern.
Gruß
Werner
-
Werner Salomon schrieb:
cooky451 schrieb:
Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
Bei einer XOR-Verschlüsselung ??

keine Ahnung wie Du das meinst.Na ja Jodocus' Eingangspost sah mir so aus als sollte die Verschlüsselung auch irgendwie genutzt werden. Und das wird dann wohl kein XOR mehr sein. Was ja auch nicht schlimm wäre, aber es gibt eben nicht nur Stromverschlüsselungen und statt CTR will man ja vielleicht auch mal CBC nutzen, insofern war meine Frage ob man das überhaupt über diese Methode realisieren könnte.
-
cooky451 schrieb:
Werner Salomon schrieb:
cooky451 schrieb:
Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
Bei einer XOR-Verschlüsselung ??

keine Ahnung wie Du das meinst.Na ja Jodocus' Eingangspost sah mir so aus als sollte die Verschlüsselung auch irgendwie genutzt werden. Und das wird dann wohl kein XOR mehr sein. Was ja auch nicht schlimm wäre, aber es gibt eben nicht nur Stromverschlüsselungen und statt CTR will man ja vielleicht auch mal CBC nutzen, insofern war meine Frage ob man das überhaupt über diese Methode realisieren könnte.
Da hilft dann auch wieder die Implementierung auf streambuf-Ebene. Dann weiß man, der Datenstrom ist zu Ende, wenn der streambuf stirbt.
-
cooky451 schrieb:
Werner Salomon schrieb:
cooky451 schrieb:
Und wie sieht's mit Padding aus? Kann man feststellen wann die Nachricht zu ende ist bzw. der Stream zerstört wird?
Bei einer XOR-Verschlüsselung ??

keine Ahnung wie Du das meinst.Na ja Jodocus' Eingangspost sah mir so aus als sollte die Verschlüsselung auch irgendwie genutzt werden. Und das wird dann wohl kein XOR mehr sein. Was ja auch nicht schlimm wäre, aber es gibt eben nicht nur Stromverschlüsselungen und statt CTR will man ja vielleicht auch mal CBC nutzen, insofern war meine Frage ob man das überhaupt über diese Methode realisieren könnte.
Ach so. Ja codecvt stellt alles zur Verfügung, falls ich das richtig verstanden habe. Du kannst eine beliebige Anzahl von Zeichen in eine beliebig andere Anzahl konvertieren. Padding geht wohl mit unshift - wenn wir das gleiche meinen. Wobei der Stream dabei nicht zerstört wird. Man kann dann aber kontrollieren wann die Konvertierung bzw. (En)Codierung eines Pakets von Bytes abgeschlossen ist.
Gruß
Werner
-
Warum willst du unbedingt IOStreams benutzen? Diese API wurde vor 20 Jahren designed und ist dementsprechend eklig.
-
So, ich hab mich jetzt mal rangemacht und habe das rausbekommen:
#include <iostream> #include <locale> #include <sstream> #include <string> #include <type_traits> #include <vector> template <typename Ch, typename Traits = std::char_traits<Ch>> class crypto : public std::locale::facet { bool crypt; public: crypto(bool c, int i = 0) : std::locale::facet(i), crypt(c) { } ~crypto() { } virtual std::basic_string<Ch, Traits> encrypt(const std::basic_string<Ch, Traits>&) const = 0; virtual std::basic_string<Ch, Traits> decrypt(const std::basic_string<Ch, Traits>&) const = 0; bool do_crypt() const { return crypt; } static std::locale::id id; }; template <typename Ch, typename Traits> std::locale::id crypto<Ch, Traits>::id; class crypto_base64 : public crypto<char> { static const std::string base64_chars; public: crypto_base64(bool do_crypt) : crypto<char>(do_crypt) { } std::string encrypt(const std::string& s) const { std::string encoded; unsigned index = 0; auto num_bytes = s.length(); for(; index + 2 < s.length(); index += 3) { encoded.push_back(base64_chars[s[index] >> 2]); encoded.push_back(base64_chars[((s[index] & 3) << 4) + (s[index + 1] >> 4)]); encoded.push_back(base64_chars[((s[index + 1] & 15) << 2) + (s[index + 2] >> 6)]); encoded.push_back(base64_chars[s[index + 2] & 63]); } if(num_bytes - index == 2) { encoded.push_back(base64_chars[s[index] >> 2]); encoded.push_back(base64_chars[((s[index] & 3) << 4) + (s[index + 1] >> 4)]); encoded.push_back(base64_chars[((s[index + 1] & 15) << 2) + (0 >> 6)]); encoded.push_back('='); } else if(num_bytes - index == 1) { encoded.push_back(base64_chars[s[index] >> 2]); encoded.push_back(base64_chars[((s[index] & 3) << 4) + (0 >> 4)]); encoded += "=="; } return encoded; } std::string decrypt(const std::string& s) const { std::vector<unsigned> bytes(s.length()); for(std::size_t n = 0; n < bytes.size(); ++n) { if(s[n] == '=') bytes[n] = 0; else bytes[n] = base64_chars.find(s[n]); } std::string decoded; for(std::size_t n = 0; n + 3 < bytes.size(); n += 4) { decoded.push_back((bytes[n] << 2) + (bytes[n + 1] >> 4)); decoded.push_back((bytes[n + 1] << 4) + (bytes[n + 2] >> 2)); decoded.push_back((bytes[n + 2] << 6) + bytes[n + 3]); } return decoded; } }; const std::string crypto_base64::base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; template <typename Ch, typename Traits = std::char_traits<Ch>> class crypto_xor : public crypto<Ch, Traits> { const char* sbegin_; std::size_t ssize_; public: crypto_xor(bool do_crypt, const char* begin = 0, std::size_t size = 0) : crypto<Ch, Traits>(do_crypt), sbegin_(begin), ssize_(size) { } std::basic_string<Ch, Traits> encrypt(const std::basic_string<Ch, Traits>& s) const { if(!ssize_) return s; std::basic_string<Ch, Traits> crypt = s; int index = -1; for(std::size_t n = 0; n != s.size(); ++n) { ++index; if(index == ssize_) index = 0; crypt[n] = crypt[n] ^ sbegin_[index]; } return crypt; } std::basic_string<Ch, Traits> decrypt(const std::basic_string<Ch, Traits>& s) const { return encrypt(s); } }; template <typename Ch, typename Traits = std::char_traits<Ch>> class crypt_buffer : public std::basic_streambuf<Ch, Traits> { std::basic_streambuf<Ch, Traits>* buf_; public: crypt_buffer(std::basic_streambuf<Ch, Traits>* buf) : buf_(buf) { } protected: std::streamsize xsputn(const Ch* ptr, std::streamsize count) { std::string str(ptr, static_cast<unsigned>(count)); if(std::has_facet<crypto<Ch, Traits>>(getloc())) { const crypto<Ch, Traits>& facet = std::use_facet<crypto<Ch, Traits>>(getloc()); str = facet.do_crypt() ? facet.encrypt(str) : facet.decrypt(str); } pubimbue(std::locale()); return buf_->sputn(str.c_str(), str.length()) == str.length() ? count : 0; } std::streamsize xsgetn(Ch* ptr, std::streamsize count) { std::streamsize ret = buf_->sgetn(ptr, count); std::basic_string<Ch, Traits> str(ptr, static_cast<std::size_t>(count)); if(std::has_facet<crypto<Ch, Traits>>(getloc())) { const crypto<Ch, Traits>& facet = std::use_facet<crypto<Ch, Traits>>(getloc()); str = facet.do_crypt() ? facet.encrypt(str) : facet.decrypt(str); } std::streamsize to_copy = count > str.length() ? str.length() : count; Traits::copy(ptr, str.c_str(), static_cast<std::size_t>(to_copy)); return to_copy; } }; std::ios& as_base64(std::ios& ios) { ios.imbue(std::locale(std::locale(), new crypto_base64(true))); return ios; } std::ios& from_base64(std::ios& ios) { ios.imbue(std::locale(std::locale(), new crypto_base64(false))); return ios; } struct xor_helper { xor_helper(const std::string& s, bool c) : salt(s), do_crypt(c) { } template <typename Ch, typename Traits> std::basic_ostream<Ch, Traits>& function(std::basic_ostream<Ch, Traits>& ios) { ios.imbue(std::locale(std::locale(), new crypto_xor<Ch, Traits>(do_crypt, salt.c_str(), salt.size()))); return ios; } const std::string& salt; bool do_crypt; }; xor_helper as_xor(const std::string& salt) { return xor_helper(salt, true); } xor_helper from_xor(const std::string& salt) { return xor_helper(salt, false); } template <typename Ch, typename Traits> std::basic_ostream<Ch, Traits>& operator <<(std::basic_ostream<Ch, Traits>& ios, xor_helper& xorh) { return xorh.function(ios); }Testprogramm:
int main() { std::streambuf* old = std::cout.rdbuf(new crypt_buffer<char>(std::cout.rdbuf())); std::cout << as_base64 << "Hallo, Welt!" << from_base64 << "SGFsbG8gV2VsdCE="; std::cout << as_xor("x") << "Hallo, Welt!"; delete std::cout.rdbuf(old); }Das Manipulator-Interface geht ganz gut. Nur zwei Dinge stören spontant:
~~1. Eigentlich gilt der Manipulator nur für die nächste Aktion, also habe ich pubimbue() benutzt, um das Locale zurückzusetzen. Leider ergibt has_facet<crypt> immernoch true.
~~
2. operator >>() mit istream benutzt scheinbar nicht sgetn(), sodass meine xsgetn()-Überladung nicht aufgerufen wird. Woran liegt das?
-
So, ich habe das erste Problem gelöst (pubimbue() statt buf_->pubimbue(), logisch).
Für das zweite Problem habe ich noch keine Idee, da ich scheinbar underflow() und uflow() definieren muss. Aber wie soll ich einen Base64-Algorithmus durchführen, wenn ich noch nicht einmal alle Zeichen habe?Außerdem funktioniert meine Lösung nicht mit std::stringstream, weil std::stringstream::rdbuf() doch tatsächlich std::basic_ios<>::rdbuf() überdeckt und dadurch inbesondere std::stringstream::rdbuf(std::streambuf*) nicht exisitiert. Das sieht für mich wie ein Bug (entweder in Visual Studio oder gar im Standard selbst) aus. Warum sollte ich den Streampuffer von Stringstreams nicht beeinflussen können?
-
Jodocus schrieb:
Für das zweite Problem habe ich noch keine Idee, da ich scheinbar underflow() und uflow() definieren muss. Aber wie soll ich einen Base64-Algorithmus durchführen, wenn ich noch nicht einmal alle Zeichen habe?
Hallo Jodocus,
Ja - Du musst mindestens eine der beiden Methoden in Deinem streambuf implementieren, damit Du auch alle Zeichen erwischst. Die Implementierung von xgetn ist dann gar nicht notwendig. Für die Ausgabe gilt das genauso für overflow bzw. xputn.
Und wenn man z.B. es in underflow einbaut, so muss man sich bei der Konvertierung immer den Zustand merken, wo man sich gerade befindet. Dann kann man auch Zeichen für Zeichen umsetzen. Ist natürlich ein wenig aufwendiger zu implementieren.Jodocus schrieb:
Außerdem funktioniert meine Lösung nicht mit std::stringstream, weil std::stringstream::rdbuf() doch tatsächlich std::basic_ios<>::rdbuf() überdeckt und dadurch insbesondere std::stringstream::rdbuf(std::streambuf*) nicht existiert. Das sieht für mich wie ein Bug (entweder in Visual Studio oder gar im Standard selbst) aus. Warum sollte ich den Streampuffer von Stringstreams nicht beeinflussen können?
Nein - das ist kein Bug, das ist Standard. In diesem Fall würde ich auch nur den basic_stringbuf in Kombination mit einem nacktem std::istream benutzen. Dann hast Du alles, was Du brauchst - eben als zwei Klassen und nicht als eine.
Ich habe mich mal hingesetzt und versucht eine codecvt-Facette für die base64-Konvertierung zu basteln.
#ifndef CPLUSPLUSFORUM_CODECVT_BASE64_H_ #define CPLUSPLUSFORUM_CODECVT_BASE64_H_ #include <locale> // std::codecvt #include <cassert> #include <string> // char_traits namespace base64_detail { // 0 1 2 3 4 5 6 // 0123456789012345678901234567890123456789012345678901234567890123 const char tab[]= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; template< typename S > char write( S& state, const char*& src ) { const int shift = ((state & 0x300) >> 7) + 2; const char res = tab[(state & 0x3f) | static_cast< unsigned char >( *src ) >> shift]; if( ((state += 0x100) &= 0x300) != 0 ) state |= (*src++ << (6 - shift)) & 0x3f; return res; } template< typename S > void read( S& state, char from, char*& to ) { const char* q = std::char_traits< char >::find( tab, 64, from ); if( q ) { const int c = q - tab; const int shift = ((state & 0x300) >> 7) + 2; // e{2,4,6,8} if( shift > 2 ) *to++ = (state & 0xff) | (c >> (8 - shift)); state = ((state + 0x100) & 0x300) | ((c << shift) & 0xff); } } } template< typename S = std::mbstate_t > class base64 : public std::codecvt< char, char, S > { typedef std::codecvt< char, char, S > base_type; public: explicit base64( std::size_t refs = 0 ) : base_type( refs ) {} protected: // -- out: 3 char -> 4 Byte virtual result do_out( state_type& state, const intern_type* from, const intern_type* from_end, const intern_type*& from_next, extern_type* to, extern_type* to_end, extern_type*& to_next ) const { for( ; from != from_end && to != to_end; ++to ) *to = base64_detail::write( state, from ); from_next = from; to_next = to; return ok; } virtual result do_unshift( state_type& state, extern_type* to, extern_type* to_end, extern_type*& to_next) const { const char* padding = "\0"; for( const char* src = padding; state != 0 && to != to_end && src != padding; ++to ) *to = base64_detail::write( state, src ); to_next = to; return ok; } // -- in: 4 Byte -> 3 char virtual result do_in( state_type& state, const extern_type* from, const extern_type* from_end, const extern_type*& from_next, intern_type* to, intern_type* to_end, intern_type*& to_next ) const { for( ; from != from_end && to != to_end; ++from ) base64_detail::read( state, *from, to ); from_next = from; to_next = to; return ok; } virtual int do_encoding() const /*noexcept*/ { return -1; // Returns: -1 if the encoding of the externT sequence is state-dependent } virtual bool do_always_noconv() const /*noexcept*/ { return false; } }; #endif // CPLUSPLUSFORUM_CODECVT_BASE64_H_wobei noch die Methoden do_length und do_max_length fehlen. Aber da diese Methode von der basic_filebuf-Implementierung beim Visual Studio gar nicht aufgerufen werden, habe ich mir das gespart. Abgesehen davon habe ich selten für so wenig Code so lange gebraucht - nun gut.
Die Anwendung ist dann wie gehabt:
#include "codecvt_base64.hpp" // (s.o.) #include <fstream> #include <string> #include <iostream> int main() { using namespace std; { // -- Schreiben ofstream out("test.dat", ios_base::binary ); out.imbue( locale( out.getloc(), new base64<> ) ); out << "Hallo Welt" << "\n"; for( int i0=1, i1=0; i1 < 100; i0 += i1, swap( i0, i1 ) ) out << i1 << "\n"; } { // -- Lesen ifstream in("test.dat", ios_base::binary ); if( !in.is_open() ) { cerr << "Fehler beim Oeffnen der Datei" << endl; return -2; } cout << "\nDatei decodiert:" << endl; in.imbue( locale( in.getloc(), new base64<> ) ); string ueberschrift; if( getline( in, ueberschrift ) ) { cout << ueberschrift << endl; for( int zahl; in >> zahl; ) cout << zahl << " "; cout << endl; } if( !in.eof() ) { cerr << "Fehler beim Lesen" << endl; } } }Sag' uns bitte noch, warum es ausgerechnet ein stringstream sein muss. Schickst Du die Bytes anschließend über einen Socket oder dergleichen?
Gruß
Werner
-
Entschuldige Werner, ich dachte, der Thread wäre tot. Danke für deine Mühe, durch diesen Code muss ich mich erst einmal durchbeißen. (Auch wenn per Google zu Themen wie codecvt kaum noch etwas anderes als Referenzen findet).
> Sag' uns bitte noch, warum es ausgerechnet ein stringstream sein muss. Schickst Du die Bytes anschließend über einen Socket oder dergleichen?
Ja, ich wollte es für jeden Zweck nutzbar haben. Auf jeden Fall nicht nur für Dateien.