GO BACK

Delphi教室 その6


「ファイルの読み書き」


■Delphiのファイルアクセスの考え方


 ある程度、実用的なプログラムを作ろうと思ったとき、どうしても必要となってくるのが「ファイルアクセス」です。作成したデータを保存し、再び読み込むというのは、実用系アプリケーションの基本ですね。

 プログラミングを勉強し始めた時、一番最初にぶつかる壁が、この「ファイルアクセス」だろうと思います。ファイルの操作というのは、それまでの単純な「プロパティを変更すればOK」とか「メソッドを呼び出せばOK」といった操作とは異なり、手続きとか決まりごととかがいろいろあったりするのが普通です。また、たいていの場合、一度に全部のデータを読み書きすることができず、決まったデータを何度も繰り返し読んだり書いたりして、それを管理していくので、慣れない内はわけがわからなくなったりするわけです。また、テキストならまだしも、グラフィックなどになるとデータの構造がどうなっているかとかどのフォーマットの場合にどう読み込むかとかいったことがどっと出てきて、シロウトにはお手上げです。

 こうした「ファイルアクセスは面倒だ」という常識をひっくり返したのがDelphiです。Delphiでは、コンポーネントごとに扱えるデータの種類が決まっているのが一般的ですね。例えば、イメージならImageコンポーネントを使う、という具合に。「それならコンポーネントごとに、利用するデータをファイルに読み書きする専用メソッドを用意して組み込んでしまえばいいや」というのが、Delphiのアプローチなのです。

 Delphiのコンポーネント群の内、長文のテキストやグラフィックを扱うようなものでは、その内部に「扱えるデータのオブジェクト」というのが用意されています。例えば、前回Imageを利用しましたが、あれにはPictureというプロパティでグラフィックファイルを読み込んでイメージに設定していました。あのPictureも、実はオブジェクトだったんです。そしてその中にはプロパティの類いだけでなく、イメージをファイルに保存したり、ファイルからイメージを読み込むための専用メソッドが用意されているんですよ。

 他のものも同様です。テキストの場合、1行しか書き込めないEditにはそうしたものは用意されていませんが、長いテキストを扱う「Memo」というコンポーネントにはテキストファイルを読み書きするためのメソッドが、またリッチテキストを扱える「RichEdit」というコンポーネントにはリッチテキストファイルを読み書きするメソッドが、それぞれ用意されています。このようにDelphiでは「コンポーネント内のデータを示すオブジェクトの中にあるファイルアクセス用メソッド」を呼び出すだけでデータのファイルへの読み書きができてしまうのです。

 もちろん、他のプログラミング言語のように、テキストデータを必要に応じて読み書きしていったり、バイナリファイルの中身を細かく制御しながら読み書きするような機能もDelphiには揃っています。が、とりあえず「データを丸ごとファイルに読み書きできるメソッド」というのが用意されているんですから、それの使い方を覚えてしまいましょう。これだけで十分役立つプログラムは作れるはずですから。

 

■イメージを読み書きする


 では、まずImageによるグラフィックファイルの読み書きから説明していきましょう。Imageに表示されたイメージをファイルに読み書きするには、先に触れたように「Picture」オブジェクト内にあるメソッドを利用します。それは、以下のようなものです。

《Picture》.LoadFromFile( ファイルパス );

《Picture》.SaveToFile( ファイルパス );

 ()内にStringでファイルのパスを設定すれば、そのファイルにイメージを読み書きします。利用可能なフォーマットはBMP、アイコン、メタファイルなどです。ファイルのフォーマットは、指定したファイル名の拡張子によって自動的に判別されるので、特にフォーマット変換などの処理は必要ありません。また、拡張子を付けずに保存するとデフォルトでビットマップファイルとして保存されるようです。

 実に安直に使えるメソッドですが、注意しないといけない点もあります。まず、ファイル操作というのは読み書きに失敗する場合もある、ということです。例えば、ファイルパスの指定が存在しないディレクトリを示していたりすると失敗します。ですから、これらはtryの中で実行するようにすべきでしょう。

 また、プログラムと同じディレクトリ内にあるファイルならファイル名だけで取りだせるんですが、それ以外の場所にあるファイルはファイルのフルパスをきちんと指定する必要があるでしょう。そうなると、ちょっと「テキストでパスを入力する」なんてやり方は通用しそうにありません。やはり、ファイルダイアログを使ってファイルを選択するような方法を知っておくべきでしょう。

 Delphiには、ファイルダイアログ関係のコンポーネントもいくつか用意されています。非常に便利なのは、グラフィックファイル専用のファイルダイアログ・コンポーネントも用意されていることです。これらは「OpenPictureDialog」「SavePictureDialog」というもので、それぞれコンポーネントパレットの「Dialog」タブ内におさめられています。

 これらは、いずれも「見えないコンポーネント」です。つまり、フォームデザイナに配置しても、実際に画面には何も表示されないのです。配置するとフォームデザイナ上ではアイコンだけが表示されるようになります。Delphiでは、こうした「実際には表示されないコンポーネント」というのがけっこうあります。要するに、画面で操作するのではなく、プログラムの中から利用するためのコンポーネントというわけですね。

 これらのコンポーネントの使い方は、比較的簡単です。これらはグラフィックファイル用に用意されているものなので、最初からDelphiで利用できるグラフィックフォーマットのファイルしか選べないような形で作られているのです。従って、単純に「ダイアログを呼び出して、選択したファイルのパスを取り出す」ということだけできれば利用できるようになります。

 ダイアログの呼び出しは、2つのコンポーネントとも共通で、「Execute」というメソッドを呼び出すだけです。これはパラメータも何も持たないシンプルなメソッドで、これを呼び出すと画面にダイアログを表示し、閉じた段階で真偽値の値を返します。これがTrueならばファイル名を入力して閉じられたことを意味し、Falseならばキャンセルされたことを示すわけです。――え? なんだかよくわからないって?

 要するに、以下のような形でif文を使ってExecuteを呼び出せば、ダイアログを利用できるってわけです。

