ホーム >プログラム >Delphi 6 ローテクTips

Formatの形式文字列

覚えたとしてもすぐに忘れてしまうFormatの形式文字列をメモ。

※このページの内容はDelphi 6のFormatとWideFormatについて書いています。
それ以外のバージョンのものについては未検証です。
一部XE2でも検証していますが、完全ではありません。


構文

Delphiのヘルプから

"%" [index ":"] ["-"] [width] ["." prec] type

形式指定子は % 文字で開始し,その後に次の各部を順に指定します。変換型文字 type 以外は省略可能です。

引数インデックス指定子 [index ":"]
左揃えインジケータ ["-"]
幅指定子 [width]
精度指定子 ["." prec]
変換型文字 type

返される文字列中に%を含めたい場合 '%%' と % を続けて書きます。

  ls_Str := Format('%d%%', [125]);  //ls_Str は '125%'
  ls_Str := Format('%d%',  [125]);  //NG エラーになる
  ls_Str := Format('%%d',  [125]);  //ls_Str は '%d'

三つ目はエラーにはなりませんが 'd'の前の '%' は二つ重ねて書かれてあるので変換するための形式指定子とはならないので、この場合は単に '%d' の文字が返るだけです。


%s

文字列に置き換え。

  ls_Weather = '雨'

  ls_Str :=     Format('今日の天気は%sです。', [ls_Weather]);  //ls_Str は '今日の天気は雨です。'
  ls_Str := WideFormat('今日の天気は%sです。', [ls_Weather]);  //ls_Str は '今日の天気は雨です。'

FormatとWideFormatの違いはWideFormatがWideString対応なだけですが、D6のWideFormatは置き換える引数の文字数が4000文字を超えたあたりでアクセス違反の例外が起きる問題があります。

%x

整数を16進値に変換。

  ls_Hex := Format('%X', [255]);  //ls_Hex は 'FF'
  ls_Hex := Format('%x', [255]);  //ls_Hex は 'FF'

'%X' と '%x' は同じ値になります。
Xが大文字なら変換される値も大文字に、xが小文字なら小文字に変換してくれるとありがたいのですがそうはなりません。
両方大文字になります。
小文字にしたければLowerCaseでさらに変換させる必要があります。

  ls_Hex := LowerCase(Format('%x', [255]));

%f %g %n %m

浮動小数点値。

  ls_Str := Format('%f', [1000.0]);   //ls_Str は '1000.00'
  ls_Str := Format('%f', [1000.3]);   //ls_Str は '1000.30'
  ls_Str := Format('%f', [1000.555]); //ls_Str は '1000.56'
  ls_Str := Format('%f', [10/3]);     //ls_Str は '3.33'

  ls_Str := Format('%g', [1000.000]); //ls_Str は '1000'
  ls_Str := Format('%g', [1000.3]);   //ls_Str は '1000.3'
  ls_Str := Format('%g', [1000.555]); //ls_Str は '1000.555'
  ls_Str := Format('%g', [10/3]);     //ls_Str は '3.33333333333333'

  ls_Str := Format('%n', [1000.000]); //ls_Str は '1,000.00'
  ls_Str := Format('%n', [1000.3]);   //ls_Str は '1,000.30'
  ls_Str := Format('%n', [1000.555]); //ls_Str は '1,000.56'
  ls_Str := Format('%n', [10/3]);     //ls_Str は '3.33'

  ls_Str := Format('%m', [1000.000]); //ls_Str は '\1,000'
  ls_Str := Format('%m', [1000.3]);   //ls_Str は '\1,000'
  ls_Str := Format('%m', [1000.555]); //ls_Str は '\1,001'
  ls_Str := Format('%m', [10/3]);     //ls_Str は '\3'

%fのデフォルトは小数点以下2桁でそれ以下は四捨五入されます。
%gのデフォルトは小数がなければ小数はなし、小数があればそのままの値が返ります。
%nは3桁ごとの区切り文字が含まれる以外は%fに同じです。
%mは3桁ごとの区切り文字が含まれ、小数点以下は四捨五入され数値の頭に通貨記号の'\'がつきます。

Delphi 6ではWideFormatで%f、%g、%n、%mに精度指定子を指定した場合小数点以下の桁数の指定とはならず、結果の文字列の最大文字数となってしまう(恐らくバグ)ことに注意。
WideFormatを使う場合は精度指定子を使わないようにするか、精度指定子が必要な場合はWideFormatではなくFormatを使うなどの対策をとる必要があります。
(XE2ではちゃんと小数点以下の桁数の指定になっていました)

