Sign in Join
Wiki copytemporary

File Handling In Pascal/ja: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: File Handling In Pascal/ja

OldNewLine
1+ほとんどのプログラマにとって、ファイルの扱いは是非とも知らなければならないものです。ファイルはユーザの設定や、エラーのログや、その他諸々のものを保存するのに使えます。ここでは基本的なテクストファイルの扱い方を学びましょう。
2+
3+# オーソドックスな Pascal
4+
5+Pascal は強固に型付けられた言語で、ファイル型もデータの型に file of を付けて作ります。
6+
7+``` pascal
8+type
9+ fc = file of char;
10+ fi = file of integer;
11+ fr = file of real;
12+```
13+
14+はそれぞれ、char 型、integer 型、real 型のデータを収めるファイルの宣言です。単純型だけではなく、配列型、レコード型、集合型といったユーザ定義の型のファイルも可能です。
15+
16+テクスト型は文字からなるファイルですが、file of char や file of string とは異なった特徴をもっており、行の概念や文字列と数との間の自動変換などの機能を持っています。
17+
18+テクスト型は、
19+
20+``` pascal
21+Program TextWriteTest;
22+var t : text;
23+ i : integer;
24+begin
25+ assign(t, ファイル名);
26+ rewrite(t);
27+ for i := 1 to 100 do write(t, i : 4);
28+ writeln(t, ' は書式付出力の例');
29+ close(t)
30+end.
31+
32+Program TextReadTest;
33+var t : text;
34+ i : integer;
35+ s : string;
36+begin
37+ assign(t, ファイル名);
38+ reset(t);
39+ for i := 1 to 100 do read(t, i);
40+ readln(t, s);
41+ close(t)
42+end.
43+```
44+
45+のように用います。「ファイル名」のファイルをエディタその他で読むと、1から100までの数がならび、それぞれ四桁のスペースをとっているはずです。Free Pascal でコンソールアプリケーションを作成する場合はこのような伝統的なプログラミングが可能です。しかし Lazarus では assign, close, text といった識別子がウィジェットセットのプロパティ名やメソッド名として既に用いられていますので、system.text などと system ユニットの名前を明示するか、次に挙げられている TextFile 型などをお使いください。例えば TextFile 型は objpash.inc の中で TextFile = text として定義されており、AssignFile 手続きはユニット objpas で定義されていて、system.text を呼出します。
46+
47+# 古い手続型スタイル
48+
49+古典的な(オブジェクト指向ではない)Pascal では、TextFile 型を使って文字列を書き込むことができます。あるいは、自前のファイル型を定義することもできます。
50+
51+``` pascal
52+...
53+type
54+ TIntegerFile = file of Integer; // Integer を書き込めます
55+ TPCharFile = file of PChar; // PChar を書き込めます
56+ TStringFile = file of String; // String を書き込めます
57+...
58+```
59+
60+TStringFile = File とだけしてしまうと、何も書けません(訳注: バイト単位で読み書きする特殊なファイルになります)。TStringFile には整数を直接書くこともできません。というのもそれは string のファイルだからです。異なる型のデータを書き込むには、TextFile 型がよいでしょう。
61+
62+## IO エラーの扱い
63+
64+コンパイラに IO エラーの場合どう処理するか知らせることができます: 例外を発生するか、例外を発生せず変数 IOResult に結果を格納するかです。これはコンパイラディレクティブです:
65+
66+``` pascal
67+{$I-} // チェックを off にする。エラーが起きたら、全て変数 IOResult に行く
68+{$I+} // チェックを on に戻す。エラーが起きると EInOutError 例外になる
69+```
70+
71+\$I をディセーブル/オフにすると、ファイル操作の結果は変数 IOResult に行きます。これは符号無し整数型です。エラーの種類が格納されます。対応表は: [1](http://www.freepascal.org/docs-html/rtl/system/ioresult.html) にあります。
72+
73+## ファイル手続き
74+
75+System ユニットに含まれるファイル関係の手続き及び関数です。詳細は: [Reference for 'System' unit](http://www.freepascal.org/docs-html/rtl/system/index-5.html) をご覧下さい。前述のように、Lazarus ではプロパティ名やメソッド名で隠蔽されるものがあります。
76+
77+- **Assign** - ファイルとファイル名とを関連づけます
78+- **Append** - 既存のファイルを開き、その最後からデータを追加していきます。
79+- **BlockRead** - ファイルを指定のバイト数一気に読み、メモリに内容を格納します。型無しファイルも可能です。
80+- **BlockWrite** - メモリの内容を、指定のバイト数一気にファイルに書き出します。型無しファイルも可能です。
81+- **Close** - 開いたファイルを閉めます。
82+- **EOF** - ファイル末に達したか調べます。
83+- **EOLN** - テクストファイルで行末に達したか調べます。
84+- **Erase** - ディスクからファイルを消去します。
85+- **FilePos** - ファイル内の読み/書きする位置を得ます。
86+- **FileSize** - ファイルの大きさを得ます。
87+- **Flush** - ファイルバッファに残ったデータをディスクに書き出します。
88+- **IOResult** - 最後のディスク IO 操作の結果を格納します。
89+- **Read** - 変数にファイルの内容を読み込みます。テクストファイル以外にも使えます。
90+- **ReadLn** - テクストファイルから行末まで読み、次の行に行きます。
91+- **Reset** - 読み込み用にファイルを開きます。
92+- **Rewrite** - 書き出し用にファイルを開きます。
93+- **Seek** - ファイル内の読み/書きする位置を変更します。
94+- **SeekEOF** - 空白を読み飛ばしながらファイル末か調べます。
95+- **SeekEOLn** - 空白を読み飛ばしながら行末か調べます。
96+- **Truncate** - 現在の位置でファイルを切り捨てます。
97+- **Write** - ファイルに変数の内容を書き出します。テクストファイル以外にも使えます。
98+- **WriteLn** - テクストファイルに変数の内容を書き出し、改行します。
99+
100+## 例
101+
102+TextFile 型を扱う例です。この例自体はコンソールアプリケーションですが、Lazarus で GUI アプリケーションを作成する場合は、こちらの例に従ってください。
103+
104+``` pascal
105+program FileTest;
106+{$mode objfpc} // コンパイラをオブジェクト Pascal モードにします。これを忘れないで
107+
108+uses
109+ Sysutils;
110+
111+var
112+ FileVar: TextFile; // textの代わりにこれを使います。
113+
114+begin
115+ WriteLn('File Test');
116+ AssignFile(FileVar, 'Test.txt'); // assignの代わりにこれを使います。.txt はなくても構いませんが、ここでは付けておきましょう
117+ {$I+} // IOエラーの際、例外を発生させます
118+ try
119+ Rewrite(FileVar); // ファイルを新規に作成します
120+ Writeln(FileVar,'Hello');
121+ CloseFile(FileVar); // closeの代わりにこれを使います。
122+ except
123+ on E: EInOutError do
124+ begin
125+ Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
126+ end;
127+ end;
128+ WriteLn('Program finished. Press enter to stop.');
129+ ReadLn; // ファイル変数を指定しないと標準入力から読みます。ここではリターンキーの空押しを待ちます。
130+end.
131+```
132+
133+さあ、ファイルをテクストエディタで開いて、「Hello」と書かれているのを確認しましょう! 一度これを走らせると、エラー処理をテストすることができます。test.txt を読み取り専用にしてもう一度プログラムを走らせてみてください。
134+
135+注意: ここでは {\$I+} にして例外を発生させています。複数のファイル操作を行う際は、この方がエラーの扱いが簡単だからです。{\$I-} にすることもできますが、ファイル操作を行うたびに IOResult をチェックしなければなりません。そうしないと次の操作で IOResult の内容が変わってしまいます。
136+
137+テクストファイルに追加する方法です:
138+
139+``` pascal
140+program EditFile;
141+{$mode objfpc}
142+
143+uses
144+ Sysutils;
145+
146+var
147+ File1: TextFile;
148+
149+begin
150+ WriteLn('Append file');
151+ {$I+}
152+ try
153+ AssignFile(File1, 'File.txt');
154+ Append(File1, 'adding some text...');
155+ CloseFile(File1);
156+ except
157+ on E: EInOutError do
158+ begin
159+ Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
160+ end;
161+ end;
162+ WriteLn('Program finished. Press enter to stop.');
163+ Readln;
164+end.
165+```
166+
167+ファイルを読む例:
168+
169+``` pascal
170+program ReadFile;
171+{$mode objfpc}
172+
173+uses
174+ Sysutils;
175+
176+var
177+ File1: TextFile;
178+ Str: String;
179+
180+begin
181+ Writeln('File Reading:');
182+ AssignFile(File1, 'File.txt');
183+ {$I+}
184+ try
185+ Reset(File1);
186+ repeat
187+ Readln(File1, Str); // 行全体を変数 Str に読み込みます
188+ Writeln(Str); // 読んだ行を書きます。ファイル変数を指定しない場合は標準出力に書きます。
189+ until(EOF(File1)); // ファイルの最後に達するまで繰り返して新しい行を読みます。
190+ CloseFile(File1);
191+ except
192+ on E: EInOutError do
193+ begin
194+ Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
195+ end;
196+ end;
197+ WriteLn('Program finished. Press enter to stop.');
198+ Readln;
199+end.
200+```
201+
202+場合によっては、string ではなく char を使ってファイルを扱うこともできます。カッコいいですね (^_-)
203+
204+# オブジェクトスタイル
205+
206+上述の古いスタイルのファイル操作に加えて、stream (データストリーム)の概念に基づく抽象度の高いシステムが用意されています。これは、ファイルの扱いをもっと少ないステップで行えるということです。
207+
208+加えて、文字列を扱うほとんどのクラスにファイルからの読み書き能力が備わっています。それらは通常 SaveToFile と LoadFromFile という名前になっています。Lazarus grid のような多くの他のオブジェクトにも同様の機能があります。Lazarus dataset (DBExport) もそうです。そのドキュメントやソースコードにはインポート/エクスポートルーチンに取り組む前に一読する価値があります。
209+
210+## バイナリファイル
211+
212+ファイルを直接開くには、TFileStream を使います。このクラスは、FileUtil ユニットにある FileOpen、FileCreate、FileRead、FileWrite、FileSeek、FileClose というシステム手続きをカプセル化したものです。
213+
214+[IO routines](http://www.freepascal.org/docs-html/rtl/sysutils/ioroutines.html)
215+
216+下の例で、ファイル操作をどのように try..finally ブロックの中にカプセル化するかご覧下さい。こうすれば、ファイル操作その他でエラーが起きたとしても、Filestream オブジェクトは確実に解放されます(finally パート)。
217+
218+``` pascal
219+var
220+ Buffer: array[0..9999] of Byte;
221+begin
222+ with TFileStream.Create('SomeFile.bin', fmCreate) do
223+ try
224+ Seek('Hello');
225+ Write(Buffer, SizeOf(Buffer));
226+ finally
227+ Free;
228+ end;
229+end;
230+```
231+
232+システムのメモリ容量と比較して小さなファイルは、その全体をメモリに読み込むこともできます。大きなファイルの読み込みもできますが、オペレーティングシステムはページスワップを始めるでしょうから、処理の効率という観点から無駄になります。
233+
234+``` pascal
235+begin
236+ with TMemoryStream.Create do
237+ try
238+ LoadFromFile('SomeFile.bin');
239+ Seek(0, soEnd);
240+ Write(Ord('A'), 1);
241+ SaveToFile('SomeFile.bin');
242+ finally
243+ Free;
244+ end;
245+end;
246+```
247+
248+ギガバイトクラスのファイルになると、例えば 4096 バイトずつバッファに読み込み(ファイルシステムのクラスタあるいはブロックサイズの倍数になるようにすると効率が上がります)、そのバッファに対して処理を行うようにしたくなるでしょう。
249+
250+``` pascal
251+var
252+ TotalBytesRead, BytesRead : Int64;
253+ Buffer : array [0..4095] of byte; // または array [0..4095] of char
254+ FileStream : TFileStream;
255+
256+try
257+ FileStream := TFileStream.Create;
258+ FileStream.Position := 0; // ファイルの先頭に読み書き点を持っていく
259+ while TotalBytesRead <= FileStream.Size do // 読み込み量 <= ストリームのサイズの間繰り返す
260+ begin
261+ BytesRead := FileStream.Read(Buffer,sizeof(Buffer)); // 4096 バイト読む
262+ inc(TotalBytesRead, BytesRead); // バッファの大きさ分(4096) TotalByteRead を増やす
263+ // バッファのデータに対しなにかする
264+ end;
265+```
266+
267+### ファイルのコピー
268+
269+上記の例を使って、簡単なファイルコピー関数を実装することができます(Lazarus のランタイムライブラリにはこれがありますが、Free Pascal のにはありません)。必要に応じて、大きなファイルに対応するように改造する等してください:
270+
271+``` pascal
272+function FileCopy(Source, Target: string): boolean;
273+// Source から Target に上書きコピーする。
274+// ファイル全体をメモリに読み込む。
275+// 成功すれば true を、失敗すれば false を返す。
276+var
277+ MemBuffer: TMemoryStream;
278+begin
279+ result:=false;
280+ MemBuffer:=TMemoryStream.Create;
281+ try
282+ try
283+ MemBuffer.LoadFromFile(Source);
284+ MemBuffer.Position:=0;
285+ MemBuffer.SaveToFile(Target); // ソースと同じはず
286+ result:=true;
287+ except
288+ result:=false; // 例外を飲んでエラーコードにする
289+ end;
290+ finally
291+ MemBuffer.Free;
292+ end;
293+end;
294+```
295+
296+## テクストファイル
297+
298+一般に、テクストファイルに対しては TStringList クラスを用いてファイル全体をメモリに読み込みます。そうすれば、各行に容易にアクセスできます。もちろん、StringList をファイルに書き戻すこともできます:
299+
300+``` pascal
301+begin
302+ with TStringList.Create do
303+ try
304+ Add('Hello');
305+ SaveToFile('SomeFile.txt');
306+ finally
307+ Free;
308+ end;
309+end;
310+```
311+
312+ストリームに一行書き込むには、次の手続きを使いたくなるかもしれません(訳注: Pascal の string 型の実装は、歴史的に変遷してきました。この例では、{\$H+}コンパイラディレクティブが使われていないので、伝統的な実装を用いていると思われます。すなわち、最初の1バイトが文字列の長さをしめし、第二バイト以降が文字列の実体を格納するという方法です。このタイプの文字列型を明示的に指定したい場合は、shortstring 型を用います。{\$H+}ディレクティブを指定すると、string 型は C 言語のように、ヌル文字を終端とする文字列の先頭のアドレスを示すポインタとして実装されるため、以下のような手続きは不要になるはずです。TStringList 自体はこちらの方法で実装されています):
313+
314+``` pascal
315+procedure SaveStringToPath(theString, filePath: String);
316+var
317+ textFile: TFileStream = nil;
318+ textLength: integer;
319+ stringBuffer: ^String;
320+begin
321+ textLength := length(theString);
322+ try
323+ textFile := TFileStream.Create(filePath, fmOpenWrite or fmCreate);
324+ { write string to stream while avoiding to write the initial length }
325+ stringBuffer := @theString + 1;
326+ textFile.WriteBuffer(stringBuffer^, textLength);
327+ finally
328+ if textFile <> nil then textFile.Free;
329+ end;
330+end;
331+```
332+
333+## 関連項目
334+
335+- [[File/ja]]
336+
337+---
338+*From the Free Pascal wiki, page "File Handling In Pascal/ja", as it stood on 2020-02-14 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history