C# string.Format の使い方徹底解説!具体的なサンプルコード付き


C# string.Format の使い方徹底解説!具体的なサンプルコード付き

はじめに

C#で文字列を整形して表示したり、特定の形式に変換したりする場合、string.Formatメソッドは非常に強力で柔軟なツールです。複数の変数やリテラル文字列を組み合わせて、読みやすい、あるいは特定の要件を満たす文字列を作成する際に広く利用されています。

このメソッドは、指定されたフォーマット文字列内のプレースホルダーを、対応するオブジェクトの値に置き換えることで機能します。特に数値、日付、時刻などのデータを特定の文化(カルチャー)に合わせた形式で表示する場合や、出力の幅や位置を揃えたい場合に威力を発揮します。

現代のC#では、文字列補間($""構文)が導入され、より簡潔に同様の処理を記述できるようになりました。しかし、string.Formatは後方互換性や、特定のシナリオ(例えば、フォーマット文字列が動的に生成される場合や、IFormatProviderを指定して明示的にカルチャーを制御する場合)で依然として重要な役割を果たします。また、文字列補間も内部的にはstring.Formatや同様のメカニズムを利用しているため、string.Formatの仕組みや書式指定子の使い方を理解することは、C#における文字列処理の基礎として非常に価値があります。

本記事では、string.Formatメソッドの基本的な使い方から、数値、日付、時刻、列挙型などの書式指定子、位置合わせ、カルチャーの影響、さらには現代的な文字列補間との比較まで、その全てを網羅的に解説します。豊富なサンプルコードと共に、string.Formatを自在に操るための知識を習得しましょう。

string.Format の基本構文

string.Formatメソッドは、静的メソッドとしてstringクラスに定義されています。最も一般的なオーバーロードは以下の形式です。

csharp
public static string Format(string format, params object[] args);

  • format: 整形のためのフォーマット文字列です。この文字列の中に、置換されるプレースホルダー(書式項目)を含めます。
  • args: フォーマット文字列中のプレースホルダーに対応する値の配列です。params object[]であるため、可変長引数として直接値を渡すことができます。

フォーマット文字列内のプレースホルダーは、 {index[,alignment][:formatString]} の形式で記述されます。

  • index: args配列内の、対応する引数のゼロベースのインデックスを指定します。
  • alignment (省略可能): 値の幅と位置合わせを指定します。正の整数は右寄せ、負の整数は左寄せを指定します。
  • formatString (省略可能): 値の表示形式を指定する書式指定子です。数値、日付、列挙型など、データの型によって利用できる書式指定子が異なります。

引数の書式 {index}

最も基本的な使い方は、indexだけを指定する方法です。これは、指定されたインデックスの引数のToString()メソッドを呼び出した結果を挿入します。

“`csharp
int age = 30;
string name = “山田”;

// 0番目の引数(age)と1番目の引数(name)を挿入
string result = string.Format(“私の名前は{1}で、年齢は{0}歳です。”, age, name);
Console.WriteLine(result); // 出力: 私の名前は山田で、年齢は30歳です。
“`

この例では、{0}age(0番目の引数)、{1}name(1番目の引数)にそれぞれ置き換わっています。引数の順序はフォーマット文字列中のインデックスによって決まるため、引数リストでの順序とフォーマット文字列中のインデックスは必ずしも一致させる必要はありません。ただし、可読性の観点からは一致させるのが一般的です。

複数の引数

複数の引数を扱う場合も同様に、それぞれの引数に対応するインデックスをフォーマット文字列中に記述します。

“`csharp
string product = “ノートPC”;
decimal price = 98500m;
int quantity = 2;

string orderSummary = string.Format(“商品: {0}, 単価: {1}, 数量: {2}”, product, price, quantity);
Console.WriteLine(orderSummary); // 出力: 商品: ノートPC, 単価: 98500, 数量: 2
“`

同じ引数を複数回使用することも可能です。

csharp
string item = "りんご";
string format = "本日のおすすめは{0}です。新鮮な{0}をどうぞ!";
string recommendation = string.Format(format, item);
Console.WriteLine(recommendation); // 出力: 本日のおすすめはりんごです。新鮮なりんごをどうぞ!

文字列リテラルとの組み合わせ

フォーマット文字列には、プレースホルダーだけでなく、通常の文字列リテラルや句読点なども自由に含めることができます。

“`csharp
DateTime now = DateTime.Now;
string greeting = “こんにちは”;

string message = string.Format(“{0}、現在の時刻は {1} です。”, greeting, now);
Console.WriteLine(message); // 出力例: こんにちは、現在の時刻は 2023/10/27 10:30:00 です。(実際の日時は実行時によって異なります)
“`

書式指定子の種類と使い方

string.Formatの強力な機能の一つは、formatString部分で指定する書式指定子です。これにより、数値、日付、時刻などのデータを、特定の形式で表示させることができます。書式指定子には、標準書式指定子とカスタム書式指定子の2種類があります。

