VC.NET ~ sprintf()からString::Format()へ

前置き

文字列の書式化と言えば、C言語ではsprintf()がお馴染みですが、.NETではString::Format()を使います。文法がsprintf()とは違うので、自分が良く使うものをまとめてみます。

本文

複合書式指定文字列

本国ではComposite Format Stringと呼ぶそうです。

文字列リテラルの中に、{}で囲んだプレースホルダを埋め込みます。プレースホルダは{INDEX,ALIGNMENT:FORMAT}という形式で書きます。INDEXは必須で、ALIGNMENTとFORMATは省略可能です。例を示します。

String^ msg = String::Format(
  "Simple   :{0}--\r\n"
  "Alignment:{1,-5}--\r\n"
  "Format   :{2:X}--\r\n"
  "Both     :{3,5:C}--",
  100,           // これがINDEX=0のプレースホルダの位置に入る。
  100,           // INDEX=1
  100,           // INDEX=2
  100);          // INDEX=3
Console::WriteLine(msg);

結果は…

Simple   :100--
Alignment:100  --
Format   :64--
Both     : \100--

INDEXについては説明不要でしょう。ALIGNMENTには幅を数値で指定します。正なら右揃え、負値なら左揃えになります。ここまでが序の口。書式化を大きく左右するのはFORMATに指定するフォーマット文字列です。FORMATを省略した場合は、"G"というフォーマット文字列を指定したのと同じ扱いになります。"G"の意味については後述します。

{}でプレースホルダを指定するので、文字通り '{' や '}' を出力するにはエスケープする必要があります。

msg = String::Format("Between {{ and }}.");
Console::WriteLine(msg);                         // "Between { and }."

ところで、複合書式指定文字列で書式化できるデータ型は、数値か日時(DateTime)か列挙型(enum)に限られます。この後で説明するフォーマット文字列は、これらの型のToString()メソッドの引数としても使うことができます。例えば、数値用のフォーマット文字列に"D"というのがありますが、これは、String::Format()にも、Int32::ToString()にも使うことができます。

Int32 i = 1250;
Console::WriteLine(String::Format("{0:D}", i));  // "1250"
Console::WriteLine(i.ToString("D"));             // "1250"

この後のサンプルコードではToString()を使いますが、同じものが、String::Format()のFORMAT部分にも指定できると考えて下さい。

ちなみに、型が数値とDateTimeとenumに限られているので、sprintf()の"%s"に相当するものはありません。無くても困らないけど、あった方が便利だと思うのですが…。

数値用のフォーマット文字列

数値のフォーマット文字列には、標準形式とカスタム形式があります。標準形式の場合、アルファベット1文字で指定しますが、オプションで数値を付けることができます。カスタム形式は……、いろいろです。

Int32 i = 1250;
Console::WriteLine(i.ToString("D"));                    // 1250
Console::WriteLine(i.ToString("D8"));                   // 00001250
Console::WriteLine(i.ToString("C"));                    // \1,250
Console::WriteLine(i.ToString("C2"));                   // \1,250.00
Console::WriteLine(i.ToString("X"));                    // 4E2
Console::WriteLine(i.ToString("X8"));                   // 000004E2
Console::WriteLine(i.ToString("x"));                    // 4e2
Console::WriteLine(i.ToString("N"));                    // 1,250.00
Console::WriteLine(i.ToString("N0"));                   // 1,250
Console::WriteLine(i.ToString("G"));                    // 1250
Console::WriteLine(i.ToString("g"));                    // 1250

Double f = 125.99;
Console::WriteLine(f.ToString("F"));                    // 125.99
Console::WriteLine(f.ToString("F3"));                   // 125.990
Console::WriteLine(f.ToString("E"));                    // 1.259900E+002
Console::WriteLine(f.ToString("E4"));                   // 1.2599E+002
Console::WriteLine(f.ToString("G"));                    // 125.99
Console::WriteLine(f.ToString("g"));                    // 125.99

Console::WriteLine(f.ToString("00000.000"));            // 00125.990
Console::WriteLine(f.ToString("#.###"));                // 125.99
Console::WriteLine(f.ToString("#.##"));                 // 125.99
Console::WriteLine(f.ToString("#.#"));                  // 126
Console::WriteLine(f.ToString("#0000.000"));            // 0125.990

Console::WriteLine(i.ToString("\\# is number sign"));   // # is number sign

String^ plusMinusZero = "  #;△#;  -";
Console::WriteLine(Int32(1).ToString(plusMinusZero));   //   1
Console::WriteLine(Int32(-8).ToString(plusMinusZero));  // △8
Console::WriteLine(Int32(0).ToString(plusMinusZero));   //   -

以下、補足です。

  • 標準形式に数値を付けると、整数部や小数部の桁数を制御できます
  • "G"はGenericのGから来てるようですが、数値に応じた一般的な書式化が行われます
  • "G"か"g"かによって、指数表記されるような数値の場合に、Eかeが出力されます
  • カスタム形式では、リテラルを混在させることが可能ですが、'0'や'#'はエスケープする必要があります
  • エスケープするには'\'を使いますが、文字列リテラルで'\'を使うことになるので"\\"とする必要があります
  • 最後の4行は、セクション区切りを使った記法です
  • セクション区切りを使うと、値が正の場合と負値の場合と0の場合とで、書式を自動切り替えさせることができます