またこれらのタイプにInteger型やInt64型の変数や関数を与えるとエラーになります。

  ls_Str := Format('%f', [1024]); //エラーになる
  ls_Str := Format('%g', [1024]); //エラーになる
  ls_Str := Format('%n', [1024]); //エラーになる
  ls_Str := Format('%m', [1024]); //エラーになる



Integer型やInt64型の値を与えたい場合はExtended型やDouble型の変数を介します。

var
  lf_Temp: Extended;
  ls_Str:  String;
begin
  lf_Temp := 1024;
  ls_Str := Format('%f', [lf_Temp]);  //ls_Str は '1024.00'
  ls_Str := Format('%g', [lf_Temp]);  //ls_Str は '1024'
  ls_Str := Format('%n', [lf_Temp]);  //ls_Str は '1,024.00'
  ls_Str := Format('%m', [lf_Temp]);  //ls_Str は '\1,024'

幅指定子

最低限この幅だけは確保したい幅を指定。

  ls_Str := Format('%4s', ['ABC']);  //ls_Str は ' ABC'
  ls_Num := Format('%4d', [255]);    //ls_Num は ' 255'
  ls_Hex := Format('%4x', [255]);    //ls_Hex は '  FF'
  ls_Ovr := Format('%4d', [12345]);  //ls_Ovr は '12345'

指定した幅に満たない場合は右詰になります。
指定した幅をオーバーしても切り捨てられることはなくはみ出すだけです。

ちなみに%sに置き換えられる文字列の長さは漢字やひらがな等の2バイト文字の場合、Formatは2とカウントされますがWideFormatは1とカウントされます。
つまり置き換えられる文字列の長さはLengthの値になるということです。
その結果、例えば桁ぞろえさせようとした場合WideFormatで漢字やひらがななどの2バイト文字が混じると思ったようなレイアウトにならないことがあります。

  ls_Wide := WideFormat('%4s', ['漢字']);  //ls_Wide は '  漢字' (半角スペース2つがパディングされる)
  ls_Ansi :=     Format('%4s', ['漢字']);  //ls_Ansi は '漢字'   (スペースはパディングされない)

上の例の場合「漢字」はWideFormatでは2とカウントされるので幅指定子の4との差分のスペース2つ分がパディングされます。
Formatでは4とカウントされるのでパディングはされません。

左揃えインジケータ

要するに左詰にするオプションです。
文字列の場合で幅指定子で幅を確保しても右詰だと困る場合などに使います。

  ls_Str := Format('%-4s', ['ABC']);  //ls_Str は 'ABC '
  ls_Num := Format('%-4d', [255]);    //ls_Num は '255 '
  ls_Hex := Format('%-4x', [255]);    //ls_Hex は 'FF  '

  ls_Str := Format('%4-s', ['ABC']);  //NG エラーになる
  ls_Num := Format('%4-d', [255]);    //NG エラーになる
  ls_Hex := Format('%4-x', [255]);    //NG エラーになる

  ls_Str := Format('%-s', ['ABC']);   //ls_Str は 'ABC' ('%s'と同じ結果になる)
  ls_Num := Format('%-d', [255]);     //ls_Num は '255' ('%d'と同じ結果になる)
  ls_Hex := Format('%-x', [255]);     //ls_Hex は 'FF'  ('%x'と同じ結果になる)

左揃えインジケータは幅指定子の前につけます。
後ろにつけてしまうとエラーになります。
'%-s' のような書き方はエラーにはなりませんが '%s' と同じになるのでわざわざ指定する意味はありません。

精度指定子

浮動小数点の場合は小数点以下何桁まで表示するかの指定。
整数と16進の場合は幅指定子と同じく最低限確保する幅の指定になりますが、幅指定子と違って足りない分は左側が0で埋められます。
文字列の場合は最大の幅の指定になりますが、幅指定子と違い最大長を越えてしまう分は切り捨てられます。

