hamigaki.png

前のページ 上に戻る ホーム 次のページ

チュートリアル

tarファイルの作成
tarファイルの展開
ZIPファイルの作成

tarファイルの作成

次のプログラムはファイルfoo.txtbar.txtを含むtarファイルtest.tarを作成する。

#include <hamigaki/archivers/tar_file.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/iostreams/copy.hpp>

namespace ar = hamigaki::archivers;
namespace fs = boost::filesystem;
namespace io = boost::iostreams;

void append_file(ar::tar_file_sink& tar, const fs::path& ph)
{
    ar::tar::header head;
    head.path = ph;
    head.file_size = fs::file_size(ph);
    tar.create_entry(head);

    fs::ifstream file(ph, std::ios_base::binary);
    io::copy(file, tar);
}

int main()
{
    ar::tar_file_sink tar("test.tar");

    append_file(tar, "foo.txt");
    append_file(tar, "bar.txt");

    tar.close_archive();
}

tarファイルを作成するには、クラスtar_file_sinkを用いる。コンストラクタの引数は、作成するtarファイルのパス名である。

アーカイブに追加する個々のファイルは(アーカイブ)エントリと呼ばれ、それぞれ個別のヘッダ情報を持つ。エントリを追加するには、適切な値を設定した構造体tar::headerを引数にして、メンバ関数create_entry()を呼び出す。

tar::headerのメンバ変数の内、path(ファイルのパス名)とfile_size(ファイルサイズ)は必須項目である。それ以外のメンバに関してはドキュメントを参照せよ。

create_entry()を呼び出すと、アーカイブはSinkとして書き込み可能となる。

書き込みが完了したら、メンバ関数close_archive()でアーカイブを閉じる。アーカイブのフォーマットによっては、close_archive()の呼び出しを忘れると、フッタ情報のない不正なアーカイブができてしまうので注意すること。

tarファイルの展開

今度は、先ほど作成したtest.tarをカレントディレクトリに展開するプログラムを作成する。

#include <hamigaki/archivers/tar_file.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/iostreams/copy.hpp>

namespace ar = hamigaki::archivers;
namespace fs = boost::filesystem;
namespace io = boost::iostreams;

bool is_valid_path(const fs::path& ph)
{
    if (ph.has_root_name() || ph.has_root_directory())
        return false;
    for (typename Path::iterator it = ph.begin(); it != ph.end(); ++it)
    {
        if (*it == "..")
            return false;
    }
    return true;
}

void extract_file(ar::tar_file_source& tar)
{
    const ar::tar::header& head = tar.header();
    if (!is_valid_path(head.path))
    {
        std::cerr << "Warning: skip invalid path: " << head.path.string() << std::endl;
    }

    fs::ofstream file(head.path, std::ios_base::binary);
    io::copy(tar, file);
}

int main()
{
    ar::tar_file_source tar("test.tar");

    while (tar.next_entry())
      extract_file(tar);
}

tarファイルを読み込むには、クラスtar_file_sourceを用いる。コンストラクタの引数は、tarファイルのパス名である。

tarファイルのオープンに成功したら、メンバ関数next_entry()を呼び出し、次のエントリを確認する。この関数は未読のエントリがある場合trueを返す。全てのエントリを読み終わり、これ以上のエントリが見つからない場合はfalseが返され、ループは終了する。

next_entry()の呼び出しに成功したら、アーカイブはSourceとして読み出し可能である。また、メンバ関数header()を呼び出すことで、ヘッダ情報を取得することもできる。ここでは、ヘッダから取得したパス名と同名のファイルを作成し、エントリから読み出した内容を書き出している。特に意図していない限り、絶対パスやカレントディレクトリより上への相対パスを含むアーカイブは悪意のあるものである。ファイルシステムへ出力する前には必ずパス名を検証する必要がある。

ZIPファイルの作成

tarファイルの作成項のプログラムをZIPフォーマット用に書き換えると、次のようになる。

#include <hamigaki/archivers/zip_file.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/iostreams/copy.hpp>

namespace ar = hamigaki::archivers;
namespace fs = boost::filesystem;
namespace io = boost::iostreams;

void append_file(ar::zip_file_sink& zip, const fs::path& ph)
{
    ar::zip::header head;
    head.path = ph;
    head.file_size = fs::file_size(ph);
    zip.create_entry(head);

    fs::ifstream file(ph, std::ios_base::binary);
    try
    {
        io::copy(file, zip);
    }
    catch (const ar::give_up_compression&)
    {
        file.clear();
        file.seekg(0, std::ios_base::beg);
        zip.rewind_entry();
        io::copy(file, zip);
    }
}

int main()
{
    ar::zip_file_sink zip("test.zip");

    append_file(zip, "foo.txt");
    append_file(zip, "bar.txt");

    zip.close_archive();
}

ZIPファイルを作成するには、クラスzip_file_sinkを用いる。コンストラクタの引数は、やはり、作成するzipファイルのパス名である。

ZIPフォーマットはtarフォーマットのようなアーカイバの機能に加え、ファイルを圧縮する機能を持っている。圧縮率はファイルの内容によって変化し、場合によっては元のサイズより大きくなってしまうこともある。Hamigaki.Archiversでは例外give_up_compressionを送出することで、この状況を報告する。

give_up_compressionが送出されたら、これを捕捉し、非圧縮で出力をやり直す必要がある。メンバ関数rewind_entry()はこのためのものであり、ファイルの書き込み位置をエントリの先頭に戻し、圧縮メソッドを非圧縮に設定する。rewind_entry()の呼び出しに成功したら、再度出力を試みることになる。

製作著作 © 2006-2008 Takeshi Mouri

前のページ 上に戻る ホーム 次のページ