FPC(Free Pascal Compiler)で簡単な外部コマンドを実行した後、その戻り値が途切れる問題で一晩ハマったのでメモしておきます。
問題のコード
実行したのは、次のようなコードです。
var
jsonResponse: string;
jsonData: TJSONData;
begin
jsonResponse := MyExecCommand(params);
jsonData := GetJSON(jsonResponse);
// …省略…
end;
少し長いJSONデータだと、必ずエラーがでます。
これ、何が問題か分かるでしょうか?
答え
問題点は、変数jsonResponse
の定義がstring
となっている部分です。
今回、FPCの設定でstringがShortString(つまり、255文字以下)と定義されていたため、255字以上の文字列が壊れてしまっていたのです。これに気付くまでに一晩かかりました(T_T)
ShortStringは255以下の文字列しか扱えないという制限があります。そのため、上記のstring
をAnsiString
に定義を変えると問題は解決します。
ShortStringとAnsiStringの違い
ShortString
と AnsiString
は、Pascalで使用される文字列型で、それぞれ以下のような違いがあります。
-
ShortString
--- 固定長文字列:最大長が255文字に制限されています。スタック上に配置され、メモリの再割り当てを行わないため、サイズの範囲内で高速に処理できます。 -
AnsiString
--- 動的長文字列:サイズに制限がなく、メモリの許す限り長い文字列を扱えます。ヒープに配置され、メモリの再割り当てによりサイズを自動で調整します。これにより、大きな文字列を動的に扱うことが可能です。
ただし、AnsiStringは、Unicode非対応であるため、1バイト単位で文字を扱います。多言語文字が必要な場合は UnicodeString
を利用します。
FPC/Delphi/Pascalの文字列処理は、文字列型がたくさんあって、扱いが本当に複雑です。「文字列型を制する者がPascalを制する」と言って良い状況かもです。
FPCにはモードがある
なお、コンパイル指示で {$H+}を有効にすると、stringが、AniStringになります。多くのIDE環境でこれが自動で指定されているため、コマンドラインでFPCを適当に使った場合に、今回の問題に遭遇してしまいます。また、最初から「Delphi」互換モードにしておけば、stringは勝手にAnsiStringになります。
FPCには次のように多くの互換モードがあり、それぞれの違いを把握するのは非常に難しい状況です。モードによって、型が変更になるというのは、最初に知っておくべきですね。
-
FPCモード (
{$MODE FPC}
):デフォルトモードで、FPC独自の構文や仕様を使用。 -
Delphiモード (
{$MODE Delphi}
):Delphi互換モードで、Delphiコードの互換性を高める。 -
OBJFPCモード (
{$MODE OBJFPC}
):オブジェクト指向拡張が強化されたモード。 -
TPモード (
{$MODE TP}
):Turbo Pascal互換モード。
まとめ
FPC深い。