数値書式指定子

数値型(int, double, decimalなど)の値を整形するために使用します。

標準数値書式指定子

単一の文字で定義された、よく使われる書式です。必要に応じて精度指定子(桁数など)を末尾に付けることができます。 {index:formatString} の形式で使用します。

  • C (通貨): 現在のカルチャーの通貨形式で数値を書式設定します。精度指定子は小数点以下の桁数を指定します。

    csharp
    decimal amount = 12345.67m;
    string currency = string.Format("価格: {0:C}", amount);
    Console.WriteLine(currency); // 出力例 (ja-JP カルチャー): 価格: ¥12,346
    // 精度指定子付き
    string currencyPrecise = string.Format("価格: {0:C2}", amount);
    Console.WriteLine(currencyPrecise); // 出力例 (ja-JP カルチャー): 価格: ¥12,345.67

    精度指定子がない場合、デフォルトの通貨小数点桁数が使用されます。

  • D (10進数): 符号付きの整数値を10進数文字列に変換します。精度指定子は結果の文字列の最小桁数を指定します。必要に応じて先頭にゼロが埋められます。数値型が整数型(int, longなど)でない場合は例外が発生します。

    csharp
    int id = 123;
    string decimalFormat = string.Format("ID: {0:D5}", id);
    Console.WriteLine(decimalFormat); // 出力: ID: 00123

  • E (指数): 数値を指数形式に書式設定します。精度指定子は小数点以下の桁数を指定します。デフォルトは6桁です。

    csharp
    double value = 12345.6789;
    string exponential = string.Format("値: {0:E}", value);
    Console.WriteLine(exponential); // 出力例: 値: 1.234568E+004
    // 精度指定子付き
    string exponentialPrecise = string.Format("値: {0:E2}", value);
    Console.WriteLine(exponentialPrecise); // 出力例: 値: 1.23E+004

  • F (固定小数点): 数値を小数点付きの固定形式で書式設定します。精度指定子は小数点以下の桁数を指定します。デフォルトは現在のカルチャーのデフォルト小数点桁数です。

    csharp
    double temperature = 25.5;
    string fixedPoint = string.Format("温度: {0:F1}", temperature);
    Console.WriteLine(fixedPoint); // 出力例: 温度: 25.5
    double pi = Math.PI;
    string piFixed = string.Format("π: {0:F4}", pi);
    Console.WriteLine(piFixed); // 出力例: π: 3.1416

  • G (一般): 数値を固定小数点形式または指数形式のどちらかコンパクトな方で書式設定します。精度指定子によって、表示される有効桁数が決まります。

    csharp
    double small = 0.00012345;
    double large = 12345000;
    string generalSmall = string.Format("Small: {0:G}", small);
    string generalLarge = string.Format("Large: {0:G}", large);
    Console.WriteLine(generalSmall); // 出力例: Small: 0.00012345
    Console.WriteLine(generalLarge); // 出力例: Large: 1.2345E+07
    // 精度指定子付き
    string generalPrecise = string.Format("Value: {0:G3}", 123456789);
    Console.WriteLine(generalPrecise); // 出力例: Value: 1.23E+08

  • N (数値): 数値を小数点とグループ区切り文字付きで書式設定します。精度指定子は小数点以下の桁数を指定します。デフォルトは現在のカルチャーのデフォルト小数点桁数です。

    csharp
    int population = 1234567;
    string numberFormat = string.Format("人口: {0:N0}", population);
    Console.WriteLine(numberFormat); // 出力例 (ja-JP カルチャー): 人口: 1,234,567
    double debt = -987654.32;
    string negativeNumber = string.Format("負の値: {0:N2}", debt);
    Console.WriteLine(negativeNumber); // 出力例 (ja-JP カルチャー): 負の値: -987,654.32

  • P (パーセント): 数値を100倍し、パーセント記号付きで書式設定します。精度指定子は小数点以下の桁数を指定します。

    csharp
    double rate = 0.125;
    string percent = string.Format("利率: {0:P1}", rate);
    Console.WriteLine(percent); // 出力例 (ja-JP カルチャー): 利率: 12.5 %

  • R (ラウンドトリップ): 数値を、その値が別の数値に変換された後でも元の値と等しくなるような文字列に変換します。これは、値を文字列として保存し、後で正確に復元する場合に使用します。精度指定子は無視されます。

    csharp
    double dbl = 1.2345678901234567;
    string roundTrip = string.Format("Value: {0:R}", dbl);
    Console.WriteLine(roundTrip); // 出力例: Value: 1.2345678901234567
    float flt = 1.234567F;
    string roundTripFloat = string.Format("Value: {0:R}", flt);
    Console.WriteLine(roundTripFloat); // 出力例: Value: 1.234567

  • X (16進数): 整数値を16進数文字列に変換します。精度指定子は結果の文字列の最小桁数を指定します。大文字または小文字のXを使用することで、出力される16進数のアルファベット(A-F)を大文字にするか小文字にするかを制御できます。数値型が整数型でない場合は例外が発生します。

    csharp
    int hexValue = 255;
    string hexLower = string.Format("16進数 (小文字): {0:x}", hexValue);
    string hexUpper = string.Format("16進数 (大文字): {0:X}", hexValue);
    string hexPadded = string.Format("16進数 (パディング): {0:X8}", hexValue);
    Console.WriteLine(hexLower); // 出力: 16進数 (小文字): ff
    Console.WriteLine(hexUpper); // 出力: 16進数 (大文字): FF
    Console.WriteLine(hexPadded); // 出力: 16進数 (パディング): 000000FF

