はじめに
本記事ではPOCO(C++ Portable Componentsの略)というオープンソースのC++用クラスライブラリを紹介します。今回も、6つのライブラリのうちのPOCO::Foundationライブラリから、いくつかクラスをピックアップして解説しましょう。今回のテーマは、最も基本的とも言えるファイル処理です。
これまでの記事
対象読者
オブジェクト指向を理解し、ネイティブC++のクラスライブラリを活用できる方を対象としています。
必要な環境
POCOは、多様なプラットフォームで動作可能で、Windows、Mac OS X、Linux、HP-UX、Tru64、Solaris、QNXでの動作を保証しています。コンパイラは、いわゆる標準C++のコードに対応したものが必要です。POCO内部でSTLを使用しています。
今回から、Windows Vista上の「Microsoft Visual C++ 2008 Express Edition」という環境で動作検証を行いました。Visual Studio 2008になって、Express EditionでもプラットフォームSDKの別途インストールは不要になっています。Win32 APIを利用する場合はインストールが楽になりました。
POCOでのファイル処理 1
ファイル処理は、POCO::Foundationライブラリ内の「Filesystem」という名のパッケージにまとめられており、以下の5つのクラスが実装されています。ファイル処理はプラットフォーム(Windows、UNIXなど)に依存するものが多いのですが、記事では基本的にWindows環境のみを想定して解説します。
クラス名 | 概要 |
File | ファイル操作クラス |
Path | ディレクトリ操作クラス |
DirectoryIterator | ディレクトリ内ファイル列挙 |
TemporaryFile | 一時ファイル名取得 |
Glob | ワイルドカード検索 |
プラットフォームが違えば、ファイルシステムも異なります。POCOでは、複数の環境に対応するため、インターフェースと実装とが分離しており、内部の実装部分でプラットフォームごとに分かれています。そのため、利用する側では特にプラットフォームの違いを意識する必要はありません。ただし、中にはプラットフォームに依存した振る舞いをする関数もあります。例えばUNIX系環境では、ファイルにアクセス権として「読込可」「書込可」「実行可」の属性を付与することができます。それに対応してsetExecutable
という実行許可を付加するメンバ関数がPoco::Fileクラスにあります。Windowsではファイルにそのような属性はありませんので、関数の定義はありますが処理は何も実装されていません。
各クラスをリファレンス的に詳しく解説していっても退屈ですので、それはPOCOのドキュメントに譲ることにし、ここではクラスの特徴的なポイントをサンプルコードと合わせて解説することにしましょう。
File
基本的なファイル操作クラスです。ただ、MFCでのCFileクラスのようなファイルをオープンして読み書きするクラスとは少し趣が異なっています。メンバ関数として用意されているのは、ファイルのコピーやリネーム、空のファイル作成など、ファイルを外部的に操作するものばかりです。その上、通常のファイルだけでなく、ディレクトリから生成することもできます。ディレクトリかどうかを返すメンバ関数isDirectory
、ディレクトリを作成するメンバ関数createDirectories
などが用意されています。
それでは、ファイルを読み書きする処理は、POCOのクラスでは実装されていないのでしょうか?
実はファイルの読み書きに関しては、POCOではストリームとして処理するようになっているのです。ストリームを操作するクラスは、けっこう豊富に用意されているので、それはまた後ほど解説することにしましょう。Poco::Fileクラスのサンプルコードを以下に示します。
#include <iostream> #include <Poco/File.h> #include <Poco/Exception.h> int _tmain(int argc, _TCHAR* argv[]) { Poco::File tmp( "c:\\test.txt" ); // ファイルコピー tmp.copyTo( "c:\\temp" ); // ファイル削除 tmp.remove(); // a,b,cのディレクトリを一度に作成する Poco::File dir1( "c:\\a\\b\\c" ); dir1.createDirectories(); dir1 = "c:\\a"; // 代入演算子が定義されており、この記述が可能 try { // 削除 dir1.remove(); } catch( Poco::Exception& ex ) { // c:\\aが空でないと削除できないが std::cout << ex.displayText() << std::endl; } // こうすれば、C:\a以下全サブディレクトリごと削除できる dir1.remove( true ); Poco::File dir2( "C:\\Lib\\poco" ); // Poco::Fileのvectorを用意 typedef std::vector<Poco::File> fary; fary files; // dir2ディレクトリ以下のファイルを取得 dir2.list( files ); // ファイル列挙(ディレクトリも含まれる) for ( fary::iterator it = files.begin(); it != files.end(); ++it ) std::cout << (*it).path() << std::endl; return 0; }
コメントを追っていけば分かると思いますが、いくつか補足をしておきます。
Poco::File.remove
Poco::File.remove
では、クラス自身が保持しているファイルを削除します。引数はリカーシブ(再帰)オプションで、デフォルトではfalseになっています。クラスがディレクトリを示していて、引数がtrueの場合はディレクトリ内のファイルの有無にかかわらずディレクトリごと削除されます。
Poco::File.list
Poco::File.list
は、クラスが保持しているディレクトリ配下のファイルを取得します。サンプルコードではPoco::File
型のvectorで取得しています。クラス自身がディレクトリを示していない場合は例外が送出されます。
Poco::File
型だけでなく、std::vector<std::string>
を引数で指定することもできます。その場合はファイル名のみを文字列として取得することになります。Poco::File
型だけでもいいように思えますが、実はPoco::File
にはファイル名を参照するメンバ関数がないのです。サンプルコードのpath
のようにフルパス名を返すメンバ関数しかありません。ファイル名だけを取り出したいときは、次のPoco::Path
クラスを使うことになります。
なお、いずれの場合も取得したファイルにはサブディレクトリが含まれます。ただし、ファイルを列挙するCランタイム関数(_findfirst
、_findnext
)や、Win32 APIのFindFirstFile
、FindNextFile
とは異なり、自分「.」や親ディレクトリ「..」は含まれません。