【问题标题】:ostream_iterator for Binary Outputostream_iterator 用于二进制输出
【发布时间】:2015-08-17 12:44:58
【问题描述】:

我希望能够使用ostream_iterator 流式传输到二进制文件。但是ostream_iterator 使用FormattedOuputFunction 所以它会写ASCII,而不是二进制:

std::ostream_iterator 是一个单通道输出迭代器,它使用operator<<T 类型的连续对象写入为其构造的std::basic_ostream 对象

除了编写我自己的迭代器之外,还有没有办法使用迭代器来编写二进制文件?

我正在尝试做的一个简化示例,但 copy 语句会将 ASCII 写入我的二进制文件:

ofstream foo("foo.txt", ios_base::binary);
vector<int> bar = {13, 42};

copy(bar.cbegin(), bar.cend(), ostream_iterator<decltype(bar)::value_type>(foo));

【问题讨论】:

    标签: c++ file-io iterator fstream binaryfiles


    【解决方案1】:

    ostreambuf_iteratorostream_iterator 更合适。它的重量要轻得多,并且不进行格式化。它接受字符类型的模板参数,因此与大多数流兼容的唯一选择是std::ostream_iterator&lt; char &gt;

    确保以二进制模式打开流。顺便说一下,标准流缓冲区永远不会以二进制模式打开。

    【讨论】:

    • 我到处找这个。甚至写了hackish answer 来获得解决方案。这是正确的答案。在我留出一些时间让这个答案获得支持后,我会接受。
    • @JonathanMee 如果你喜欢 :) 。但是,在不接受答案时要小心;如果我在 unaccept 之后没有编辑,你将永远无法再次接受它。
    • 呃,看来我仍然需要我的老套答案。 ostreambuf_iterator 仅接受 chars。这意味着就像我的回答一样,除了InputIterator or ForwardIterator 之外,这对任何东西都不起作用,或者您还必须使用包装器或专门的迭代器。
    • @JonathanMee 根据定义,迭代器只能使用一种类型。听起来您想要一个二进制序列化库。您确实需要格式化,而不是文本。有多种方法,但您可能会提出更具体的问题。
    • 该问题要求使用标准算法的迭代器。像 reverse_copypartial_sort_copy 这样的算法对于这个答案或我的 hack 来说是灾难。
    【解决方案2】:

    它有效,但您必须明确使用ostream_iterator&lt;char&gt;

    示例(包括为简洁而省略):

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

    将 0 到 255 的 256 个字节写入 foo.txt。


    以上假设您想直接将 int 的值作为 char 写入文件。根据您的评论,您希望将 int 值写入本机主机字节序中的 4 字节值(假设为 int32_t)。我会使用辅助类来完成这项工作:

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

    这个写入 1024 个字节(对于大小为 4 的整数),其中包含 256 个第一个整数的表示。它会自动适应其他大小的 int。

    【讨论】:

    • 看到这里,我的心怦怦直跳。但可惜它不起作用。它只是将每个int 转换为char,然后将该ASCII 字符输出到流中。 This example 显示结果:"wrong value 1: 842281777 wrong value 2: 0 wrong value 3: 0 wrong value 4: 0 right value 1: 49 right value 2: 51 right value 3: 52 right value 4: 50"
    • @JonathanMee:我认为这是你所期望的。 {13, 42} 你想要什么结果?
    • 我正在以二进制格式写入整数。当我以二进制格式读取两个整数时,我希望它们是相同的往返,所以我希望是 13、42。在我链接的示例中,您可以看到 readwrite 如何正确完成此操作。给我一个不变的往返。
    • @JonathanMee:我明白了。我必须重新考虑我的解决方案......我应该过一会儿给你一些东西
    • 有趣的包装类...我没有考虑过那个攻角。
    【解决方案3】:

    对于仅使用InputIterator or ForwardIterator 作为输入的算法,简单的强制转换就足够了。对于更复杂的算法writing a wrapper,可能需要编写专门的迭代器或使用 Boost 功能。只要算法输入与这些条件一致,这样的事情就会起作用:

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

    显然,这需要经过往返认证才能被认为是可靠的。验证 output 中的值是连续的可能很乏味,因此代码被劫持 from here 来做到这一点:

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

    output from this证明这个方法实际上是成功的:

    第一个元素:0
    最后一个元素:255
    任何不连续的元素:没有

    虽然不是所有可能的int 都被尝试过,但所有可能的char 都被尝试过,并且由于任何int 都可以由chars 的集合组成,这表明ints 的任何集合都是以这种方式流式传输。

    【讨论】:

    • 该标准确实要求 a char 具有较弱的对齐方式。 3.11 对齐第 6 节(来自标准 C++11 草案 n4296)说:窄字符类型应具有最弱的对齐要求。 [注意:这使得窄字符类型可以用作对齐内存区域的基础类型——结束注释]。这意味着sizeof(T) 必须是sizeof(char) 的倍数
    • @SergeBallesta 谢谢,我不知道。我已经进行了相应的编辑。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-14
    • 2017-12-21
    • 1970-01-01
    • 2017-11-29
    • 1970-01-01
    相关资源
    最近更新 更多