カスタム数値書式指定子

標準書式指定子よりも柔軟な書式を定義したい場合に、複数の文字や記号を組み合わせて書式文字列を作成します。

文字 説明 例 (値 12345.6789, ja-JP カルチャー)
0 ゼロプレースホルダー。対応する桁に数値が存在しない場合、ゼロが表示されます。小数点以下の桁数も指定できます。 {0:000000} -> 012346
# 桁プレースホルダー。対応する桁に数値が存在する場合にのみ表示されます。ゼロが表示されるのを避けたい場合に使用します。 {0:######} -> 12346
. 小数点プレースホルダー。現在のカルチャーの小数点記号に置換されます。書式文字列中に一度だけ使用できます。 {0:0.00} -> 12345.68
, グループ区切りプレースホルダー。現在のカルチャーのグループ区切り記号に置換されます。整数の左側または小数点以下に置きます。 {0:#,###} -> 12,346
% パーセントプレースホルダー。数値を100倍し、現在のカルチャーのパーセント記号に置換されます。 {0:#.##%} (値 0.123) -> 12.3%
E0, E+0, E-0 指数形式を指定します。 {0:0.00E+0} -> 1.23E+004
\ エスケープ文字。直後の文字をリテラルとして表示します。 {0:\#0} -> #12346
' " リテラル文字列デリミタ。シングルクォートまたはダブルクォート内の文字をリテラルとして表示します。 {0:'Value is '0} -> Value is 12346
; セクション区切り文字。正の値、負の値、ゼロに対する書式を指定します。 {正の値の書式;負の値の書式;ゼロの書式} {0:#,##0.00;(#,##0.00);ゼロ} (値 -12345.67) -> (12,345.67)

カスタム書式指定子の例:

“`csharp
double value1 = 12345.6789;
double value2 = -9876.5;
int value3 = 0;
double value4 = 0.0123;

// 小数点以下2桁、カンマ区切り
string custom1 = string.Format(“値1: {0:#,##0.00}”, value1);
Console.WriteLine(custom1); // 出力例 (ja-JP カルチャー): 値1: 12,345.68

// 負の値は括弧で囲む
string custom2 = string.Format(“値2: {0:#,##0.00;(#,##0.00)}”, value2);
Console.WriteLine(custom2); // 出力例 (ja-JP カルチャー): 値2: (9,876.50)

// ゼロは特定の文字列を表示
string custom3 = string.Format(“値3: {0:#,##0.00;(#,##0.00);在庫なし}”, value3);
Console.WriteLine(custom3); // 出力: 値3: 在庫なし

// パーセント表示 (小数点以下2桁)
string custom4 = string.Format(“値4: {0:0.00%}”, value4);
Console.WriteLine(custom4); // 出力例 (ja-JP カルチャー): 値4: 1.23%

// 先頭にドル記号を付けて、小数点以下1桁
string custom5 = string.Format(“値5: {0:’$’#,##0.0}”, value1);
Console.WriteLine(custom5); // 出力例 (ja-JP カルチャー): 値5: $12,345.7
“`

カスタム書式指定子は非常に柔軟ですが、その分複雑になりがちです。意図した通りに表示されるか、十分にテストすることが重要です。

日付と時刻の書式指定子

DateTimeまたはDateTimeOffset型の値を整形するために使用します。

標準日付と時刻の書式指定子

単一の文字で定義された、よく使われる書式です。カルチャーによって出力形式が変わります。

文字 名前 説明 例 (ja-JP カルチャー, DateTime(2023, 10, 27, 14, 30, 59))
d 短い日付パターン yyyy/MM/dd のような形式。 2023/10/27
D 長い日付パターン yyyy年M月d日 のような形式。 2023年10月27日
f フル日付/時刻パターン (短い) Dパターンと tパターンを組み合わせたもの。 2023年10月27日 14:30
F フル日付/時刻パターン (長い) Dパターンと Tパターンを組み合わせたもの。 2023年10月27日 14時30分59秒
g 一般日付/時刻パターン (短い) dパターンと tパターンを組み合わせたもの。 2023/10/27 14:30
G 一般日付/時刻パターン (長い) dパターンと Tパターンを組み合わせたもの。 2023/10/27 14:30:59
M or m 月日パターン MM月dd日 のような形式。 10月27日
O or o ラウンドトリップパターン ISO 8601 形式で、タイムゾーン情報も含む。正確な日付/時刻の保存と復元に適しています。 2023-10-27T14:30:59.0000000+09:00
R or r RFC1123 パターン RFC 1123 形式 (インターネット標準)。世界標準時で表示されます。 Fri, 27 Oct 2023 05:30:59 GMT
s 並べ替え可能な日付/時刻パターン ISO 8601 形式。日付と時刻のみ。並べ替えに便利です。 2023-10-27T14:30:59
t 短い時刻パターン H:mm のような形式。 14:30
T 長い時刻パターン H時mm分ss秒 のような形式。 14時30分59秒
U or u ユニバーサル並べ替え可能パターン 世界標準時でのyyyy-MM-dd HH:mm:ssZ 形式。 2023-10-27 05:30:59Z
Y or y 年月パターン yyyy年M月 のような形式。 2023年10月

標準日付と時刻の書式指定子の例:

“`csharp
DateTime dt = new DateTime(2023, 10, 27, 14, 30, 59);

Console.WriteLine(string.Format(“短い日付: {0:d}”, dt)); // 出力例: 短い日付: 2023/10/27
Console.WriteLine(string.Format(“長い日付: {0:D}”, dt)); // 出力例: 長い日付: 2023年10月27日
Console.WriteLine(string.Format(“短い時間: {0:t}”, dt)); // 出力例: 短い時間: 14:30
Console.WriteLine(string.Format(“長い時間: {0:T}”, dt)); // 出力例: 長い時間: 14時30分59秒
Console.WriteLine(string.Format(“一般 (短い): {0:g}”, dt)); // 出力例: 一般 (短い): 2023/10/27 14:30
Console.WriteLine(string.Format(“一般 (長い): {0:G}”, dt)); // 出力例: 一般 (長い): 2023/10/27 14:30:59
Console.WriteLine(string.Format(“ラウンドトリップ: {0:o}”, dt)); // 出力例: ラウンドトリップ: 2023-10-27T14:30:59.0000000
Console.WriteLine(string.Format(“RFC1123: {0:r}”, dt)); // 出力例: RFC1123: Fri, 27 Oct 2023 05:30:59 GMT (UTCへの変換含む)
“`

カスタム日付と時刻の書式指定子

標準書式指定子よりも詳細な表示形式を定義したい場合に、複数の文字や記号を組み合わせて書式文字列を作成します。

文字 説明 例 (DateTime(2023, 10, 08, 07, 05, 01, 123))
h 時 (12時間形式、先行ゼロなし) 7
hh 時 (12時間形式、先行ゼロあり) 07
H 時 (24時間形式、先行ゼロなし) 7
HH 時 (24時間形式、先行ゼロあり) 07
m 分 (先行ゼロなし) 5
mm 分 (先行ゼロあり) 05
s 秒 (先行ゼロなし) 1
ss 秒 (先行ゼロあり) 01
f 秒の小数点以下の桁数 (1桁) 1
ff 秒の小数点以下の桁数 (2桁) 12
fff 秒の小数点以下の桁数 (3桁) 123
F 秒の小数点以下の桁数 (末尾のゼロを除く) 1 (値 0.1) / 12 (値 0.12) / 123 (値 0.123)
FF, FFF, … 同上 (末尾のゼロを除く、指定桁数まで) 12 (値 0.123F) / 123 (値 0.123FFF)
t 午前/午後指定子の最初の文字 (ja-JP)
tt 午前/午後指定子 午前 (ja-JP)
d 日 (先行ゼロなし) 8
dd 日 (先行ゼロあり) 08
ddd 曜日略称 (ja-JP)
dddd 曜日正式名称 日曜日 (ja-JP)
M 月 (先行ゼロなし) 10
MM 月 (先行ゼロあり) 10
MMM 月略称 10月 (ja-JP)
MMMM 月正式名称 10月 (ja-JP, 英語では October)
y 年 (2桁、先行ゼロなし) 23 (2023)
yy 年 (2桁、先行ゼロあり) 23 (2023)
yyy 年 (4桁以上、先行ゼロなし) 2023
yyyy 年 (4桁以上、先行ゼロあり) 2023
z タイムゾーン オフセット (時、先行ゼロなし) -5 (UTC-05:00)
zz タイムゾーン オフセット (時、先行ゼロあり) -05 (UTC-05:00)
zzz タイムゾーン オフセット (時と分、先行ゼロあり) -05:00 (UTC-05:00)
: 時刻区切り文字 (現在のカルチャー) :
/ 日付区切り文字 (現在のカルチャー) /
' " リテラル文字列デリミタ 'Year 'yyyy -> Year 2023
\ エスケープ文字 dddd\, MMMM dd\, yyyy -> 日曜日, 10月 08, 2023

カスタム日付と時刻の書式指定子の例:

“`csharp
DateTime dt = new DateTime(2023, 10, 27, 14, 30, 59, 123); // 年,月,日,時,分,秒,ミリ秒

Console.WriteLine(string.Format(“カスタム1: {0:yyyy/MM/dd HH:mm:ss.fff}”, dt));
// 出力: カスタム1: 2023/10/27 14:30:59.123

Console.WriteLine(string.Format(“カスタム2: {0:dddd, MMMM dd, yyyy}”, dt));
// 出力例 (ja-JP カルチャー): カスタム2: 金曜日, 10月 27, 2023

Console.WriteLine(string.Format(“カスタム3: {0:yyyy’年’MM’月’dd’日’ ‘午後’h’時’m’分’}”, dt));
// 出力例 (ja-JP カルチャー): カスタム3: 2023年10月27日 午後2時30分 (14時 -> 12時間表記の午後2時)

Console.WriteLine(string.Format(“カスタム4: {0:MM/dd/yyyy hh:mm:ss tt}”, dt));
// 出力例 (ja-JP カルチャー): カスタム4: 10/27/2023 02:30:59 午後 (ttはPM/AMに対応)

DateTimeOffset dto = new DateTimeOffset(2023, 10, 27, 14, 30, 59, TimeSpan.FromHours(9)); // 日本標準時 +09:00
Console.WriteLine(string.Format(“カスタム5 (Offset): {0:yyyy-MM-dd HH:mm:ss zzz}”, dto));
// 出力: カスタム5 (Offset): 2023-10-27 14:30:59 +09:00
“`

列挙体の書式指定子

列挙型(enum)の値は、以下の標準書式指定子を使って表示できます。

文字 説明 例 (DayOfWeek.Friday)
G 列挙体名 (既定)。数値が定義されていない場合は数値。 Friday
D 数値表現 5
X 16進数表現 00000005
F フラグ属性が付いた列挙型の場合、各フラグの名前をカンマ区切りで表示。 Friday, Saturday (Flags属性の例)

列挙体の書式指定子の例:

“`csharp
DayOfWeek today = DayOfWeek.Friday;

Console.WriteLine(string.Format(“既定 (名前): {0:G}”, today)); // 出力: 既定 (名前): Friday
Console.WriteLine(string.Format(“数値: {0:D}”, today)); // 出力: 数値: 5
Console.WriteLine(string.Format(“16進数: {0:X}”, today)); // 出力: 16進数: 00000005

[Flags]
enum FilePermissions
{
None = 0,
Read = 1,
Write = 2,
Execute = 4
}

FilePermissions permissions = FilePermissions.Read | FilePermissions.Write;
Console.WriteLine(string.Format(“フラグ付き (名前): {0:F}”, permissions)); // 出力: フラグ付き (名前): Read, Write
“`

位置合わせと幅指定

string.Formatでは、各項目の表示される幅と位置合わせを指定できます。これはテーブル形式の出力を整形する際などに便利です。

構文は {index,alignment} または {index,alignment:formatString} です。

  • alignment に正の整数を指定すると、その幅内で右寄せになります。
  • alignment に負の整数を指定すると、その幅内で左寄せになります。

指定された幅が値の文字数よりも小さい場合、位置合わせ指定は無視され、値全体が表示されます。

例:

“`csharp
string name1 = “アリス”;
string name2 = “ボブ”;
string name3 = “チャーリー”;
int score1 = 85;
int score2 = 92;
int score3 = 78;

Console.WriteLine(string.Format(“{0,-10} {1,5}”, “名前”, “スコア”)); // ヘッダー
Console.WriteLine(string.Format(“{0,-10} {1,5}”, “———-“, “—–“)); // 区切り線
Console.WriteLine(string.Format(“{0,-10} {1,5}”, name1, score1));
Console.WriteLine(string.Format(“{0,-10} {1,5}”, name2, score2));
Console.WriteLine(string.Format(“{0,-10} {1,5}”, name3, score3));
“`

このコードの出力は以下のようになります。

“`
名前 スコア


アリス 85
ボブ 92
チャーリー 78
“`

{0,-10} は、「0番目の引数(名前)を10文字幅で左寄せ」を意味します。
{1,5} は、「1番目の引数(スコア)を5文字幅で右寄せ」を意味します。

書式指定子と組み合わせる場合は、位置合わせの後にコロンを付けて書式指定子を記述します。

“`csharp
decimal price = 123.45m;
double percentage = 0.75;

Console.WriteLine(string.Format(“{0,-10:C} {1,8:P1}”, “価格:”, price));
Console.WriteLine(string.Format(“{0,-10} {1,8:P1}”, “割引率:”, percentage));
“`

出力例 (ja-JP カルチャー):

価格: ¥123.45 75.0 %
割引率: 75.0 %

この例では、通貨は10文字幅で左寄せ({0,-10:C})、パーセントは8文字幅で右寄せ({1,8:P1})で表示されています。

カルチャーによる書式設定

数値、日付、時刻の書式設定は、実行環境の現在のカルチャー(地域と言語の設定)に影響されます。例えば、小数点記号、グループ区切り文字、日付の順序、通貨記号などはカルチャーによって異なります。

string.Formatメソッドには、明示的にカルチャーを指定できるオーバーロードがあります。

csharp
public static string Format(IFormatProvider provider, string format, params object[] args);

IFormatProviderインタフェースを実装するオブジェクトとして、System.Globalization.CultureInfoクラスのインスタンスがよく使用されます。CultureInfoオブジェクトは、特定のカルチャーに関連する書式設定情報(数値形式、日付/時刻形式、大文字小文字変換規則など)を提供します。

CultureInfoクラスの静的プロパティとして、以下のものがよく使われます。

  • CultureInfo.CurrentCulture: 現在のスレッドで使用されているカルチャー。デフォルトでstring.Formatが使用します。
  • CultureInfo.InvariantCulture: インバリアントカルチャー。英語(米国)に基づいたカルチャーで、カルチャーに依存しない一貫した書式設定を提供します。ファイルI/Oやデータ交換など、カルチャーに依存しない書式が求められる場面で便利です。

特定のカルチャーを指定するには、new CultureInfo("カルチャー名")のようにインスタンスを作成します。例えば、米国英語カルチャーは "en-US"、フランス語(フランス)カルチャーは "fr-FR"、ドイツ語(ドイツ)カルチャーは "de-DE" といった名称で指定します。

カルチャー指定の例:

“`csharp
double value = 12345.67;

// 1. 現在のカルチャー (例: ja-JP)
string currentCultureFormat = string.Format(“Current: {0:N2}”, value);
Console.WriteLine(currentCultureFormat); // 出力例: Current: 12,345.67

// 2. インバリアントカルチャー
CultureInfo invariantCulture = CultureInfo.InvariantCulture;
string invariantFormat = string.Format(invariantCulture, “Invariant: {0:N2}”, value);
Console.WriteLine(invariantFormat); // 出力: Invariant: 12,345.67 (小数点、カンマとも en-US 形式)

// 3. 特定のカルチャー (例: en-US – 米国英語)
CultureInfo usCulture = new CultureInfo(“en-US”);
string usFormat = string.Format(usCulture, “en-US: {0:C2}”, value);
Console.WriteLine(usFormat); // 出力: en-US: $12,345.67

// 4. 特定のカルチャー (例: fr-FR – フランス語(フランス))
CultureInfo frCulture = new CultureInfo(“fr-FR”);
string frFormat = string.Format(frCulture, “fr-FR: {0:C2}”, value);
Console.WriteLine(frFormat); // 出力例: fr-FR: 12 345,67 € (ユーロ記号は後ろ、スペースが桁区切り、コンマが小数点)

// 5. 日付のカルチャー指定
DateTime dt = new DateTime(2023, 10, 27);
Console.WriteLine(string.Format(usCulture, “en-US Date: {0:D}”, dt)); // 出力例: en-US Date: Friday, October 27, 2023
Console.WriteLine(string.Format(frCulture, “fr-FR Date: {0:D}”, dt)); // 出力例: fr-FR Date: vendredi 27 octobre 2023
“`

このように、IFormatProvider(通常はCultureInfo)を指定することで、プログラムの実行環境によらず、意図した通りのカルチャーで書式設定を行うことができます。これは、国際化(I18N)対応において非常に重要な機能です。

その他の考慮事項と高度な使い方

中括弧 { } のエスケープ

フォーマット文字列内でリテラルの { または } 文字を表示したい場合は、それらを二重に記述してエスケープする必要があります。

“`csharp
string result = string.Format(“括弧を表示: {{ これはリテラル }}.”);
Console.WriteLine(result); // 出力: 括弧を表示: { これはリテラル }.

// 引数と組み合わせる場合
string item = “サンプル”;
string combined = string.Format(“アイテム: {{ {0} }}”, item);
Console.WriteLine(combined); // 出力: アイテム: { サンプル }
“`

string.Format と 文字列補間 ($””) の比較

C# 6.0で導入された文字列補間(Interpolated String)は、string.Formatの代替として、より簡潔で読みやすい構文を提供します。文字列補間は、文字列リテラルの前に$記号を付け、波括弧 {} 内に直接変数や式を記述するものです。

“`csharp
int age = 30;
string name = “山田”;

// string.Format を使用
string formatString = string.Format(“私の名前は{1}で、年齢は{0}歳です。”, age, name);

// 文字列補間を使用
string interpolatedString = $”私の名前は{name}で、年齢は{age}歳です。”;

Console.WriteLine(formatString); // 出力: 私の名前は山田で、年齢は30歳です。
Console.WriteLine(interpolatedString); // 出力: 私の名前は山田で、年齢は30歳です。
“`

どちらのコードも同じ出力を生成しますが、文字列補間の方が変数名とフォーマット文字列内のプレースホルダーが直接対応しているため、可読性が高いことが多いです。

文字列補間でも、string.Formatと同様に書式指定子や位置合わせを指定できます。構文は {variable:formatString}{variable,alignment} です。

“`csharp
decimal price = 123.45m;
DateTime now = DateTime.Now;

// string.Format で書式指定
string formatExample = string.Format(“価格: {0:C}, 日時: {1:yyyy/MM/dd HH:mm}”, price, now);
Console.WriteLine(formatExample);

// 文字列補間で書式指定
string interpolatedExample = $”価格: {price:C}, 日時: {now:yyyy/MM/dd HH:mm}”;
Console.WriteLine(interpolatedExample);
“`

出力例 (ja-JP カルチャー):

価格: ¥123.45, 日時: 2023/10/27 14:30
価格: ¥123.45, 日時: 2023/10/27 14:30

位置合わせも文字列補間で同様に指定できます。

“`csharp
string item = “ガジェット”;
int count = 42;

// string.Format で位置合わせ
string formatAlign = string.Format(“{0,-15} {1,5:D}”, item, count);
Console.WriteLine(formatAlign);

// 文字列補間で位置合わせ
string interpolatedAlign = $”{item,-15} {count,5:D}”;
Console.WriteLine(interpolatedAlign);
“`

出力:

ガジェット 00042
ガジェット 00042

カルチャー指定も、文字列補間では string.Create(provider, $"...) のように行えますが、string.Format(provider, $"...) の形式でIFormatProviderを渡すこともできます。

“`csharp
CultureInfo usCulture = new CultureInfo(“en-US”);
decimal amount = 1234.56m;

// string.Format でカルチャー指定
string formatCulture = string.Format(usCulture, “Amount: {0:C2}”, amount);
Console.WriteLine(formatCulture); // 出力: Amount: $1,234.56

// 文字列補間でカルチャー指定 (string.Format オーバーロードを使用)
string interpolatedCulture = string.Format(usCulture, $”Amount: {amount:C2}”);
Console.WriteLine(interpolatedCulture); // 出力: Amount: $1,234.56

// 文字列補点でカルチャー指定 (string.Create を使用)
// string interpolatedCreateCulture = string.Create(usCulture, $”Amount: {amount:C2}”);
// Console.WriteLine(interpolatedCreateCulture); // 同じ出力
“`

文字列補間 vs string.Format: 使い分け

ほとんどの新しいコードでは、可読性の高さから文字列補間を使用するのが推奨されます。しかし、以下のようなケースではstring.Formatやその派生メソッド(StringBuilder.AppendFormatなど)の方が適している場合があります。

  1. フォーマット文字列が動的に生成される場合: 例えば、データベースからフォーマット文字列を取得したり、ユーザー入力を基にフォーマット文字列を組み立てたりする場合など。文字列補間はリテラル文字列に対して使用するため、動的なフォーマット文字列には適用できません。
  2. 国際化対応で、多数の異なるカルチャーに対応する必要がある場合: フォーマット文字列自体をリソースファイルに格納し、現在のUIカルチャーに基づいて適切なフォーマット文字列を取得してstring.Formatに渡す、というパターンが一般的です。
  3. 複合書式指定文字列を扱う場合: 極めて複雑なカスタム書式が必要な場合や、古いコードベースとの互換性を保つ場合など。
  4. ログ出力など、パフォーマンスが特に重視される場面: StringBuilder.AppendFormatは、大量の文字列連結を行う場合にパフォーマンス上の利点がある場合があります。文字列補間も最適化されますが、シナリオによっては明示的なAppendFormatが有利なこともあり得ます(ただし、これはマイクロベンチマークで検証する必要があります)。

パフォーマンスに関する考慮事項

文字列の連結や整形は、特にループ内で大量に行う場合にパフォーマンスのボトルネックとなることがあります。

  • 単純な文字列連結(+演算子やstring.Concat)は、多数回繰り返すと中間文字列オブジェクトが大量に生成され、非効率になる可能性があります。
  • string.Formatは内部で最適化が行われますが、非常に大量かつ複雑な整形を繰り返す場合は、StringBuilderクラスとAppendFormatメソッドを組み合わせるのが最も効率的です。StringBuilderは可変長のバッファを使用するため、中間文字列の生成を抑えられます。

“`csharp
// 非効率な文字列連結 (大量のループでは避ける)
string result = “”;
for (int i = 0; i < 1000; i++)
{
result += “Item ” + i.ToString() + “, “;
}

// string.Format を使用 (より良いが、StringBuilderがさらに良い場合も)
string[] items = Enumerable.Range(0, 1000).Select(i => $”Item {i}”).ToArray();
string resultFormat = string.Join(“, “, items); // string.Format ではないが、整形の一例として

// StringBuilder + AppendFormat を使用 (大量のループで最も効率的)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
if (i > 0) sb.Append(“, “);
sb.AppendFormat(“Item {0}”, i);
}
string resultBuilder = sb.ToString();
“`

文字列補間もコンパイラによって最適化されるため、多くの場合はStringBuilderを使用するのと同等かそれに近いパフォーマンスを発揮します。通常は可読性の高い文字列補間を使用し、特定のパフォーマンス要求がある場合にStringBuilderstring.Format + StringBuilderを検討するのが良いでしょう。

よくあるエラーとその対処法

string.Formatを使用する際によく遭遇するエラーとその対処法です。

  1. IndexOutOfRangeException: フォーマット文字列内のプレースホルダーのインデックスが、提供された引数の配列の範囲を超えている場合に発生します。

    • 原因: {0}{1}… と記述しているが、対応する引数の数が足りない、またはインデックスが間違っている。
    • 対処法: フォーマット文字列中の最大インデックスが、引数の数から1を引いた値と一致しているか確認します。引数の順序も確認しましょう。
      csharp
      // エラー例: Index 1 (引数2つ目が期待されている) が存在しない
      // string error = string.Format("私の名前は{1}です。", "山田"); // 実行時エラー
      // 正しい例:
      string correct = string.Format("私の名前は{0}です。", "山田"); // OK
  2. FormatException: フォーマット文字列の構文が間違っているか、引数の型と指定した書式指定子が互換性がない場合に発生します。

    • 原因: {} の対応が取れていない、書式指定子の文字が間違っている、数値型ではない引数に数値書式指定子を使っている、日付型ではない引数に日付書式指定子を使っている、など。
    • 対処法:
      • フォーマット文字列の {} の数が合っているか、エスケープが必要な {}} が正しく {{}} と記述されているか確認します。
      • 使用している書式指定子(C, D, F, yyyy, MMなど)が、対応する引数の型(数値、日付、列挙体など)と互換性があるか確認します。
      • カスタム書式指定子を使用している場合は、記号の並びやセクション区切り(;)が正しいか確認します。

    ``csharp
    // エラー例1:
    {` の対応が取れていない
    // string error1 = string.Format(“値: {0”, 123); // 実行時エラー

    // エラー例2: 数値ではない引数に数値書式指定子
    // string error2 = string.Format(“名前: {0:N}”, “山田”); // 実行時エラー

    // 正しい例1:
    string correct1 = string.Format(“値: {0}”, 123); // OK

    // 正しい例2:
    string correct2 = string.Format(“名前: {0}”, “山田”); // OK
    “`

  3. カルチャーによる予期しない出力: 特定の環境では問題ないが、別の環境(別のカルチャーが設定されているマシンなど)で実行すると、小数点記号や日付形式などが意図と異なる表示になる場合があります。

    • 原因: string.FormatがデフォルトでCultureInfo.CurrentCultureを使用するため、実行環境によって書式が変わる。
    • 対処法: 明示的にIFormatProviderを指定できるオーバーロードを使用し、CultureInfo.InvariantCultureや特定のカルチャー(例: new CultureInfo("en-US"))を渡します。特に、ログファイル出力やデータファイル出力など、プログラム間で共通の書式が必要な場合は、CultureInfo.InvariantCultureを使用することを強く推奨します。

まとめ

string.Formatメソッドは、C#における文字列整形処理の基盤となる強力なツールです。基本的なプレースホルダーの仕組みから、数値、日付、時刻、列挙体といった様々なデータ型に対応した豊富な標準およびカスタム書式指定子、さらには位置合わせやカルチャー制御まで、多岐にわたる機能を提供します。

現代のC#では、文字列補間($"")が多くの一般的な用途でstring.Formatよりも簡潔かつ直感的な記述方法を提供します。しかし、string.Formatは動的なフォーマット文字列の扱い、明示的なカルチャー制御、StringBuilder.AppendFormatとの連携など、特定のシナリオで依然として重要な役割を果たします。

本記事で解説した内容、特に各種書式指定子、位置合わせ、カルチャーの影響を理解することで、string.Formatや文字列補間を使った文字列処理をより効果的に、そして正確に行えるようになります。用途に応じて最適な方法を選択し、読みやすく保守しやすいコードを記述していきましょう。

文字列処理はプログラミングにおいて非常に頻繁に行われる操作です。string.Formatとその関連技術を習得することは、C#プログラマとしてのスキル向上に不可欠です。ぜひ本記事を参考に、様々なケースで文字列整形を試してみてください。


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール