SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

オープンソースC++用クラスライブラリPOCO活用講座

POCO流ファイル処理あれこれ

オープンソースC++用クラスライブラリPOCO活用講座(4)

  • X ポスト
  • このエントリーをはてなブックマークに追加

ストリーム関連クラス 2

文字カウント・ストリーム

 ストリームの文字数や、現在の位置は何行目であるかを取得できます。また、指定行へシークすることができます。これも特定の用途では便利でしょう。

 Poco::DirectoryIteratorとPoco::CountingOutputStreamクラスを利用し、ちょっとしたサンプルを作ってみました。

DirectoryIteratorとCountingOutputStreamのサンプルコード
#include <iostream>
#include "Poco/DirectoryIterator.h"
#include "Poco/StreamCopier.h"
#include "Poco/CountingStream.h"

// 指定されたディレクトリ以下に含まれるファイルを走査して、
// ディレクトリ名とファイルサイズの合計値をストリームに出力、
// ファイルサイズを返す。
Poco::File::FileSize  getDirSize( const std::string& path, 
                                        std::ostream& out )
{
    Poco::File::FileSize    flen = 0;    // ファイルサイズの合計

    Poco::DirectoryIterator  di( path );
    while ( !di.name().empty() )         // カレントのファイル名が
                                         // 空なら終わり
    {
        if ( di->isDirectory() ) {       // ディレクトリなら
                                         // ( ->でPoco::Fileクラス
                                         // として参照可)
            flen += 
                getDirSize( di->path(), out );  // 再帰呼び出し
        }
        else
            flen += di->getSize();   // ファイルサイズを合算

        ++di;                        // ++オペレータで次のファイルへ
    }

    // pathの名前(UTF-8)と、
    // path以下に含まれるファイルサイズの合計値を出力。
    out << path << "," << (__int64)flen << std::endl;

    return flen;
}

int _tmain(int argc, _TCHAR* argv[])
{
    // 行数をカウントするために
    Poco::CountingOutputStream    ct( std::cout );
    getDirSize( "C:\\XXX", ct );

    // 走査したサブディレクトリ数を出力。
    // getDirSizeの引数で指定したディレクトリを、
    // 数に入れないように-1する。
    std::cout << "total : " << ct.lines() - 1 << " dir" << std::endl;
}

 ディレクトリを再帰的に走査し、各ディレクトに含まれるファイルの合計ファイルサイズを列挙するサンプルコードです。getDirSizeという関数で、ディレクトリ内のファイル走査にPoco::DirectoryIteratorを使っています。走査したファイルは、->オペレータを使い、Poco::Fileのポインタとして取り出すことができます。ディレクトリが見つかったら、getDirSize自身を再帰呼び出しをして、そのサブディレクトリのファイルサイズを求めています。ディレクトリの処理には、この例のように再帰呼び出しが定番の処理です。ファイルがディレクトリでなかった場合は、ファイルサイズを単に合算するだけです。

 getDirSizeの出力先は、標準出力にリンクしたPoco::CountingOutputStreamを使っています。こうすると、画面の出力と行数のカウントの両方を同時に行うことができます。行数を-1しているのは、最初に指定したディレクトリ分を除くためです。

 POCOのクラスのおかげで、かんたんに記述できたのではないでしょうか。

 ハードディスクのファイルを整理する際に、どこのディレクトリで容量を消費しているのか知りたいことがありますよね。そんなときに使えそうです。ただ、読み出したまま出力しているので、例えばファイルサイズ順に並べたいと思うかもしれません。いろいろ工夫の余地はありますが何かの参考になれば幸いです。

StreamTokenizer

 前回紹介したPoco::StringTokenizerのストリーム版で、ストリームを指定のデリミタでトークンに分割するクラスなのですが、Poco::StringTokenizerに比べて、ちょっと使い方が分かりにくいものになっています。単純にデリミタを指定して分割するのではなく、デリミタでの区切り処理を実装したTokenクラスというのを別に定義してやり、それをPoco::StreamTokenizerに渡すという感じになります。

 つまり、用途にあわせてTokenクラスの実装を自分で行う必要があるわけで、便利かどうかは微妙なところです。サンプルコードを紹介しておきますが、けっこうなコード量が必要です。

StreamTokenizerのサンプルコード
#include "Poco/StreamTokenizer.h"
#include "Poco/Token.h"

// カンマ区切り用Tokenクラス
class testToken : public Poco::Token
{
public:
    testToken(){}
    ~testToken(){}
    bool start(char c, std::istream& istr);
    void finish(std::istream& istr);
    Class tokenClass() const { return Poco::Token::USER_TOKEN; }
};

// 切り出すトークンの最初の文字を探す
bool testToken::start(char c, std::istream& istr)
{
    if ( c != ',' ) _value = c;
    return true;
}

// トークンの終わりを判断
void testToken::finish(std::istream& istr)
{
    int c = istr.peek();
    while ( c != ',' && !istr.eof() )
    {
        istr.get();
        _value += (char) c;
        c = istr.peek();
    }
    istr.get();
}

//CSVファイルを読み出し、トークンを出力する。
int _tmain(int argc, _TCHAR* argv[])
{
    std::fstream    fs( "c:\\test.txt" );
    Poco::StreamTokenizer   st( fs );

    // トークンの前の空白を削除
    st.addToken( new Poco::WhitespaceToken );

    // カンマ区切りトークン
    st.addToken( new testToken );

    // トークンの取り出し
    // 終了はEOF_TOKENかどうかで判断
    for ( const Poco::Token * pt = st.next();
            pt->tokenClass() != Poco::Token::EOF_TOKEN;  
              pt = st.next() )
    {
        //「トークン」と出力
        std::cout << "「" << pt->tokenString() << "」" << std::endl; 
    }
}

 簡単なCSVファイル読み込みのサンプルなので、カンマが連続するデータや複数行のデータには対応していません。

その他

 上記以外にも次のようなストリームクラスがあります。また、表には記していませんがプラットフォームによって異なるテキストの改行コードを吸収するためのクラスなどがあります。

hexBinary変換ストリーム

 BASE64変換ストリームと同じような使い方ができます。BASE64ではなく、hexBinaryに変換します。hexBinaryとは、バイナリデータをそのまま「0~9」「a~f」「A~F」の文字で16進数として表現する形式です。

バイナリデータ操作ストリーム

 これもフィルタ的に使用するストリームで、バイナリデータのバイトオーダーが変換できるようになっています。ビッグエンディアン、リトルエンディアンの変換はもちろん、バイトオーダーを示すBOMを出力するwriteBOMというメンバ関数もあります。ただ、オープン系システムではあまり使う場面はないかもしれません。

まとめ

 今回は、サンプルコードを中心に紹介しました。詳しいクラスの定義は、Pocoのドキュメントやヘッダーファイルを参照してください。ファイル関連の処理は実際のプログラム開発でも頻出する処理です。POCOのファイルやストリームのクラスをうまく活用すれば、ソースをとてもシンプルにすることができます。ぜひ試してみてください。

参考資料

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
オープンソースC++用クラスライブラリPOCO活用講座連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 高江 賢(タカエ ケン)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/2205 2008/02/28 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング