boost::iostreams::copy() 的异常

Posted

技术标签:

【中文标题】boost::iostreams::copy() 的异常【英文标题】:exceptions from boost::iostreams::copy() 【发布时间】:2010-07-02 15:34:37 【问题描述】:

在下面的代码中,我有一个损坏的“hello.bz2”,其中包含超出 EOF 的杂散字符。

有没有办法让 boost::iostreams::copy() 调用 throw ?

#include <fstream>
#include <iostream>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/bzip2.hpp>

int main() 

    using namespace std;
    using namespace boost::iostreams;

    ifstream file("hello.bz2", ios_base::in | ios_base::binary);
    filtering_streambuf<input> in;
    in.push(bzip2_decompressor());
    in.push(file);
    boost::iostreams::copy(in, cout);

编辑: 请忽略目前最受关注的线路; EOF。请假设使用损坏的 bzip2 文件。 我使用“EOF”提示我在文件上运行 bzcat 时遇到的错误

bzcat hello.bz2
hello world

bzcat: hello.bz2: trailing garbage after EOF ignored

【问题讨论】:

“EOF”是什么意思?某种标记? @Neil 请忽略 EOF 部分...假设文件已损坏。 【参考方案1】:

研究

std::ios_base::failure 是“Iostreams 库中的函数作为异常抛出的所有对象类型的基类,用于报告在流缓冲区操作期间检测到的错误。”

Looking 在 boost 文档中:

class bzip2_error : public std::ios_base::failure 
public:
    bzip2_error(int error);
    int error() const;
;

bzip2_error 是使用 bzip2 过滤器时抛出的特定异常,它继承自 std::ios_base::failure。如您所见,它是通过传入一个表示错误代码的整数来构造的。它还有一个方法 error(),它返回构造它的错误代码。 文档列出 bzip2 错误代码如下:

data_error - 表示压缩数据流已损坏。等于 BZ_DATA_ERROR。 data_error_magic - 表示压缩的数据流不是以'magic' 序列'B' 'Z' 'h' 开头。等于 BZ_DATA_ERROR_MAGIC。 config_error - 表示 libbzip2 已针对当前平台进行了不正确的配置。等于 BZ_CONFIG_ERROR。

代码

编辑 我还想澄清 boost::iostreams::copy() 不会是这里抛出异常的那个,而是 bzip2 过滤器。只有 iostream 或过滤器会抛出异常,仅复制 使用 iostream/filter 可能会导致 iostream/filter 抛出异常。

**编辑 2 ** 如您所料,问题似乎出在 bzip2_decompressor_impl 上。当 bz2 文件为空时,我已经复制了无限旋转循环。我花了一点时间才弄清楚如何构建 boost 并与 bzip2、zlib 和 iostreams 库链接,看看我是否可以复制你的结果。

g++ test.cpp -lz -lbz2 boostinstall/boost/bin.v2/libs/iostreams/build/darwin-4.2.1/release/link-static/threading-multi/libboost_iostreams.a -Lboostinstall/boost/bin.v2/libs/ -Iboost/include/boost-1_42 -g

test.cpp:

#include <fstream>
#include <iostream>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/bzip2.hpp>

int main()

    using namespace std;
    using namespace boost::iostreams;

    try 
        ifstream file("hello.bz2", ios_base::in | ios_base::binary);
        filtering_streambuf<input> in;
        in.push(bzip2_decompressor());
        in.push(file);
        boost::iostreams::copy(in, cout);
    
    catch(const bzip2_error& exception) 
        int error = exception.error();

        if(error == boost::iostreams::bzip2::data_error) 
            // compressed data stream is corrupted
            cout << "compressed data stream is corrupted";
        
        else if(error == boost::iostreams::bzip2::data_error_magic)
        
            // compressed data stream does not begin with the 'magic' sequence 'B' 'Z' 'h'
            cout << "compressed data stream does not begin with the 'magic' sequence 'B' 'Z' 'h'";
        
        else if(boost::iostreams::bzip2::config_error) 
            // libbzip2 has been improperly configured for the current platform
            cout << "libbzip2 has been improperly configured for the current platform";
        
    

调试:

gdb a.out
(gdb) b bzip2.hpp:344

