operaciones ejemplos con binarios archivos c++ iostream

c++ - ejemplos - fwrite en c



¿Hay flujos de memoria binarios en C++ (5)

Bueno, solo usa caracteres, no enteros.

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

Usualmente uso stringstream para escribir en la cadena en memoria. ¿Hay una manera de escribir en un búfer de caracteres en modo binario? Considere el siguiente código:

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

La memoria en ch se verá así: 0x313233 - los códigos ASCII de los caracteres 1, 2 y 3. Estoy buscando una manera de escribir los valores binarios. Es decir, quiero 0x010203 en la memoria. El problema es que quiero poder escribir una función.

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

Y decide fuera de qué tipo de flujo utilizar. Algo como esto:

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

¿Algunas ideas?


La sobrecarga de algunos operadores inusuales funciona bastante bien. A continuación, elegí sobrecargar <= porque tiene la misma asociatividad de izquierda a derecha que << y de alguna manera tiene una apariencia cercana ...

#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; }

Hay varios inconvenientes:

  • el nuevo significado de <= puede confundir a los lectores o llevar a resultados inesperados:

    cout <= 31 <= 32;

    no dará el mismo resultado que

    cout <= (31 <= 32);

  • La endianess no se menciona claramente al leer el código, como se ilustra en el ejemplo anterior.

  • no se puede mezclar simplemente con << porque no pertenece al mismo grupo de precedencia. Usualmente uso paréntesis para aclarar tales como:

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


Para este caso de uso, me implementé un "operador de cambio sin procesar":

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; }

Ponlo en un lugar conveniente y úsalo así:

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

Ventajas:

  • chainable
  • sizeof() automático sizeof()
  • toma arrays y estructuras / instancias de clase, también

Desventajas:

  • inseguro para objetos que no son POD: filtra los punteros y el relleno
  • La salida es específica de la plataforma: relleno, endianess, tipos enteros

Para leer y escribir datos binarios en secuencias, incluidas las cadenas de caracteres, use las funciones de miembro read () y write (). Asi que

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";

Esto da 0x4030201 en mi sistema.

Edición: para que esto funcione de manera transparente con los operadores de inserción y extracción (<< y >>), apueste para crear un streambuf derivado que haga lo correcto, y páselo a cualquier secuencia que desee utilizar.


Puedes hacer este tipo de cosas con plantillas. P.ej:

//struct to hold the value: template<typename T> struct bits_t { T t; }; //no constructor necessary //functions to infer type, construct bits_t with a member initialization list //use a reference to avoid copying. The non-const version lets us extract too template<typename T> bits_t<T&> bits(T &t) { return bits_t<T&>{t}; } template<typename T> bits_t<const T&> bits(const T& t) { return bits_t<const T&>{t}; } //insertion operator to call ::write() on whatever type of stream template<typename S, typename T> S& operator<<(S &s, bits_t<T> b) { return s.write((char*)&b.t, sizeof(T)); } //extraction operator to call ::read(), require a non-const reference here template<typename S, typename T> S& operator>>(S& s, bits_t<T&> b) { return s.read((char*)&b.t, sizeof(T)); }

Podría usar algo de limpieza, pero es funcional. P.ej:

//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);