ピリオド '.' のあとに表示する桁数を指定します。 

  ls_Str := Format('%.4s', ['ABC']);   //ls_Str は ' ABC'
  ls_Num := Format('%.4d', [255]);     //ls_Num は '0255'
  ls_Hex := Format('%.4x', [255]);     //ls_Hex は '00FF'
  ls_Flt := Format('%.4f', [255.3]);   //ls_Flt は '255.3000'

  //幅指定子との組み合わせ
  ls_Str := Format('%8.4s', ['ABC']);  //ls_Str は '     ABC'
  ls_Num := Format('%8.4d', [255]);    //ls_Num は '    0255'
  ls_Hex := Format('%8.4x', [255]);    //ls_Hex は '    00FF'
  ls_Flt := Format('%8.4f', [255.3]);  //ls_Flt は '255.3000'

  //幅指定子と左揃えインジケータとの組み合わせ
  //左揃えインジケータ、幅指定子、精度指定子の順に指定
  ls_Str := Format('%-8.4s', ['ABC'])); //ls_Str は 'ABC     '
  ls_Num := Format('%-8.4d', [255]));   //ls_Num は '0255    '
  ls_Hex := Format('%-8.4x', [255]));   //ls_Hex は '00FF    '
  ls_Flt := Format('%-8.4f', [255.3])); //ls_Flt は '255.3000'

  //桁数を指定しなかった場合
  ls_Str := Format('%.s', ['ABC']);  //ls_Str は ''
  ls_Num := Format('%.d', [255]);    //ls_Num は '255'
  ls_Hex := Format('%.x', [255]);    //ls_Hex は 'FF'
  ls_Flt := Format('%.f', [255.3]);  //ls_Flt は '255'

桁数を指定しなかった場合は0を指定したものとみなすようです。
文字列の場合精度指定子は最大長を指定したことになるので、桁数を指定しなかった場合は0が指定されたとみなされ結果は空文字になります。

置き換えられる文字列に2バイト文字が含まれる場合、Delphi 6などのUnicode対応でないFormatは注意が必要です。

  ls_Wide := WideFormat('%.3s検定', ['漢字']);  //ls_Wide は '漢字検定'
  ls_Ansi :=     Format('%.3s検定', ['漢字']);  //ls_Ansi は '漢詞汳'

上の例ではFormatの戻り値が文字化けします。
最大長が3(バイト)であることから「字」の1バイト目までが置き換えられる結果こうなってしまいます。
WideFormatの場合は最大長は3文字となるので文字化けの問題はありません。
とはいえ見た目は同じでも違う文字でもとりあげたように合成文字の場合は合成文字の途中で切られてしまうこともあります。

var
  ls_Str: WideString;
begin
  ls_Str  := 'ねか' + WideChar($3099) + 'い'; //WideChar($3099)は直前の「か」と合わせて「が」の合成文字となる
  ls_Wide := WideFormat('%.2s', [ls_Str]);    //ls_Wide は 'ねか'

この場合ls_Wideは「ねが」となってほしいところですがそうはならず「ねか」となります。

また%fの項でも述べたようにDelphi 6のWideFormatで浮動小数点値を変換するときにこの精度指定子を指定すると望んだ結果にならない場合があります。

以上のことから精度指定子がどうしても必要な場合、浮動小数点値の変換ではFormatを使い文字列の場合はWideFormatを使うといった使い分けが必要になります。
あるいはIntToStrやIntToFloatなどを活用して代替関数を作りこむとか。

引数インデックス指定子

これを使いこなせれば強力ですがややこしいです。
要はFormatの引数のうち[]でくくられたオープン配列中の好きな位置にある引数を指定できるということです。
引数の位置は0ベースのインデックスで指定します。
取り出したい位置の引数を0ベースのインデックスで指定し、後ろにコロン ':' をつけます。
指定するインデックスは順不同でも構いません。
連続している必要もありません。

  li_Left   :=   8;
  li_Top    :=  20;
  li_Width  := 328;
  li_Height := 274;

  //いきなりインデックス2から指定してもOK
  ls_Text := Format('W=%2:d H=%3:d', [li_Left, li_Top, li_Width, li_Height]); //ls_Textは 'W=328 H=274'

  //インデックス3を指定した後にインデックス0を指定してもOK
  ls_Text := Format('H=%3:d L=%0:d', [li_Left, li_Top, li_Width, li_Height]); //ls_Textは 'H=274 L=8'

この例の場合でいうと引数のインデックスは、li_Leftは 0、li_Topは 1、li_Widthは 2、li_Heightは 3になります。

同じ引数を何度指定してもOKです。

  li_Left   :=   8;
  li_Top    :=  20;
  li_Width  := 328;
  li_Height := 274;

  ls_Text := Format('W=%2:d 幅=%2:d', [li_Left, li_Top, li_Width, li_Height]); //ls_Textは 'W=328 幅=328'

形式指定子にインデックスを指定した場合で、次の形式指定子にインデックスを指定しなかった場合は直前に指定したインデックスの次の引数が対象になります。

  li_Left   :=   8;
  li_Top    :=  20;
  li_Width  := 328;
  li_Height := 274;

  ls_Text := Format('W=%2:d H=%d', [li_Left, li_Top, li_Width, li_Height]); //ls_Textは 'W=328 H=274'
  //最初の形式指定子 %2:d はインデックス2の li_Width に置き換えられる。
  //次の形式指定子 %d はインデックスが指定されていないので直前に指定されたインデックス2の次のインデックス3の li_Heigh に置き換えられる。