if 《ダイアログ》.Execute then ・・・ファイルを読み書きする処理・・・

 これで、ダイアログを表示し、それでファイルを入力して閉じたら何かの処理をする、ということができます。で、肝心の「選択したり入力したファイルをどう知るか」ですが、これは「FileName」というプロパティで得ることができます。これは、最後に選択されたファイルのパスを示すStringのプロパティです。つまりExecuteした後で、コンポーネントのFileNameを調べれば、入力したファイルのパスがわかるってわけです。

 では、簡単なサンプルを作ってみましょう。フォームにImageを1つ作成し、それからOpenPictureDialog、SavePictureDialogをそれぞれ1つ配置します。そして読み書きするためのButtonを2つ用意しましょう。

 ここで2つのボタンに「ファイルからイメージを読み込んでImage1に表示する」「Image1のイメージをファイルに保存する」というプロシージャを作成します。


procedure TForm1.Button1Click(Sender: TObject);  
var
	fStr: String;

begin
	try
		if OpenPictureDialog1.Execute then
	begin
		fStr := OpenPictureDialog1.FileName;
		Image1.Picture.LoadFromFile(fStr);
	end;
	except
		Beep;
	end;
end;


procedure TForm1.Button2Click(Sender: TObject);  
var
	fStr: String;

begin
	try
		if SavePictureDialog1.Execute then
	begin
		fStr := SavePictureDialog1.FileName;
		Image1.Picture.SaveToFile(fStr);
	end;
	except
		Beep;
	end;
end;

 

 ざっとこんな感じでしょうか。なんか、あっけないほど簡単にできてしまいました。たったこれだけで、グラフィックをファイルに読み書きできてしまうんですから、Delphiってのはたいしたもんですね。

 それと、今回はどちらもtryを使ってますが、ファイルダイアログのFileNameを使った場合、「ファイルやディレクトリが存在しない」ということはまずありえませんから、tryをはずしてしまっても大丈夫でしょう。

 

■テキストを読み書きする


 では次に、テキスト関係の読み書きを行なってみましょう。これは、Editコンポーネントは使いません。Editには、ファイルアクセスのメソッドなどは用意されていないのです。まぁ、1行だけのテキストしか使えないものですから必要無いと考えたんでしょうね。

 長文テキストを扱うコンポーネントには「Memo」「RichEdit」があります。Memoは「Standard」タブに、RichEditは「Win32」タブにそれぞれ用意されています。どちらも基本的な扱いは同じなのですが、RichEditはリッチテキスト対応になっていて、テキストのフォントやスタイルを変更できます。そのためのプロパティやメソッドなどがいくつか追加されているのが特徴です。

 とりあえず、共通するいくつかのプロパティについて頭に入れておきましょう。これらは、プログラム内で使うというより、オブジェクトインスペクタのプロパティタブであらかじめ設定をしておく、というものです。

Lines――テキストを設定するものです。右端の「・・・」ボタンをクリックするとダイアログが開き、テキストを書き込んでおけるようになっています。(実は、Editなどと同様にTextプロパティもあるのですが、オブジェクトインスペクタではLinesしか表示されません)

ScrollBars――スクロールバーの表示を示すものです。これを設定しないと、長文を書いてもスクロールバーが現れません。設定は、以下の4つの内のいずれかにします。
(ssNone, ssBoth, ssHorizontal, ssVertical)

Alignment――テキストの文字揃えに関するもので、どちらにそろえるかを指定します。以下の値が用意されています。
(taLeftJustify, taCenter, taRightJustify)

WantTabs, WantReturns――それぞれTabキー、Returnキー(enterキー)を受け付けるかどうかを示すものです。これは、あくまでキー入力を受け付けるかなので、例えばタブや改行を含んだテキストをペーストしたりした場合には、それらはきちんと認識されます。

