2009-10-13 35 views
50

Tôi thường sử dụng stringstream để ghi vào chuỗi trong bộ nhớ. Có cách nào để ghi vào một bộ đệm char trong chế độ nhị phân? Xét đoạn mã sau:Có luồng bộ nhớ nhị phân nào trong C++

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

Bộ nhớ tại ch sẽ trông như thế này: 0x313233 - các mã ASCII của ký tự 1, 2 và 3. Tôi đang tìm kiếm một cách để viết các giá trị nhị phân thân. Đó là, tôi muốn 0x010203 trong bộ nhớ. Vấn đề là tôi muốn có thể viết một hàm

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

Và quyết định bên ngoài loại luồng nào sẽ sử dụng. Một cái gì đó như thế này:

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

Bất kỳ ý tưởng nào?

+1

Đó là hex, không phải nhị phân. Tại sao bạn không thể viết 0x01, 0x02, v.v., mặc dù ... đó là những ký tự ASCII thực tế. – jrockway

+1

Ông muốn nội dung của bộ nhớ (byte thực tế) là 0x010203 (66051 thập phân), không phải là chuỗi "0x010203". – KeithB

+1

Tôi đã sửa đổi câu hỏi. Hy vọng nó rõ ràng hơn bây giờ. – FireAphis

Trả lời

5

Vâng, chỉ cần sử dụng ký tự chứ không phải số nguyên.

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

Để đọc và ghi dữ liệu nhị phân vào luồng, bao gồm cả chuỗi, sử dụng hàm thành viên read() và write(). Vì vậy,

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

Điều này cho phép 0x4030201 trên hệ thống của tôi.

Edit: Để thực hiện công việc này một cách minh bạch với sự chèn và khai thác các nhà khai thác (< < và >>), đặt cược tốt nhất của bạn nó để tạo ra một streambuf có nguồn gốc mà không được điều đúng đắn, và thông qua đó để bất cứ điều gì bạn muốn suối sử dụng.

+0

Nó chắc chắn trả lời phần đầu tiên của câu hỏi, nhưng có cách nào để làm cho việc chèn luôn giống nhau (tức là s << a) nhưng biểu diễn dữ liệu bên trong có khác nhau tùy thuộc vào loại luồng không? – FireAphis

+0

Streambuf của riêng bạn không thể làm điều này; định dạng được thực hiện trong các phương pháp istream và ostream (không ảo) và kết quả của đó là những gì streambuf thấy. –

+0

Câu hỏi thực sự cho thấy kết quả trong bộ nhớ '0x010203' trong khi điều này có khả năng sẽ tạo ra' 0x00000001 0x00000002 0x00000003' (giả sử 'sizeof (int) == 4'). – MSalters

1

quá tải một số toán tử bất thường hoạt động khá tốt. Dưới đây dưới đây tôi chọn lựa để quá tải < = vì nó có associativity trái sang phải giống như < < và có bằng cách nào đó kết thúc look-and-feel ...

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

Có một số nhược điểm :

  • ý nghĩa mới của < = có thể nhầm lẫn độc giả hoặc dẫn đến kết quả bất ngờ:

    cout <= 31 <= 32; 
    

    sẽ không cung cấp các kết quả tương tự như

    cout <= (31 <= 32); 
    
  • các endianess không mentionned rõ tại đọc mã, như minh họa trong ví dụ trên.

  • nó không thể trộn đơn giản với < < bởi vì nó không thuộc cùng một nhóm ưu tiên.Tôi thường sử dụng dấu ngoặc đơn để làm rõ như vậy như:

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

Đó là một bằng chứng tuyệt vời về khái niệm, nhưng lưu ý rằng các toán tử quá tải của C++ được coi là xấu bởi vì chúng cho phép * this *. Sự quá tải không rõ ràng của '<<' là hợp lý chỉ vì nó là một ** quá tải ** tiêu chuẩn. Không có quá tải hacky mới nên được phát minh và quá tải chính nó nên được sử dụng với một sự chăm sóc tuyệt vời. – cubuspl42

0

Đối với trường hợp sử dụng này tôi thực hiện bản thân mình một "hành sự thay đổi thô":

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

Đặt nó ở đâu đó thuận tiện và sử dụng nó như thế này :

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

Ưu điểm:

0.123.
  • thể kết nối
  • tự động sizeof()
  • mất mảng và các trường hợp struct/lớp, quá

Nhược điểm:

  • không an toàn cho phi POD đối tượng: rò rỉ gợi ý và padding
  • đầu ra là nền tảng cụ thể: padding, endianess, số nguyên loại
0

Bạn có thể thực hiện việc này với các mẫu. Ví dụ:

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

Nó có thể sử dụng một số dọn dẹp, nhưng nó hoạt động. Ví dụ:

//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); 
Các vấn đề liên quan