カスタム形式にリテラルを混在させると、負値の場合に残念な結果になるので注意しましょう。

String^ fmt = "Value is #";
Console::WriteLine(Int32(12).ToString(fmt));   // Value is 12
Console::WriteLine(Int32(-12).ToString(fmt));  // -Value is 12

日時(DateTime)用のフォーマット文字列

DateTimeにも標準形式とカスタム形式があります。標準形式の場合、アルファベット1文字で指定します。カスタム形式でもアルファベットを使います。

DateTime dt = DateTime(2008, 1, 2, 18, 5, 6, 99);
Console::WriteLine(dt.ToString("D"));            // 2008年1月2日
Console::WriteLine(dt.ToString("d"));            // 2008/01/02
Console::WriteLine(dt.ToString("T"));            // 18:05:06
Console::WriteLine(dt.ToString("t"));            // 18:05

Console::WriteLine(dt.ToString("F"));            // 2008年1月2日 18:05:06
Console::WriteLine(dt.ToString("f"));            // 2008年1月2日 18:05
Console::WriteLine(dt.ToString("G"));            // 2008/01/02 18:05:06
Console::WriteLine(dt.ToString("g"));            // 2008/01/02 18:05
Console::WriteLine(dt.ToString("R"));            // Wed, 02 Jan 2008 18:05:06 GMT
Console::WriteLine(dt.ToString("s"));            // 2008-01-02T18:05:06
Console::WriteLine(dt.ToString("U"));            // 2008年1月2日 9:05:06
Console::WriteLine(dt.ToString("u"));            // 2008-01-02 18:05:06Z

Console::WriteLine(dt.ToString("%y"));           // 8
Console::WriteLine(dt.ToString("yy"));           // 08
Console::WriteLine(dt.ToString("yyy"));          // 2008
Console::WriteLine(dt.ToString("yyyy"));         // 2008

Console::WriteLine(dt.ToString("%M"));           // 1
Console::WriteLine(dt.ToString("MM"));           // 01
Console::WriteLine(dt.ToString("MMM"));          // 1
Console::WriteLine(dt.ToString("MMMM"));         // 1月

Console::WriteLine(dt.ToString("%d"));           // 2
Console::WriteLine(dt.ToString("dd"));           // 02
Console::WriteLine(dt.ToString("ddd"));          // 水
Console::WriteLine(dt.ToString("dddd"));         // 水曜日

Console::WriteLine(dt.ToString("%H"));           // 18
Console::WriteLine(dt.ToString("HH"));           // 18

Console::WriteLine(dt.ToString("%h"));           // 6
Console::WriteLine(dt.ToString("hh"));           // 06

Console::WriteLine(dt.ToString("%m"));           // 5
Console::WriteLine(dt.ToString("mm"));           // 05

Console::WriteLine(dt.ToString("%s"));           // 6
Console::WriteLine(dt.ToString("ss"));           // 06

Console::WriteLine(dt.ToString("%f"));           // 0
Console::WriteLine(dt.ToString("ff"));           // 09
Console::WriteLine(dt.ToString("fff"));          // 099
Console::WriteLine(dt.ToString("ffff"));         // 0990

Console::WriteLine(dt.ToString("\\y \\for \\year"));  // y for year

以下、補足です。

  • カスタム形式でアルファベット1文字の場合は、標準形式と区別するために%を頭に付けます。
  • カスタム形式では、リテラルを混在させることが可能ですが、'y'や'M'などの特殊な意味を持つ文字はエスケープする必要があります
  • エスケープするには'\'を使いますが、文字列リテラルで'\'を使うことになるので"\\"とする必要があります

列挙型(enum)用のフォーマット文字列

enumの場合、標準とカスタムの区別はありません。"F"、"G"、"D"、"X/x"の4つだけです。"D"は10進数、"X/x"は16進数ですね。

例として2つの列挙型を定義してみました。

enum class Color {
  Red = 1,
  Green = 16,
  Blue = 256
};

[Flags] 
enum class Status {
  L2Error = 1,
  L3Error = 2,
  L4Error = 4,
  L5Error = 8
};

StatusにFlagsアトリビュートが付いているところに注意して下さい。

Console::WriteLine(Color::Blue.ToString("F"));   // Blue
Console::WriteLine(Color::Blue.ToString("G"));   // Blue
Console::WriteLine(Color::Blue.ToString("D"));   // 2
Console::WriteLine(Color::Blue.ToString("X"));   // 00000002

Status st = Status::L2Error | Status::L4Error;
Console::WriteLine(st.ToString("F"));            // L2Error, L4Error
Console::WriteLine(st.ToString("G"));            // L2Error, L4Error
Console::WriteLine(st.ToString("D"));            // 5
Console::WriteLine(st.ToString("X"));            // 00000005

この例では"F"と"G"の違いが分かりませんが、以下のような汚いコードでは違いが出るようです。

Color c = static_cast<Color>(257);
Console::WriteLine(c.ToString("F"));             // Red, Blue
Console::WriteLine(c.ToString("G"));             // 257
Last modified:2012/01/28 12:12:01
Keyword(s):
References:[.NETアプリ開発] [VC.NET ~ atoi/itoa系はConvertクラスにお任せ]
This page is frozen.