ostream_iterator for Binary Output

后端 未结 3 760
时光取名叫无心
时光取名叫无心 2021-01-22 06:47

I want to be able to use an ostream_iterator to stream to a binary file. But the ostream_iterator uses a FormattedOuputFunction so it will write ASCII, not binary:<

相关标签:
3条回答
  • 2021-01-22 07:34

    For algorithms that only use an InputIterator or ForwardIterator for the input, a simple cast is sufficient. For more complex algorithms writing a wrapper, writing a specialized iterator, or using Boost functionality may be necessary. Provided the algorithm input aligns with those conditions, something like this will work:

    ofstream foo("foo.txt", ios_base::binary);
    vector<int> bar = {13, 42};
    
    copy(reinterpret_cast<const char*>(&*bar.cbegin()), reinterpret_cast<const char*>(&*bar.cend()), ostreambuf_iterator(foo));
    

    Obviously this needs to be round trip certified before it can be considered dependable. Validating that the values in output are consecutive can be tedious so code was hijacked from here to do that:

    ofstream foo("foo.txt", ios::binary);
    vector<int> bar(numeric_limits<unsigned char>::max() + 1);
    
    iota(bar.begin(), bar.end(), 0);
    
    copy(reinterpret_cast<const char*>(&*bar.data()), reinterpret_cast<const char*>(&*bar.data() + bar.size()), ostreambuf_iterator<char>(foo));
    foo.close();
    
    ifstream file_read("foo.txt", ios::binary);
    vector<decltype(bar)::value_type> output(bar.size());
    
    copy(istreambuf_iterator<char>(file_read), istreambuf_iterator<char>(), reinterpret_cast<char*>(&*output.data()));
    
    cout << "First element: " << output.front() << "\nLast element: " << output.back() << "\nAny non-consecutive elements: " << (output.cend() == mismatch(output.cbegin(), prev(output.cend()), next(output.cbegin()), [](auto first1, auto first2) { return first1 + 1 == first2; }).second ? "no\n" : "yes\n");
    

    The output from this demonstrates that this method is actually successful:

    First element: 0
    Last element: 255
    Any non-consecutive elements: no

    Although not every possible int was tried, every possible char was tried, and since any int can be made up of a collection of chars this demonstrates that any collection of ints is streamable this way.

    0 讨论(0)
  • ostreambuf_iterator is more appropriate than ostream_iterator. It's much lighter weight and it does no formatting. It takes a template argument for the character type, so the only choice compatible with most streams is std::ostream_iterator< char >.

    Be sure to open the stream in binary mode. The standard stream buffers, by the way, are never opened in binary mode.

    0 讨论(0)
  • 2021-01-22 07:39

    It works, but you will have to explicitely use an ostream_iterator<char>.

    Example (includes omitted for brievety):

    int main(int argc, char **argv) {
        std::vector<int> arr;
    
        std::ofstream fd("foo.txt", std::ios::binary | std::ios::out);
    
        for (int i=0; i<256; i++) arr.push_back(i);
    
        std::ostream_iterator<char> oi(fd);
        std::copy(arr.begin(), arr.end(), oi);
        fd.close();
        return 0;
    }
    

    will write the 256 bytes for 0 to 255 in foo.txt.


    Above assumed that you wanted to write directly the value of the int as a char to the file. From your comment, you want to write the int value as a 4 bytes value (assuming int32_t) in native host endianness. I would use an auxilliary class to do the job:

    class bint {
    private:
        char c[sizeof(int)];
    
    public:
        bint(const int i) { // allows bint b = 5;
            ::memcpy(c, &i, sizeof(c));
        }
        operator int() const {  // allows : bint b = 5; int i=b => gives i=5
            int i;
            ::memcpy(&i, c, sizeof(int));
            return i;
        }
        const char *getBytes() const { // gives public read-only access to the bytes
            return c;
        }
    };
    
    std::ostream& operator <<(std::ostream& out, const bint& b) {
        out.write(b.getBytes(), sizeof(int));
        return out;
    }
    
    int main(int argc, char **argv) {
        std::vector<int> arr;
    
        std::ofstream fd("foo.txt", std::ios::binary | std::ios::out);
    
        for (int i=0; i<256; i++) arr.push_back(i);
    
        std::ostream_iterator<bint> oi(fd);
        std::copy(arr.begin(), arr.end(), oi);
        fd.close();
        return 0;
    }
    

    This one writes 1024 bytes (for integer of size 4) containing the representations of the 256 first integers. It would automatically adapts to other sizes of int.

    0 讨论(0)
提交回复
热议问题