Difference between revisions of "File Handling In Pascal/ja"

From Free Pascal wiki
Jump to navigationJump to search
m (→‎オーソドックスな Pascal: Notes for original Pascal are added)
m (Fixed syntax highlighting; deleted category included in page template)
 
(6 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
{{File Handling In Pascal}}
 
{{File Handling In Pascal}}
  
ほとんどのプログラマにとって、ファイルの扱いは是非とも知らなければならないものです。ファイルはユーザの設定や、エラーのログや、その他諸々のものを保存するのに使えます。ここでは基本的なテクストファイルの扱い方を教えましょう。
+
ほとんどのプログラマにとって、ファイルの扱いは是非とも知らなければならないものです。ファイルはユーザの設定や、エラーのログや、その他諸々のものを保存するのに使えます。ここでは基本的なテクストファイルの扱い方を学びましょう。
  
 
= オーソドックスな Pascal =
 
= オーソドックスな Pascal =
 
Pascal は強固に型付けられた言語で、ファイル型もデータの型に file of を付けて作ります。
 
Pascal は強固に型付けられた言語で、ファイル型もデータの型に file of を付けて作ります。
  
<source>
+
<source lang=pascal>
 
type
 
type
 
   fc = file of char;
 
   fc = file of char;
Line 19: Line 19:
 
テクスト型は、
 
テクスト型は、
  
<source>
+
<source lang=pascal>
 
Program TextWriteTest;
 
Program TextWriteTest;
 
var t : text;
 
var t : text;
Line 45: Line 45:
  
 
のように用います。「ファイル名」のファイルをエディタその他で読むと、1から100までの数がならび、それぞれ四桁のスペースをとっているはずです。Free Pascal でコンソールアプリケーションを作成する場合はこのような伝統的なプログラミングが可能です。しかし Lazarus では assign, close, text といった識別子がウィジェットセットのプロパティ名やメソッド名として既に用いられていますので、system.text などと system ユニットの名前を明示するか、次に挙げられている TextFile 型などをお使いください。例えば TextFile 型は objpash.inc の中で TextFile = text として定義されており、AssignFile 手続きはユニット objpas で定義されていて、system.text を呼出します。
 
のように用います。「ファイル名」のファイルをエディタその他で読むと、1から100までの数がならび、それぞれ四桁のスペースをとっているはずです。Free Pascal でコンソールアプリケーションを作成する場合はこのような伝統的なプログラミングが可能です。しかし Lazarus では assign, close, text といった識別子がウィジェットセットのプロパティ名やメソッド名として既に用いられていますので、system.text などと system ユニットの名前を明示するか、次に挙げられている TextFile 型などをお使いください。例えば TextFile 型は objpash.inc の中で TextFile = text として定義されており、AssignFile 手続きはユニット objpas で定義されていて、system.text を呼出します。
 +
  
 
= 古い手続型スタイル =
 
= 古い手続型スタイル =
Line 50: Line 51:
 
古典的な(オブジェクト指向ではない)Pascal では、TextFile 型を使って文字列を書き込むことができます。あるいは、自前のファイル型を定義することもできます。
 
古典的な(オブジェクト指向ではない)Pascal では、TextFile 型を使って文字列を書き込むことができます。あるいは、自前のファイル型を定義することもできます。
  
<source>...
+
<source lang=pascal>...
 
type
 
type
 
  TIntegerFile = file of Integer; // Integer を書き込めます
 
  TIntegerFile = file of Integer; // Integer を書き込めます
Line 59: Line 60:
 
TStringFile = File とだけしてしまうと、何も書けません(訳注: バイト単位で読み書きする特殊なファイルになります)。TStringFile には整数を直接書くこともできません。というのもそれは string のファイルだからです。異なる型のデータを書き込むには、TextFile 型がよいでしょう。
 
TStringFile = File とだけしてしまうと、何も書けません(訳注: バイト単位で読み書きする特殊なファイルになります)。TStringFile には整数を直接書くこともできません。というのもそれは string のファイルだからです。異なる型のデータを書き込むには、TextFile 型がよいでしょう。
  
==IO==
+
== IO エラーの扱い ==
  
IO is the file handling thingy for Pascal. It tells the compiler how to deal with IO error situations: raise an exception or store the result into the IOResult variable.
+
コンパイラに IO エラーの場合どう処理するか知らせることができます: 例外を発生するか、例外を発生せず変数 IOResult に結果を格納するかです。これはコンパイラディレクティブです:
Since it is a compiler directive, you have to do this:
+
<source lang=pascal>
<source>{$I-} // Turn off checking. This way all errors go into the IOResult variable
+
{$I-} // チェックを off にする。エラーが起きたら、全て変数 IOResult に行く
{$I+} // Turn it back on; errors will lead to an EInOutError exception
+
{$I+} // チェックを on に戻す。エラーが起きると EInOutError 例外になる
 
</source>
 
</source>
  
By disabling/turning off $I, file operation results go into the IOResult variable. This is a cardinal type (Numbers). So, if you want to write IOResult, you have to use the IntToStr function. Different numbers mean different errors. So you may want to check the documentation for the different errors: [http://www.freepascal.org/docs-html/rtl/system/ioresult.html].
+
$I をディセーブル/オフにすると、ファイル操作の結果は変数 IOResult に行きます。これは符号無し整数型です。エラーの種類が格納されます。対応表は: [http://www.freepascal.org/docs-html/rtl/system/ioresult.html] にあります。
  
==File procedures==
+
== ファイル手続き ==
  
These file handling procedures and functions are located in unit system. See the FPC documentation for more details: [http://www.freepascal.org/docs-html/rtl/system/index-5.html Reference for 'System' unit].
+
System ユニットに含まれるファイル関係の手続き及び関数です。詳細は: [http://www.freepascal.org/docs-html/rtl/system/index-5.html Reference for 'System' unit] をご覧下さい。前述のように、Lazarus ではプロパティ名やメソッド名で隠蔽されるものがあります。
  
* '''Assign''' - Assign a name to a file
+
* '''Assign''' - ファイルとファイル名とを関連づけます
* '''Append''' - Opens an existing file for appending data to end of file and editing it
+
* '''Append''' - 既存のファイルを開き、その最後からデータを追加していきます。
* '''BlockRead''' -  Read data from an untyped file into memory
+
* '''BlockRead''' -  ファイルを指定のバイト数一気に読み、メモリに内容を格納します。型無しファイルも可能です。
* '''BlockWrite''' - Write data from memory to an untyped file
+
* '''BlockWrite''' - メモリの内容を、指定のバイト数一気にファイルに書き出します。型無しファイルも可能です。
* '''Close''' - Close opened file
+
* '''Close''' - 開いたファイルを閉めます。
* '''EOF''' - Check for end of file
+
* '''EOF''' - ファイル末に達したか調べます。
* '''Erase''' - Erase file from disk
+
* '''EOLN''' - テクストファイルで行末に達したか調べます。
* '''FilePos''' - Get position in file
+
* '''Erase''' - ディスクからファイルを消去します。
* '''FileSize''' - Get size of file
+
* '''FilePos''' - ファイル内の読み/書きする位置を得ます。
* '''Flush''' - Write file buffers to disk
+
* '''FileSize''' - ファイルの大きさを得ます。
* '''IOResult''' - Return result of last file IO operation
+
* '''Flush''' - ファイルバッファに残ったデータをディスクに書き出します。
* '''Read''' - Read from a text file into variable
+
* '''IOResult''' - 最後のディスク IO 操作の結果を格納します。
* '''ReadLn''' - Read from a text file into variable and goto next line
+
* '''Read''' - 変数にファイルの内容を読み込みます。テクストファイル以外にも使えます。
* '''Reset''' - Opens a file for reading
+
* '''ReadLn''' - テクストファイルから行末まで読み、次の行に行きます。
* '''Rewrite''' - Open file for writing
+
* '''Reset''' - 読み込み用にファイルを開きます。
* '''Seek''' - Change position in file
+
* '''Rewrite''' - 書き出し用にファイルを開きます。
* '''SeekEOF''' - Set file position to end of file
+
* '''Seek''' - ファイル内の読み/書きする位置を変更します。
* '''SeekEOLn''' - Set file position to end of line
+
* '''SeekEOF''' - 空白を読み飛ばしながらファイル末か調べます。
* '''Truncate''' - Truncate the file at position
+
* '''SeekEOLn''' - 空白を読み飛ばしながら行末か調べます。
* '''Write''' - Write variable to a text file
+
* '''Truncate''' - 現在の位置でファイルを切り捨てます。
* '''WriteLn''' - Write variable to a text file and append newline
+
* '''Write''' - ファイルに変数の内容を書き出します。テクストファイル以外にも使えます。
 +
* '''WriteLn''' - テクストファイルに変数の内容を書き出し、改行します。
  
  
==Example==
+
== ==
  
A full example of handling a text file of type TextFile:
+
TextFile 型を扱う例です。この例自体はコンソールアプリケーションですが、Lazarus で GUI アプリケーションを作成する場合は、こちらの例に従ってください。
  
<source>program FileTest;
+
<source lang=pascal>
 
+
program FileTest;
{$mode objfpc} // Do not forget this ever
+
{$mode objfpc} // コンパイラをオブジェクト Pascal モードにします。これを忘れないで
  
 
uses
 
uses
Line 108: Line 110:
  
 
var
 
var
  FileVar: TextFile;
+
  FileVar: TextFile; // textの代わりにこれを使います。
  
 
begin
 
begin
 
   WriteLn('File Test');
 
   WriteLn('File Test');
   AssignFile(FileVar, 'Test.txt'); // You do not have to put .txt but this is just for now
+
   AssignFile(FileVar, 'Test.txt'); // assignの代わりにこれを使います。.txt はなくても構いませんが、ここでは付けておきましょう
   {$I+} //use exceptions
+
   {$I+} // IOエラーの際、例外を発生させます
 
   try   
 
   try   
     Rewrite(FileVar);  // creating the file  
+
     Rewrite(FileVar);  // ファイルを新規に作成します  
 
     Writeln(FileVar,'Hello');
 
     Writeln(FileVar,'Hello');
     CloseFile(FileVar);
+
     CloseFile(FileVar); // closeの代わりにこれを使います。
 
   except
 
   except
 
     on E: EInOutError do
 
     on E: EInOutError do
Line 125: Line 127:
 
   end;
 
   end;
 
   WriteLn('Program finished. Press enter to stop.');   
 
   WriteLn('Program finished. Press enter to stop.');   
   ReadLn;
+
   ReadLn; // ファイル変数を指定しないと標準入力から読みます。ここではリターンキーの空押しを待ちます。
end.</source>
+
end.
 
+
</source>
Now open the file in any text editor and you will see Hello written to it!
 
You can test the error handling by running it once, setting test.txt to read-only and try running it again.
 
 
 
Note that we used exception handling ({$I+}) as that is an easy way to perfom multiple file operations and handling the errors. You could also use {$I-}, but then you would have to check IOResult after each operation and modify your next operation.
 
  
Here's how appending/adding to a file works:
+
さあ、ファイルをテクストエディタで開いて、「Hello」と書かれているのを確認しましょう!
 +
一度これを走らせると、エラー処理をテストすることができます。test.txt を読み取り専用にしてもう一度プログラムを走らせてみてください。
  
<source>program EditFile;
+
注意: ここでは {$I+} にして例外を発生させています。複数のファイル操作を行う際は、この方がエラーの扱いが簡単だからです。{$I-} にすることもできますが、ファイル操作を行うたびに IOResult をチェックしなければなりません。そうしないと次の操作で IOResult の内容が変わってしまいます。
  
 +
テクストファイルに追加する方法です:
  
 +
<source lang=pascal>
 +
program EditFile;
 
{$mode objfpc}
 
{$mode objfpc}
  
Line 161: Line 163:
 
   WriteLn('Program finished. Press enter to stop.');   
 
   WriteLn('Program finished. Press enter to stop.');   
 
   Readln;
 
   Readln;
end.</source>
+
end.
 
+
</source>
Reading a file:
 
 
 
<source>program ReadFile;
 
  
 +
ファイルを読む例:
  
 +
<source lang=pascal>
 +
program ReadFile;
 
{$mode objfpc}
 
{$mode objfpc}
  
Line 184: Line 186:
 
     Reset(File1);
 
     Reset(File1);
 
     repeat
 
     repeat
       Readln(File1, Str); // Reads the whole line from the file
+
       Readln(File1, Str); // 行全体を変数 Str に読み込みます
       Writeln(Str); // Writes the line read
+
       Writeln(Str); // 読んだ行を書きます。ファイル変数を指定しない場合は標準出力に書きます。
     until(EOF(File1)); // EOF(End Of File) The the program will keep reading new lines until there is none.
+
     until(EOF(File1)); // ファイルの最後に達するまで繰り返して新しい行を読みます。
 
     CloseFile(File1);
 
     CloseFile(File1);
 
   except
 
   except
Line 196: Line 198:
 
   WriteLn('Program finished. Press enter to stop.');   
 
   WriteLn('Program finished. Press enter to stop.');   
 
   Readln;
 
   Readln;
end.</source>
+
end.
 +
</source>
  
It is possible to do some file handling using chars instead of strings. This makes it look cool :D.
+
場合によっては、string ではなく char を使ってファイルを扱うこともできます。カッコいいですね (^_-)
  
=Object style=
+
= オブジェクトスタイル =
  
In addition to the old style file handling routines mentioned above, a new system exists that uses the concept of streams (streams of data) at a higher abstraction level, which means that you as a programmer have to perform fewer steps when dealing with files.
+
上述の古いスタイルのファイル操作に加えて、stream (データストリーム)の概念に基づく抽象度の高いシステムが用意されています。これは、ファイルの扱いをもっと少ないステップで行えるということです。
  
In addition, most string handling classes have the ability to load and save content from/to file. These methods are usually named SaveToFile and LoadFromFile. A lot of other objects (such as Lazarus grids) have similar functionality, including Lazarus datasets (DBExport); it pays to look through the documentation/source code before trying to roll your own import/export routines.
+
加えて、文字列を扱うほとんどのクラスにファイルからの読み書き能力が備わっています。それらは通常 SaveToFile LoadFromFile という名前になっています。Lazarus grid のような多くの他のオブジェクトにも同様の機能があります。Lazarus dataset (DBExport) もそうです。そのドキュメントやソースコードにはインポート/エクスポートルーチンに取り組む前に一読する価値があります。
  
==Binary files==
+
== バイナリファイル ==
  
For opening files for direct access TFileStream should be used. This class is an encapsulation of the system procedures FileOpen, FileCreate, FileRead, FileWrite, FileSeek and FileClose which resides in unit FileUtil.
+
ファイルを直接開くには、TFileStream を使います。このクラスは、FileUtil ユニットにある FileOpen、FileCreate、FileRead、FileWrite、FileSeek、FileClose というシステム手続きをカプセル化したものです。
  
 
[http://www.freepascal.org/docs-html/rtl/sysutils/ioroutines.html IO routines]
 
[http://www.freepascal.org/docs-html/rtl/sysutils/ioroutines.html IO routines]
  
In the example below, note how we encapsulate the file handling action with a try..finally block. This makes sure the Filestream object is always released (the finally... part) even if there are file access (or other) errors.
+
下の例で、ファイル操作をどのように try..finally ブロックの中にカプセル化するかご覧下さい。こうすれば、ファイル操作その他でエラーが起きたとしても、Filestream オブジェクトは確実に解放されます(finally パート)。
<source>var
+
 
 +
<source lang=pascal>
 +
var
 
   Buffer: array[0..9999] of Byte;
 
   Buffer: array[0..9999] of Byte;
 
begin
 
begin
Line 223: Line 228:
 
     Free;
 
     Free;
 
   end;
 
   end;
end;</source>
+
end;
 +
</source>
  
 +
システムのメモリ容量と比較して小さなファイルは、その全体をメモリに読み込むこともできます。大きなファイルの読み込みもできますが、オペレーティングシステムはページスワップを始めるでしょうから、処理の効率という観点から無駄になります。
  
You can load entire files into memory too if its size is comparatively smaller than available system memory. Bigger sizes might work but your operating system will start using the page/swap file, making the exercise useless from a performance standpoint.
+
<source lang=pascal>
 
+
begin
<source>begin
 
 
   with TMemoryStream.Create do  
 
   with TMemoryStream.Create do  
 
   try
 
   try
Line 238: Line 244:
 
     Free;
 
     Free;
 
   end;
 
   end;
end;</source>
+
end;
 +
</source>
  
With larger files of many Gb, you may want to read in buffers of, say, 4096 bytes (you're advised to use a multiple of the filesytem cluster or block size) and do something with the data of each buffer read.
+
ギガバイトクラスのファイルになると、例えば 4096 バイトずつバッファに読み込み(ファイルシステムのクラスタあるいはブロックサイズの倍数になるようにすると効率が上がります)、そのバッファに対して処理を行うようにしたくなるでしょう。
  
<source>
+
<source lang=pascal>
 
var
 
var
 
   TotalBytesRead, BytesRead : Int64;
 
   TotalBytesRead, BytesRead : Int64;
   Buffer : array [0..4095] of byte;  // or, array [0..4095] of char
+
   Buffer : array [0..4095] of byte;  // または array [0..4095] of char
 
   FileStream : TFileStream;
 
   FileStream : TFileStream;
  
 
try
 
try
 
   FileStream := TFileStream.Create;
 
   FileStream := TFileStream.Create;
   FileStream.Position := 0;  // Ensure you are at the start of the file
+
   FileStream.Position := 0;  // ファイルの先頭に読み書き点を持っていく
   while TotalBytesRead <= FileStream.Size do  // While the amount of data read is less than or equal to the size of the stream do
+
   while TotalBytesRead <= FileStream.Size do  // 読み込み量 <= ストリームのサイズの間繰り返す
 
   begin
 
   begin
     BytesRead := FileStream.Read(Buffer,sizeof(Buffer));  // Read in 4096 of data
+
     BytesRead := FileStream.Read(Buffer,sizeof(Buffer));  // 4096 バイト読む
     inc(TotalBytesRead, BytesRead);                      // Increase TotalByteRead by the size of the buffer, i.e. 4096 bytes
+
     inc(TotalBytesRead, BytesRead);                      // バッファの大きさ分(4096) TotalByteRead を増やす
     // Do something with Buffer data
+
     // バッファのデータに対しなにかする
 
   end;     
 
   end;     
 
</source>
 
</source>
  
=== FileCopy ===
+
=== ファイルのコピー ===
With the above, we can implement a simple FileCopy function (FreePascal has none in its RTL although Lazarus has one) - adjust as needed for bigger files etc:
+
上記の例を使って、簡単なファイルコピー関数を実装することができます(Lazarus のランタイムライブラリにはこれがありますが、Free Pascal のにはありません)。必要に応じて、大きなファイルに対応するように改造する等してください:
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
function FileCopy(Source, Target: string): boolean;
 
function FileCopy(Source, Target: string): boolean;
// Copies source to target; overwrites target.
+
// Source から Target に上書きコピーする。
// Caches entire file content in memory.
+
// ファイル全体をメモリに読み込む。
// Returns true if succeeded; false if failed
+
// 成功すれば true を、失敗すれば false を返す。
 
var
 
var
 
   MemBuffer: TMemoryStream;
 
   MemBuffer: TMemoryStream;
Line 275: Line 282:
 
       MemBuffer.LoadFromFile(Source);
 
       MemBuffer.LoadFromFile(Source);
 
       MemBuffer.Position:=0;
 
       MemBuffer.Position:=0;
       MemBuffer.SaveToFile(Target); //may be same as source
+
       MemBuffer.SaveToFile(Target); // ソースと同じはず
 
       result:=true;
 
       result:=true;
 
     except
 
     except
       result:=false; //swallow exception; convert to error code
+
       result:=false; // 例外を飲んでエラーコードにする
 
     end;
 
     end;
 
   finally
 
   finally
Line 286: Line 293:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Text files==
+
== テクストファイル ==
  
In general, for text files you can use the TStringList class to load the entire file into memory and have simple access to their lines. Of course, you can also save your StringList back to file:
+
一般に、テクストファイルに対しては TStringList クラスを用いてファイル全体をメモリに読み込みます。そうすれば、各行に容易にアクセスできます。もちろん、StringList をファイルに書き戻すこともできます:
  
<source>begin
+
<source lang=pascal>
 +
begin
 
   with TStringList.Create do  
 
   with TStringList.Create do  
 
   try
 
   try
Line 298: Line 306:
 
     Free;
 
     Free;
 
   end;
 
   end;
end;</source>
+
end;
 +
</source>
  
In order to write a single string to a stream you might want to use the following procedure:
+
ストリームに一行書き込むには、次の手続きを使いたくなるかもしれません(訳注: Pascal の string 型の実装は、歴史的に変遷してきました。この例では、{$H+}コンパイラディレクティブが使われていないので、伝統的な実装を用いていると思われます。すなわち、最初の1バイトが文字列の長さをしめし、第二バイト以降が文字列の実体を格納するという方法です。このタイプの文字列型を明示的に指定したい場合は、shortstring 型を用います。{$H+}ディレクティブを指定すると、string 型は C 言語のように、ヌル文字を終端とする文字列の先頭のアドレスを示すポインタとして実装されるため、以下のような手続きは不要になるはずです。TStringList 自体はこちらの方法で実装されています):
  
<syntaxhighlight>
+
<syntaxhighlight lang=pascal>
 
procedure SaveStringToPath(theString, filePath: String);
 
procedure SaveStringToPath(theString, filePath: String);
 
var
 
var
Line 321: Line 330:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==See also==
+
== 関連項目 ==
 
 
* [[File]]
 
  
[[Category:Tutorials]]
+
* [[File/ja]]
[[Category:Code]]
 

Latest revision as of 23:35, 14 February 2020

العربية (ar) English (en) español (es) suomi (fi) français (fr) 日本語 (ja) русский (ru) 中文(中国大陆)‎ (zh_CN) 中文(台灣)‎ (zh_TW)

ほとんどのプログラマにとって、ファイルの扱いは是非とも知らなければならないものです。ファイルはユーザの設定や、エラーのログや、その他諸々のものを保存するのに使えます。ここでは基本的なテクストファイルの扱い方を学びましょう。

オーソドックスな Pascal

Pascal は強固に型付けられた言語で、ファイル型もデータの型に file of を付けて作ります。

type
  fc = file of char;
  fi = file of integer;
  fr = file of real;

はそれぞれ、char 型、integer 型、real 型のデータを収めるファイルの宣言です。単純型だけではなく、配列型、レコード型、集合型といったユーザ定義の型のファイルも可能です。

テクスト型は文字からなるファイルですが、file of char や file of string とは異なった特徴をもっており、行の概念や文字列と数との間の自動変換などの機能を持っています。

テクスト型は、

Program TextWriteTest;
var t : text;
    i : integer;
begin
  assign(t, ファイル名);
  rewrite(t);
  for i := 1 to 100 do write(t, i : 4);
  writeln(t, ' は書式付出力の例');
  close(t)
end.

Program TextReadTest;
var t : text;
    i : integer;
    s : string;
begin
  assign(t, ファイル名);
  reset(t);
  for i := 1 to 100 do read(t, i);
  readln(t, s);
  close(t)
end.

のように用います。「ファイル名」のファイルをエディタその他で読むと、1から100までの数がならび、それぞれ四桁のスペースをとっているはずです。Free Pascal でコンソールアプリケーションを作成する場合はこのような伝統的なプログラミングが可能です。しかし Lazarus では assign, close, text といった識別子がウィジェットセットのプロパティ名やメソッド名として既に用いられていますので、system.text などと system ユニットの名前を明示するか、次に挙げられている TextFile 型などをお使いください。例えば TextFile 型は objpash.inc の中で TextFile = text として定義されており、AssignFile 手続きはユニット objpas で定義されていて、system.text を呼出します。


古い手続型スタイル

古典的な(オブジェクト指向ではない)Pascal では、TextFile 型を使って文字列を書き込むことができます。あるいは、自前のファイル型を定義することもできます。

...
type
 TIntegerFile = file of Integer; // Integer を書き込めます
 TPCharFile = file of PChar; // PChar を書き込めます
 TStringFile = file of String; // String を書き込めます
...

TStringFile = File とだけしてしまうと、何も書けません(訳注: バイト単位で読み書きする特殊なファイルになります)。TStringFile には整数を直接書くこともできません。というのもそれは string のファイルだからです。異なる型のデータを書き込むには、TextFile 型がよいでしょう。

IO エラーの扱い

コンパイラに IO エラーの場合どう処理するか知らせることができます: 例外を発生するか、例外を発生せず変数 IOResult に結果を格納するかです。これはコンパイラディレクティブです:

{$I-} // チェックを off にする。エラーが起きたら、全て変数 IOResult に行く
{$I+} // チェックを on に戻す。エラーが起きると EInOutError 例外になる

$I をディセーブル/オフにすると、ファイル操作の結果は変数 IOResult に行きます。これは符号無し整数型です。エラーの種類が格納されます。対応表は: [1] にあります。

ファイル手続き

System ユニットに含まれるファイル関係の手続き及び関数です。詳細は: Reference for 'System' unit をご覧下さい。前述のように、Lazarus ではプロパティ名やメソッド名で隠蔽されるものがあります。

  • Assign - ファイルとファイル名とを関連づけます
  • Append - 既存のファイルを開き、その最後からデータを追加していきます。
  • BlockRead - ファイルを指定のバイト数一気に読み、メモリに内容を格納します。型無しファイルも可能です。
  • BlockWrite - メモリの内容を、指定のバイト数一気にファイルに書き出します。型無しファイルも可能です。
  • Close - 開いたファイルを閉めます。
  • EOF - ファイル末に達したか調べます。
  • EOLN - テクストファイルで行末に達したか調べます。
  • Erase - ディスクからファイルを消去します。
  • FilePos - ファイル内の読み/書きする位置を得ます。
  • FileSize - ファイルの大きさを得ます。
  • Flush - ファイルバッファに残ったデータをディスクに書き出します。
  • IOResult - 最後のディスク IO 操作の結果を格納します。
  • Read - 変数にファイルの内容を読み込みます。テクストファイル以外にも使えます。
  • ReadLn - テクストファイルから行末まで読み、次の行に行きます。
  • Reset - 読み込み用にファイルを開きます。
  • Rewrite - 書き出し用にファイルを開きます。
  • Seek - ファイル内の読み/書きする位置を変更します。
  • SeekEOF - 空白を読み飛ばしながらファイル末か調べます。
  • SeekEOLn - 空白を読み飛ばしながら行末か調べます。
  • Truncate - 現在の位置でファイルを切り捨てます。
  • Write - ファイルに変数の内容を書き出します。テクストファイル以外にも使えます。
  • WriteLn - テクストファイルに変数の内容を書き出し、改行します。


TextFile 型を扱う例です。この例自体はコンソールアプリケーションですが、Lazarus で GUI アプリケーションを作成する場合は、こちらの例に従ってください。

program FileTest;
{$mode objfpc} // コンパイラをオブジェクト Pascal モードにします。これを忘れないで

uses
 Sysutils;

var
 FileVar: TextFile;  // textの代わりにこれを使います。

begin
  WriteLn('File Test');
  AssignFile(FileVar, 'Test.txt'); // assignの代わりにこれを使います。.txt はなくても構いませんが、ここでは付けておきましょう
  {$I+} // IOエラーの際、例外を発生させます
  try  
    Rewrite(FileVar);  // ファイルを新規に作成します  
    Writeln(FileVar,'Hello');
    CloseFile(FileVar);  // closeの代わりにこれを使います。
  except
    on E: EInOutError do
    begin
     Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
    end;    
  end;
  WriteLn('Program finished. Press enter to stop.');  
  ReadLn;  // ファイル変数を指定しないと標準入力から読みます。ここではリターンキーの空押しを待ちます。
end.

さあ、ファイルをテクストエディタで開いて、「Hello」と書かれているのを確認しましょう! 一度これを走らせると、エラー処理をテストすることができます。test.txt を読み取り専用にしてもう一度プログラムを走らせてみてください。

注意: ここでは {$I+} にして例外を発生させています。複数のファイル操作を行う際は、この方がエラーの扱いが簡単だからです。{$I-} にすることもできますが、ファイル操作を行うたびに IOResult をチェックしなければなりません。そうしないと次の操作で IOResult の内容が変わってしまいます。

テクストファイルに追加する方法です:

program EditFile;
{$mode objfpc}

uses
 Sysutils;

var
 File1: TextFile;
 
begin
  WriteLn('Append file');
  {$I+}
  try
    AssignFile(File1, 'File.txt');
    Append(File1, 'adding some text...');
    CloseFile(File1);
  except
    on E: EInOutError do
    begin
     Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
    end;    
  end;
  WriteLn('Program finished. Press enter to stop.');  
  Readln;
end.

ファイルを読む例:

program ReadFile;
{$mode objfpc}

uses
 Sysutils;

var
 File1: TextFile;
 Str: String;
 
begin
  Writeln('File Reading:');
  AssignFile(File1, 'File.txt');
  {$I+}
  try
    Reset(File1);
    repeat
      Readln(File1, Str); // 行全体を変数 Str に読み込みます
      Writeln(Str); // 読んだ行を書きます。ファイル変数を指定しない場合は標準出力に書きます。
    until(EOF(File1)); // ファイルの最後に達するまで繰り返して新しい行を読みます。
    CloseFile(File1);
  except
    on E: EInOutError do
    begin
     Writeln('File handling error occurred. Details: '+E.ClassName+'/'+E.Message);
    end;    
  end;
  WriteLn('Program finished. Press enter to stop.');  
  Readln;
end.

場合によっては、string ではなく char を使ってファイルを扱うこともできます。カッコいいですね (^_-)

オブジェクトスタイル

上述の古いスタイルのファイル操作に加えて、stream (データストリーム)の概念に基づく抽象度の高いシステムが用意されています。これは、ファイルの扱いをもっと少ないステップで行えるということです。

加えて、文字列を扱うほとんどのクラスにファイルからの読み書き能力が備わっています。それらは通常 SaveToFile と LoadFromFile という名前になっています。Lazarus grid のような多くの他のオブジェクトにも同様の機能があります。Lazarus dataset (DBExport) もそうです。そのドキュメントやソースコードにはインポート/エクスポートルーチンに取り組む前に一読する価値があります。

バイナリファイル

ファイルを直接開くには、TFileStream を使います。このクラスは、FileUtil ユニットにある FileOpen、FileCreate、FileRead、FileWrite、FileSeek、FileClose というシステム手続きをカプセル化したものです。

IO routines

下の例で、ファイル操作をどのように try..finally ブロックの中にカプセル化するかご覧下さい。こうすれば、ファイル操作その他でエラーが起きたとしても、Filestream オブジェクトは確実に解放されます(finally パート)。

var
  Buffer: array[0..9999] of Byte;
begin
  with TFileStream.Create('SomeFile.bin', fmCreate) do 
  try
    Seek('Hello');
    Write(Buffer, SizeOf(Buffer));
  finally
    Free;
  end;
end;

システムのメモリ容量と比較して小さなファイルは、その全体をメモリに読み込むこともできます。大きなファイルの読み込みもできますが、オペレーティングシステムはページスワップを始めるでしょうから、処理の効率という観点から無駄になります。

begin
  with TMemoryStream.Create do 
  try
    LoadFromFile('SomeFile.bin');
    Seek(0, soEnd);
    Write(Ord('A'), 1);
    SaveToFile('SomeFile.bin');
  finally
    Free;
  end;
end;

ギガバイトクラスのファイルになると、例えば 4096 バイトずつバッファに読み込み(ファイルシステムのクラスタあるいはブロックサイズの倍数になるようにすると効率が上がります)、そのバッファに対して処理を行うようにしたくなるでしょう。

var
  TotalBytesRead, BytesRead : Int64;
  Buffer : array [0..4095] of byte;  // または array [0..4095] of char
  FileStream : TFileStream;

try
  FileStream := TFileStream.Create;
  FileStream.Position := 0;  // ファイルの先頭に読み書き点を持っていく
  while TotalBytesRead <= FileStream.Size do  // 読み込み量 <= ストリームのサイズの間繰り返す
  begin
    BytesRead := FileStream.Read(Buffer,sizeof(Buffer));  // 4096 バイト読む
    inc(TotalBytesRead, BytesRead);                       // バッファの大きさ分(4096) TotalByteRead を増やす
    // バッファのデータに対しなにかする
  end;

ファイルのコピー

上記の例を使って、簡単なファイルコピー関数を実装することができます(Lazarus のランタイムライブラリにはこれがありますが、Free Pascal のにはありません)。必要に応じて、大きなファイルに対応するように改造する等してください:

function FileCopy(Source, Target: string): boolean;
// Source から Target に上書きコピーする。
// ファイル全体をメモリに読み込む。
// 成功すれば true を、失敗すれば false を返す。
var
  MemBuffer: TMemoryStream;
begin
  result:=false;
  MemBuffer:=TMemoryStream.Create;
  try
    try
      MemBuffer.LoadFromFile(Source);
      MemBuffer.Position:=0;
      MemBuffer.SaveToFile(Target); // ソースと同じはず
      result:=true;
    except
      result:=false; // 例外を飲んでエラーコードにする
    end;
  finally
    MemBuffer.Free;
  end;
end;

テクストファイル

一般に、テクストファイルに対しては TStringList クラスを用いてファイル全体をメモリに読み込みます。そうすれば、各行に容易にアクセスできます。もちろん、StringList をファイルに書き戻すこともできます:

begin
  with TStringList.Create do 
  try
    Add('Hello');
    SaveToFile('SomeFile.txt');
  finally
    Free;
  end;
end;

ストリームに一行書き込むには、次の手続きを使いたくなるかもしれません(訳注: Pascal の string 型の実装は、歴史的に変遷してきました。この例では、{$H+}コンパイラディレクティブが使われていないので、伝統的な実装を用いていると思われます。すなわち、最初の1バイトが文字列の長さをしめし、第二バイト以降が文字列の実体を格納するという方法です。このタイプの文字列型を明示的に指定したい場合は、shortstring 型を用います。{$H+}ディレクティブを指定すると、string 型は C 言語のように、ヌル文字を終端とする文字列の先頭のアドレスを示すポインタとして実装されるため、以下のような手続きは不要になるはずです。TStringList 自体はこちらの方法で実装されています):

procedure SaveStringToPath(theString, filePath: String);
var
  textFile: TFileStream = nil;
  textLength: integer;
  stringBuffer: ^String;
begin
  textLength := length(theString);
  try
    textFile := TFileStream.Create(filePath, fmOpenWrite or fmCreate);
    { write string to stream while avoiding to write the initial length }
    stringBuffer := @theString + 1;
    textFile.WriteBuffer(stringBuffer^, textLength);
  finally
    if textFile <> nil then textFile.Free;
  end;
end;

関連項目