在 symmetric.hpp:109 中有一个循环驱动 bzip2 的解压缩:

        while (true)
        
            // Invoke filter if there are unconsumed characters in buffer or if
            // filter must be flushed.
            bool flush = status == f_eof;
            if (buf.ptr() != buf.eptr() || flush) 
                const char_type* next = buf.ptr();
                bool done =
                    !filter().filter(next, buf.eptr(), next_s, end_s, flush);
                buf.ptr() = buf.data() + (next - buf.data());
                if (done)
                    return detail::check_eof(
                               static_cast<std::streamsize>(next_s - s)
                           );
            

            // If no more characters are available without blocking, or
            // if read request has been satisfied, return.
            if ( (status == f_would_block && buf.ptr() == buf.eptr()) ||
                 next_s == end_s )
            
                return static_cast<std::streamsize>(next_s - s);
            

            // Fill buffer.
            if (status == f_good)
                status = fill(src);
        

bzip2_decompressor_impl 的过滤方法 bzip2.hpp:344 在 symmetric.hpp:117 上被调用:

template<typename Alloc>
bool bzip2_decompressor_impl<Alloc>::filter
    ( const char*& src_begin, const char* src_end,
      char*& dest_begin, char* dest_end, bool /* flush */ )

    if (!ready())
        init();
    if (eof_)
        return false;
    before(src_begin, src_end, dest_begin, dest_end);
    int result = decompress();
    after(src_begin, dest_begin);
    bzip2_error::check BOOST_PREVENT_MACRO_SUBSTITUTION(result);
    return !(eof_ = result == bzip2::stream_end);

我认为问题很简单,bzip2_decompressor_impl 的 eof_ 标志永远不会被设置。除非它假设以某种我不理解的神奇方式发生,否则它归 bzip2_decompressor_impl 类所有,并且它只会被设置为 false。所以当我们这样做时:

cat /dev/null > hello.bz2

我们得到一个永不结束的旋转循环,当 EOF 被击中时我们不会中断。这肯定是一个错误,因为其他程序(如 vim)打开以类似方式创建的文本文件没有问题。但是,当 bz2 文件“损坏”时,我可以让过滤器抛出:

echo "other corrupt" > hello.bz2
./a.out
compressed data stream does not begin with the 'magic' sequence 'B' 'Z' 'h'

有时您必须对开源代码持保留态度。您的 bz2 更有可能被损坏并正确抛出。但是, /dev/null 情况是一个严重的错误。我们应该将它提交给 boost 开发者,以便他们修复它。

【讨论】:

@manifest 非常感谢您的详细回复。但不幸的是,它对我的​​特殊情况没有帮助。不知道引擎盖下发生了什么。 boost iostreams 及其过滤机制似乎有些可疑。可能只是新进入者,bzip2 功能。我注意到的一件事是,如果我向过滤器提供格式不正确的 bz2 文件,过滤器机制无法发现某些错误正在发生。最简单的方法是,如果在上面的代码中,创建一个空的 hello.bz2 并运行它。你会看到 cpu 使用率飙升到 99% ?!? 空的 hello.bz2,我的意思是“cat /dev/null > hello.bz2” 查看编辑,我认为 bzip2 过滤器实现存在错误 考虑提交一个错误,我从主干检查了 boost。 bzip2 过滤器代码完全不同,因此他们可能已修复它。稍后会在这里更新 当我检查了trunk,bjammed 并重新链接 cat /dev/null > hello.bz2 然后 ./a.out 导致“libbzip2 未正确配置为当前平台”。虽然这可能不是我所期望的例外,但至少这是一个例外,而不是无休止的旋转。正确创建的 .bz2 文件仍然可以工作,损坏的文件会引发魔术序列异常。我建议您确保链接到最新的库。【参考方案2】:

如何在文件末尾有杂散字符?

如果您的意思是文件中包含垃圾数据,那么解压缩算法如何能够判断数据是否为垃圾才能做出决定throw

【讨论】:

这是我在文件上运行 bzcat 时看到的内容。 “bzcat: hello.bz2: EOF 忽略后的尾随垃圾” 老实说,我更感兴趣的是找到从解压缩器捕获错误的方法。

以上是关于boost::iostreams::copy() 的异常的主要内容,如果未能解决你的问题,请参考以下文章