1

以下のコードでは、EOF を超えた文字が壊れた "hello.bz2" があります。

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 ファイルで作業することを想定してください。ファイルに対してbzcatを実行したときに発生したエラーを示唆する「EOF」を使用しました

bzcat hello.bz2
hello world

bzcat: hello.bz2: trailing garbage after EOF ignored
4

2 に答える 2

2

リサーチ

std::ios_base::failureは、「ストリーム バッファ操作中に検出されたエラーを報告するために、Iostreams ライブラリの関数によって例外としてスローされるすべてのオブジェクトの型の基本クラス」です。

ブーストドキュメントを見る:

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

bzip2_errorは、 std::ios_base::failureから継承する bzip2 フィルターの使用時にスローされる特定の例外です。ご覧のとおり、エラー コードを表す整数を渡すことによって作成されます。また、構築に使用したエラー コードを返すerror()メソッドもあります。
ドキュメントには、bzip2 エラー コードが次のようにリストされています。

  • data_error - 圧縮されたデータ ストリームが破損していることを示します。BZ_DATA_ERROR に等しい。
  • data_error_magic - 圧縮されたデータ ストリームが「マジック」シーケンス「B」「Z」「h」で始まらないことを示します。BZ_DATA_ERROR_MAGIC に等しい。
  • config_error - libbzip2 が現在のプラットフォームに対して不適切に構成されていることを示します。BZ_CONFIG_ERROR に等しい。

コード

EDIT また、boost::iostreams::copy() がここで例外をスローするのではなく、bzip2 フィルターであることを明確にしたいと思います。iostream またはフィルターのみが例外をスローします。copy は iostream/filter を使用するだけで、iostream/filter が例外をスローする可能性があります。

**EDIT 2 ** ご想像のとおり、問題は bzip2_decompressor_impl にあるようです。bz2 ファイルが空の場合、無限の回転ループを複製しました。ブーストを構築し、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 のケースは深刻なバグです。修正できるように、ブースト開発者に提出する必要があります。

于 2010-07-02T20:04:43.067 に答える
-1

ファイルの末尾を超えて文字がはぐれているのはどうしてですか?

ファイルにガベージ データが含まれていることを意味する場合、解凍アルゴリズムは、データがガベージであるかどうかをどのように判断して判断できるのthrowでしょうか?

于 2010-07-02T15:50:52.790 に答える