Есть ли потоки двоичной памяти в C ++

Я обычно использую stringstreamдля записи в строку в памяти. Есть ли способ записи в буфер символов в двоичном режиме? Рассмотрим следующий код:

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

Память в chбудет выглядеть следующим образом: 0x313233 - ASCII-коды символов 1, 2 и 3. Я ищу способ записать двоичные значения сами. То есть я хочу 0x010203 в памяти. Проблема в том, что я хочу иметь возможность написать функцию

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

И решить за пределами, какой поток использовать. Что-то вроде этого:

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

Любые идеи?

13.10.2009 10:01:33
Это гекс, а не бинарный. Почему вы не можете написать 0x01, 0x02 и т. Д., Хотя ... в конце концов, это настоящие символы ASCII.
jrockway 13.10.2009 10:03:24
Он хочет, чтобы содержимое памяти (фактические байты) было 0x010203 (66051 десятичное), а не строка «0x010203».
KeithB 13.10.2009 11:18:06
Я изменил вопрос. Надеюсь, теперь все понятно.
FireAphis 13.10.2009 14:20:09
Отличный вопрос. Жаль, что невозможно дать хороший ответ, потому что это ошибка проектирования в стандартных библиотеках.
pyon 12.01.2013 03:50:08
5 ОТВЕТОВ

Ну, просто используйте символы, а не целые числа.

s << char(1) << char(2) << char(3);
3
13.10.2009 10:05:45

Для чтения и записи двоичных данных в потоки, включая строковые потоки, используйте функции-члены read () и write (). Так

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

Это дает 0x4030201в моей системе.

Редактировать: чтобы сделать это прозрачным с помощью операторов вставки и извлечения (<< и >>), лучше всего создать производный поток потоков, который будет работать правильно, и передать его любым потокам, которые вы хотите использовать.

36
13.01.2017 17:54:16
Это определенно отвечает на первую часть вопроса, но есть ли способ сделать так, чтобы вставка выглядела всегда одинаково (т.е. s << a), но внутреннее представление данных различается в зависимости от типа потока?
FireAphis 13.10.2009 14:17:03
Ваш собственный потоковый поток не может этого сделать; форматирование выполняется в (не виртуальных) методах istream и ostream, и результат - это то, что видит streambuf.
Roger Pate 17.07.2010 00:30:54
Вопрос на самом деле показывает результат в памяти, 0x010203тогда как он, скорее всего, даст 0x00000001 0x00000002 0x00000003(при условии sizeof(int)==4).
MSalters 13.01.2017 13:17:23
@MSalters Вы правы, видимо, на 6 лет я был идиотом.
KeithB 13.01.2017 17:55:54

Перегрузка некоторых необычных операторов работает довольно хорошо. Здесь ниже я решил перегрузить <=, потому что он имеет ту же ассоциативность слева направо, что и <<, и имеет как-то близкий внешний вид ...

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

Есть несколько недостатков:

  • новое значение <= может запутать читателей или привести к неожиданным результатам:

    cout <= 31 <= 32;

    не даст такой же результат, как

    cout <= (31 <= 32);
  • при чтении кода явно не упоминается о порядке байтов, как показано в приведенном выше примере.

  • он не может просто смешиваться с <<, потому что он не принадлежит к той же группе приоритетов. Я обычно использую скобки, чтобы уточнить такие как:

    ( cout <= htonl(a) <= htonl(b) ) << endl;
3
17.04.2013 12:13:56
Это классное подтверждение концепции, но обратите внимание, что перегруженные операторы C ++ считаются злыми, потому что они допускают это . Неочевидная перегрузка <<оправдана только потому, что это стандартная перегрузка. Никаких новых хакерских перегрузок не должно быть изобретено, а сама перегрузка должна использоваться с большой осторожностью.
cubuspl42 10.07.2014 00:04:18

Для этого варианта использования я реализовал «оператор необработанного сдвига»:

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

Поместите это где-нибудь удобно и используйте это как это:

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

Преимущества:

  • цепной
  • автоматический sizeof()
  • также принимает массивы и экземпляры struct / class

Недостатки:

  • небезопасно для не POD-объектов: утечки указателей и заполнения
  • вывод зависит от платформы: заполнение, endianess, целочисленные типы
2
13.01.2017 13:39:51

Вы можете делать такие вещи с помощью шаблонов. Например:

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

Это может использовать некоторую очистку, но это функционально. Например:

//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);
8
7.02.2018 04:54:03
Я предпочитаю этот ответ , потому что это не смущая и это также может обернуть другие вещи , какvector<float>
陈家胜 20.04.2018 13:26:28
Эй, @SamuelPowell Мне очень нравится этот подход, я пошел дальше и написал больше сериализаторов поверх этого подхода. Мне нравится это, поскольку он имеет такую ​​низкую сложность по сравнению с другими сериализаторами C ++. Если интересно, посмотрите на github.com/goblinhack/simple-c-plus-plus-serializer - будут заинтересованы ваши комментарии. Я обнаружил, что мне пришлось удалить тип потока в шаблонах из-за (я думаю) проблем с перегрузкой операторов. Во всяком случае, это хорошо работает для многих типов.
Neil McGill 8.09.2019 02:44:27