引数の数を超えたインデックスを指定するとエラーになります。

  //NG 引数の数をオーバーしたインデックス4を指定しているのでエラーになる。引数は0ベースで数える
  ls_Text := Format('%4:d', [li_Left, li_Top, li_Width, li_Height]);

  //NG '%3:d'で最後の引数を指定しているのでその次の'%d'は'%4:d'と同じ意味になるのでエラー
  ls_Text := Format('%3:d %d', [li_Left, li_Top, li_Width, li_Height]);

二つ目の例のように %3:d でインデックス3の引数が指定されると次の %d はインデックス4の引数が対象になりますが、例では引数の数は四つしかないのでエラーとなります。
インデックスは0ベースなので四つの引数がある場合はインデックスは3までになります。

アスタリスク

さらにややこしいのがヘルプにある、

インデックス指定子,幅指定子,精度指定子は "%10d" のように 10 進数の文字列で直接指定することも,"%*.*f" のようにアスタリスクで間接的に指定することもできます。アスタリスクを使う場合,引数リスト内の次の引数(整数値)が実際に使われる値です。例を次に示します。

Format('%*.*f', [8, 2, 123.456])

これは次の形式指定子と同じです。

Format('%8.2f', [123.456])

というものでしょう。
ほとんど何に使うんだ?ってな感じですが、引数の中に幅や桁数を仕込めるので使いではあります。
要点は、引数に幅指定子あるいは精度指定子と変換対象の数値なり文字列なりをセットにして並べて書くという点でしょうか。

  function Ceil(X: Extended): Integer;
  //小数点以下切り上げ
  begin
    Result := Trunc(X);
    if (Frac(X) > 0) then begin
      Inc(Result);
    end;
  end;
var
  ls_Text: AnsiString;
  li_Len:  Integer;
begin
  //桁ぞろえ、タブの展開風
  li_Len  := Ceil((Length(Caption)+1) / 8) * 8;
  ls_Text := Format('%-*s%s', [li_Len, Caption, Name]);

こんな感じでしょうか。
この場合Captionをli_Lenの幅で左詰で変換するようにしています。
'*' がli_Lenに置き換わるという認識でよいと思います。

  ls_Text := Format('%-' + IntToStr(li_Len) + 's%s', [Caption, Name]);

と同じことをちょっと簡潔に書けるということです。

  ls_Text := Format('%*d %-*s%s', [2, i, li_Len, Caption, Name]);

この場合、

  1. 最初のアスタリスクは2に置き換わり、
  2. 次のdがiに置き換わり、
  3. 二番目のアスタリスクがli_Lenに置き換わり、
  4. 次のsがCaptionに置き換わり、
  5. 次の%sがNameに置き換わる

という感じです。

ちなみに幅指定子や精度指定子にInt64を渡すと幅指定子や精度指定子が無視されたようになり、上の例ではCaptionとNameの間の空白はなくなりくっついてしまいます。
TruncのようにInt64を返す関数を使う場合はIntegerでキャストするかInteger型の変数を利用するようにしないとうまくいきません。 

var
  ls_Text: AnsiString;
  li_Len:  Inte64;
begin
  //桁ぞろえ、タブの展開風
  li_Len  := Ceil((Length(Caption) +1) / 8) * 8;
  ls_Text := Format('%-*s%s', [li_Len, Caption, Name]);
    //li_LenがInt64なので期待通りにならずCaptionとNameはくっついてしまう(エラーにはならない)

幅指定子や精度指定子がInt64型でなければならない理由はなく、Integer型の変数を使えば良いので問題はないと思いますが、関数で戻り値がInt64型のものを使う場合気をつける必要があります。

  ls_Text := Format('%-*s%s', [Trunc(100 / 3), Caption, Name]);
    //TruncがInt64を返すので期待通りにならずCaptionとNameはくっついてしまう(エラーにはならない)

この場合TruncがInt64型を返すので33の幅を指定したつもりなのにCaptionとNameはくっついてしまいます。
正しくは

  ls_Text := Format('%-*s%s', [Integer(Trunc(100 / 3)), Caption, Name]);

としなければなりません。


2010-12-20:%g %n %mの詳細の記述を追記。
2009-04-05:幅指定子と制度指定子での文字列の扱いを追記。
2009-02-01: