プログラムを作成するとき、いつも使用するコードはひな型ファイルとして用意しておくと便利です。
LazausやCodeTyphonのようなIDE(統合開発環境)を使用する場合は、新規プロジェクト作成時に用意されたテンプレートでソースコードが作成されますが、テキストエディタで一からプログラムを作成する場合には、いつも使用するコードをひな型ファイルとして用意し、それに機能を実装していくのが便利です。
ひな形の各構成要素が何を意味しているかを理解すればObject Pascalプログラミングは難しくありません。
私の場合は以下のようなひな型ファイルを作成して使用しています。
programファイルはメインプログラムになるファイルです。コードページ UTF-8 で作成してください。
program Prog1;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
uses
SysUtils{, Classes, Unit1, ...};
{ ここに 宣言・定義 }
begin
SetMultiByteConversionCodePage(CP_UTF8);
SetMultiByteRTLFileSystemCodePage(CP_UTF8);
{ ここに 実行文 }
end.
Lazarusでは、programファイル(メインプログラム)の拡張子は「.lpr」になりますが、内容は普通のPascalファイルです。Lazarusのプロジェクト管理のために「.lpr」となっています。同様にCodeTyphonでは「.ppr」になります。このひな型を「.lpr」や「.ppr」ファイルに適用することもできます。
programは、次のような要素で構成されます。
program Prog1;
ヘッダ部はプログラムの先頭でプログラム名を設定します。
標準Pascalのようにprogram Prog1(Input, Output);などのファイルの引数はありません。指定しても問題ありませんが無視されます。
Free Pascalでは、ヘッダ部を省略することができます。
プログラム名はプログラムのネームスペースになります。プログラム名には「.」を含むこともできます。ヘッダ部を省略した場合はネームスペースは生成されないと思います。省略時のデフォルトネームスペースがあるかどうかはわかりませんでした。まぁ、programのネームスペースを使わなければならないケースはないと思います。
私は昔からPascalになじみがあるのでヘッダ部を省略すると違和感があります。
生成されるEXEファイルのファイル名はプログラム名ではなく、ソースファイルのファイル名の拡張子を.exeにしたものが既定のファイル名になります。
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
コンパイラディレクティブはソースコード内でコンパイル時にコンパイラに対して指示をするものです。
ディレクティブには、グローバルディレクティブとローカルディレクティブがあります。
グローバルディレクティブは、そのソースファイル全体に影響します。他のソースファイルには影響しません。必要であればそれぞれのソースファイルで指定します。同じディレクティブはソースファイル内で1回だけ指定できます。
uses句より前に指定します。ヘッダ部の前でも可能です。
ローカルディレクティブは、ソースファイル全体ではなく記述された時点から有効になります。ソースファイル内で複数指定可能で設定を切り替えることができます。他のソースファイルには影響しません。
{$MODE OBJFPC}は、グローバルディレクティブになります。コンパイルモードの指定でコンパイラにソースコードをObject Pascalとしてコンパイルするよう指示します。
{$H+}は、ローカルディレクティブになります。長い書き方では{$LONGSTRINGS ON}で、String型をAnsiString型(長い文字列)として扱うように指定します。{$H-}または{$LONGSTRINGS OFF}とした場合は、String型はShortString型(255文字までの文字列)となります。ソースコードの任意の場所に指定してString型の扱いを切り替えることも可能です(推奨はしません)。文字列型をAnsiString型やShortString型として宣言すれば本ディレクティブの影響は受けません。{$H-}と{$H+}のどちらが既定になるかはコンパイルモードによって違います。OBJFPCモードでは{$H-}が設定されるため{$H+}を指定して切り替えます。また、{$MODE OBJFPC}の後に{$H+}を指定します。逆にすると{$MODE OBJFPC}が{$H-}を設定するので{$H+}が{$H-}に切り替えられてしまいます。
{$J-}は、ローカルディレクティブになります。既定の動作では型付き定数は代入可能な初期化済み変数のようになります。{$J-}を指定することにより代入不可の定数として扱うようになります。Turbo Pascal時代に変数宣言で初期化ができなかったため、型付き定数宣言でその機能を実装したようです。現在では変数宣言で初期化の設定が可能なため型付き定数は安全のために代入不可能な定数として扱うようにします。
{$MINFPCONSTPREC 64}は、ローカルディレクティブとなります。コンパイラが実数のリテラルや型指定のない定数に型を割り当てるときに精度を落とさず格納可能な最小の型を割り当てます。そのため、0.5はSingle型(32bit実装)となり、0.1はDouble型(64bit実数)になります。計算結果の精度にも影響するため、すべてDouble型(64bit実数)を割り当てるために{$MINFPCONSTPREC 64}を指定します。既定に戻すには{$MINFPCONSTPREC 32}または{$MINFPCONSTPREC DEFAULT}を指定します。
{$CODEPAGE UTF8}は、グローバルディレクティブになります。ソースコードがコードページUTF-8で書かれていることをコンパイラに教えます。Free PascalではソースコードをUTF-8で作成してください。このディレクティブがないとソースコードに書かれた文字列リテラルをプログラム内のデータとしてコンパイルするときに正しく変換できなくなります。
その他に、{$APPTYPE CONSOLE}と{$APPTYPE GUI}があります。これらはグローバルディレクティブで、コンソールアプリケーションとして作成するかGUIアプリケーションとして作成するかを指定します。どちらか選択になります。Free Pascalではどちらも省略するとコンソールアプリケーションになります。ちなみにDelphiでは省略するとGUIアプリケーションになります。
{$APPTYPE …}はprogramファイルのみ有効です。ユニットやライブラリでは無視されます。
Lazarusでは、GUIアプリケーションの場合も{$APPTYPE GUI}が設定されませんが、コンパイラのコマンドラインオプションで「-WG」を指定することでGUIアプリケーションとして作成します。
ディレクティブとコンパイラのコマンドラインオプションで同じ設定がある場合、ソースコードのディレクティブが優先(上書き)されます。
uses
SysUtils{, Classes, Unit1, ...};
uses句は、プログラムで使用するユニットを指定します。「,」で区切って複数指定することができます。
何も指定しない場合、uses句は省略可能です。
SysUtilsを使用するとランタイムエラーを例外として扱うことができるようになり、例外の捕捉(try 〜 except 〜 end)もできるようになるため、uses句で指定することを推奨します。その他タイプヘルパー、文字列関数、日付・時間など便利なルーチンも使用できるようになります。
Classesを使用するとFree Componet Libraly(FCL)の基本クラスが使用できるようになります。TStringListやTStreamなど使う場合は指定します。ひな形ではコメントアウトしています。
ちなみに、Classesの中でSysUtilsがusesに指定されているので、Classesだけを指定してもSysUtilsは組み込まれ、ランタイムエラーを例外として扱うことができるようになります。SysUtilsが公開している識別子(定数,型,変数,関数/手続き)などを使用(コードに記述)する場合はusesにSysUtilsを明示的に指定します。
{ ここに 宣言・定義 }
ラベル、定数、型、変数、手続き/関数などを宣言・定義はヘッダ部とuses句の後で、実行文部の前に記述します。標準Pascalでは、これらの記述順序は決まっていますが、Free Pascalでは(label, const, type, var, threadvar, resourcestring, procedure, function)が順不同で複数回の記述が可能です。
begin
SetMultiByteConversionCodePage(CP_UTF8);
SetMultiByteRTLFileSystemCodePage(CP_UTF8);
{ ここに 実行文 }
end.
begin〜endの間にFree Pascal(Object Pascal)の実行文を記述します。
最後の「.」はプログラム定義の終了です。
Free Pascalでは実行文部の中にラベル、定数、型、変数、手続き/関数などの宣言・定義を記述することはできません。これはPascal言語の一般的な規則ですが、Delphiではインライン変数宣言という機能が追加され、実行文部の中で変数宣言ができるようになっています。
これは必須な構文ではなく、単に手続きの呼び出しを行っている実行文です。
「SetMultiByteConversionCodePage(CP_UTF8)」は、ワイド文字(UTF-16)とマルチバイト文字の変換で使用されるマルチバイト文字のコードページを設定します。日本語Windowsの場合コードページ932(シフトJIS)が設定されていますが、ここではCP_UTF8(=65001)でコードページUTF-8に設定します。設定されているコードページは変数DefaultSystemCodePageで確認することができます。
「SetMultiByteRTLFileSystemCodePage(CP_UTF8)」は、OSのファイルシステムのファイル名/パス名をRawByteStringで受け取った文字列をマルチバイト文字列へ変換する場合に使用されるマルチバイト文字のコードページを設定します。日本語Windowsの場合コードページ932(シフトJIS)が設定されていますが、ここではCP_UTF8(=65001)でコードページUTF-8に設定します。設定されているコードページは変数DefaultRTLFileSystemCodePageで確認することができます。
長い文字列(AnsiString型)は、文字列データのほか、その文字列のコードページ、長さ、参照カウントを持っているので、文字列関数や演算などで異なるコードページ同士でも正しく変換して処理されます。通常はコードページをあまり意識する必要はないかもしれません。ただ、Free PascalやLazarusのライブラリやパッケージでは文字列はUTF-8が前提となっているものがほとんどです。そのため、自分の書いたプログラムもUTF-8を意識して作成したほうがよいと考えます。
ちなみにLazarusやCodeTyphonのフォームアプリケーションでは、これらの手続きが使用するユニット内で呼ばれUTF-8の設定になっています。これらの手続きを追加する必要はありませんが、追加しても問題はありません。
OSの既定のコードページがUTF-8の場合も必要ありません。
実際のテストプログラムで動作を見てみましょう。
program CPTest;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
uses
SysUtils;
const
STR1 : UnicodeString = '文字列';
var
S1, S2, S3, S4, S5, S6 : String;
begin
WriteLn('■ケース1');
S1 := '文字列';
S2 := String(STR1);
GetDir(0, S3);
WriteLn('S1 => ', StringCodePage(S1));
WriteLn('S2 => ', StringCodePage(S2));
WriteLn('S3 => ', StringCodePage(S3));
SetMultiByteConversionCodePage(CP_UTF8);
SetMultiByteRTLFileSystemCodePage(CP_UTF8);
WriteLn('■ケース2');
S4 := '文字列';
S5 := String(STR1);
GetDir(0, S6);
WriteLn('S1 => ', StringCodePage(S4));
WriteLn('S2 => ', StringCodePage(S5));
WriteLn('S3 => ', StringCodePage(S6));
end.
ケース1は既定のままで、ケース2でマルチバイト文字のコードページにUTF-8を設定しています。
実行結果を以下に示します。日本語Windowsでの実行です。
■ ケース1
S1 => 65001
S2 => 932
S3 => 932
■ ケース2
S1 => 65001
S2 => 65001
S3 => 65001
65001はUTF-8です。932はシフトJISです。
日本語Windowsのシステムコードページは932です。
ケース1では、すべて932になるわけではなくUTF-8になる場合もあります。ケース2ではすべてUTF-8になりました。
マルチバイト文字のコードページをUTF-8に設定すれば必ずUTF-8になるという保証はありません。OSのAPIを低レベルで使用するライブラリなどではそのままOSのコードページを返したり、文字列データとコードページ情報が一致しない(特定できない)場合もあるかもしれません。文字列ポインターでやり取りする場合はコードページ情報自体がありません。
とりあえずマルチバイト文字のコードページをUTF-8にして、正しく処理ではないときに文字列データとコードページ情報がどうなっているかを調べるようにします。
unitファイルはメインプログラムや他のユニットから使用されるファイルです。
unit Unit1;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
interface
uses
SysUtils{, Classes, ...};
{ ここに 宣言・定義 (実装(処理)は記述しない) }
implementation
{uses }
{ ここに 実現部だけで使用するユニット, ...; }
{ ここに インターフェース部の実装 }
{ ここに ユニットプライベートな宣言・定義 }
initialization
{ ここに 初期化処理の実行文 }
finalization
{ ここに 終了処理の実行文 }
end.
unitは、次のような要素で構成されます。
unit Unit1;
ヘッダ部はユニットの先頭でユニット名を設定します。
ユニットファイル名はヘッダ部のユニット名と同じである必要があります。この場合は「Unit1.pas」になります。Windowsではファイル名の大文字小文字は区別しません。
programとちがいユニットではヘッダ部を省略することはできません。
ユニット名はユニットのネームスペースになります。ユニット名には「.」を含むこともできます。
他のモジュール(program, unit)からuses句で使用されるときの名前になります。
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
コンパイラディレクティブはソースコード内でコンパイル時にコンパイラに対して指示をするものです。内容はprogramと同じです。
ファイルごとに指定が必要です。programで設定した内容が自動的に適用されることはないので注意してください。また、programと同じ設定である必要はありませんが矛盾がないようにしてください。
interface
uses
SysUtils{, Classes, ...};
{ ここに 宣言・定義 (実装(処理)は記述しない) }
インターフェース部はinterfaceで始まり、このユニットを使用する他のモジュールから参照可能な定数、型、変数、手続き/関数/プロパティなどを宣言・定義を記述します。手続き/関数は仕様の宣言のみで実行分部(begin〜end)は記述しません。ユニットではプロパティ(property)も宣言することができます。
この宣言・定義に必要なユニットをuses句で宣言・定義の前に指定します
何も指定しない場合、uses句は省略可能です。
implementation
{uses }
{ ここに 実現部だけで使用するユニット, ...; }
{ ここに インターフェース部の実装 }
{ ここに ユニットプライベートな宣言・定義 }
実現部はimplementationではじまり、インターフォース部で宣言のみ記述した手続き/関数を宣言と実行分部(begin〜end)を合わせて定義(実装)します。
さらに、必要によりこのユニット内だけで使用される定数、型、変数、手続き/関数/プロパティなどを宣言・定義を記述します。手続き/関数は実行分部(begin〜end)も記述します。これらはユニットプライベートで他のモジュールから隠蔽されます。
インターフェース部の実装部分とプライベート部分の記述の順番は自由です。
実現部に必要なユニットをuses句で宣言・定義の前に指定します。インターフェース部で指定したuses句は実現部でも有効です。実現部だけに指定したuses句はインターフェース部には適用されません。インターフェース部と実現部で使用ユニットの重複はできません。
何も指定しない場合、uses句は省略可能です。
initialization
{ ここに 初期化処理の実行文 }
finalization
{ ここに 終了処理の実行文 }
end.
initialization 〜 finalization 〜 end でユニットの初期化と終了処理を記述します。
initializationの後にユニットの初期化のための実行文を記述します。プログラム開始時に各ユニットのユニット初期化部が実行されます。
つまりメインプログラム(program)の実行文部(begin〜end)の実行前に各ユニットの初期化部が実行されます。メインプログラムで必要なデータや資源の確保を準備することができます。
ユニット初期化部で文字列処理を行う場合、SetMultiByteConversionCodePage(CP_UTF8)とSetMultiByteRTLFileSystemCodePage(CP_UTF8)が必要であればここで実行します。これらは何度実行しても問題なないので気にせず記述可能です。メインプログラムから消さなければならないということはありません。また、他のユニットで指定してあってもかまいません。
初期化が不要な場合はユニット初期化部を省略することができます。initializationキーワードは削除しても残してもどちらでもかまいません。
finalizationの後にユニットの終了処理のための実行文を記述します。プログラム終了時に各ユニットのユニット終了処理部が実行されます。
つまりメインプログラム(program)の実行文部(begin〜end)の実行が完了した後に各ユニットの終了処理部が実行されます。動的に確保した記憶域の開放やオープンしたファイルのクローズなどを確実に行うことができます。
終了処理が不要な場合はユニット終了処理部を省略することができます。finalizationキーワードは削除しても残してもどちらでもかまいません。
endでユニット初期化部とユニット終了処理部の定義が終了します。ユニット初期化部とユニット終了処理部の両方を省略しinitialization/finalizationキーワードも省略した場合でもendは省略できません。
最後の「.」はユニット定義の終了です。
ユニット初期化部やユニット終了処理部の中でuses句の指定はできません。必要なユニットがある場合、インターフェース部か実現部のuses句で指定してください。
begin
{ ここに 初期化処理の実行文 }
end.
ユニット初期化処理部だけが必要で終了処理部が不要な場合、initialization 〜 end の処理を begin 〜 end で定義することができます。完全に等価です。
この場合も、最後の「.」はユニット定義の終了です。
本記事のプログラムコードなどは以下のページでダウンロードできます。