WordWrap――ワードラップに関するものです。Trueにすればワードラップして表示します。

 これらをあらかじめ設定して使うのが基本と考えておくと良いでしょう。これらの中で、「扱うテキストのデータ」を示すものが「Lines」です。Imageでグラフィックに関するオブジェクトとしてPictureがあったように、このLinesが、MemoやRichEditのテキストデータに関するさまざまな機能を持っています。ファイルアクセスのメソッドも、このLinesの中にあります。

《Lines》.LoadFromFile( ファイルパス );

《Lines》.SaveToFile( ファイルパス );

 見ればわかるように、Pictureにあったメソッドと全く同じですね。従って、使い方も全く一緒。ただし、前に使ったOpenPictureDialog/SavePictureDialogはグラフィックファイル用ですから、それ以外には使えません。普通のファイル選択用のダイアログは、やはり「Dialog」タブに「OpenDialog」「SaveDialog」というものが用意されています。これらは、先のグラフィック用のダイアログの左側にある2つのアイコンです。使い方は同じで、ExecuteしてFileNameを取り出す、というやり方ですね。

 では、Memoコンポーネントを使って、テキストファイルを読み書きするようなサンプルを作ってみましょう。フォームデザイナにMemo、OpenDialog、SaveDialogをそれぞれ1つ、そしてButtonを2つ作成します。この2つのボタンで、テキストファイルを読み書きするようにしてみましょう。


procedure TForm1.Button1Click(Sender: TObject);  
var
	fStr: String;

begin
	if OpenDialog1.Execute then
	begin
		fStr := OpenDialog1.FileName;
		Memo1.Lines.LoadFromFile(fStr);
	end;
end;

procedure TForm1.Button2Click(Sender: TObject);  
var
	fStr: String;

begin
	if SaveDialog1.Execute then
	begin
		fStr := SaveDialog1.FileName;
		Memo1.Lines.SaveToFile(fStr);
	end;
end;

 

 こんな感じでしょうか。今回は、tryを取り外した形で書いてみました。これで実際に動かしてみると、Memoのテキストをテキストファイルに読み書きできることがわかりますよ。

 ただし、実際にやってみると、少しだけ問題があることに気づくでしょう。それは、ファイルダイアログでテキストファイル以外のものも選択できてしまうことです。これは、ダイアログに表示するファイルの種類を設定する「フィルター」というものが用意されていないからです。フィルターというのは、Windowsではファイルダイアログの下の方に「ファイルの種類」というコンボボックスが表示荒れますよね? あれのことだと考えて下さい。

 OpenDialog/SaveDialogコンポーネントには「Filter」というプロパティがあります。これが、フィルターを設定するためのものです。このプロパティの右端にある「・・・」ボタンをクリックすると、「フィルタの設定」ダイアログが現れます。

 これは縦に2列になっていて、左側の「フィルタ名」にはファイルダイアログの「ファイルの種類」に表示される名前、右側の「フィルタ」で実際にダイアログで表示可能になるファイルの種類(拡張子)を記します。

 ファイルの種類は、例えばTXT拡張子を使いたいのであれば「*.txt」というように記述します。複数の拡張子を設定したければ、それらをセミコロンでつなげて記述します。

 これで、必要なファイルだけを選択できるようにしておけば、開けないファイルを開いてしまうようなトラブルを防ぐことができます。

 

■Linesの仕組み


 ところで、この「Lines」というオブジェクトですが、一体どういうものなんでしょうか。Textプロパティとはどう違うんでしょう?

 このLinesは、「テキストを行単位で扱えるようにしたオブジェクト」と考えればいいでしょう。実はこのLinesは、その後に [ 行数 ] というように指定することで、特定の行のテキストを取り出したり書き換えたりできるんです。例えば、

Memo1.Lines[0] := 'Hello';

こんな感じで、Memo1の最初の行を書き換えることができます。なかなか便利そうでしょ? 注意しておきたいのは、一番最初の行が1ではなくて0になるってことですね。――この他に、Linesの中にはさまざまなメソッドが用意されていて、行単位でテキストを自由に扱えるんです。ざっとメソッドを整理してみましょう。

《Lines》.Add(《String》);
一番最後にテキストを追加する。

《Lines》.Insert(《Integer》,《String》);
第1パラメータの番号の後にテキストを追加する。

《Lines》.Delete(《Integer》);
パラメータで指定した番号の行を削除する。

《Lines》.Clear;
すべて削除する。

《Lines》.Move(《Integer1》,《Integer2》);
Integer1の行をInteger2番目に移動する。

《Lines》.Exchange(《Integer1》,《Integer2》);
2つの番号の行を入れ替える。

《変数》 = 《Lines》.Count;
行数を調べる。

 まぁ、別に全部覚える必要は全くないんですが、こんな具合に、行単位でテキストが操作できるってのは、なかなか便利です。単に長文のテキストを書くだけでなく、データベース的に情報を管理したりするのにも使えそうですね。テキスト関係のプログラムを作ってみたい人は、ちょっとこれらのメソッドの働きを試してみるといいでしょう。

 


GO NEXT


GO HOME