[C#] 文字列補間による値の整形

文字列補間を使って、書式を指定した文字列整形をいろいろ試した。
`String.Format`メソッドでの書式指定となにが違うというわけでもないが、記述量が減るので、書式のサンプル集としてはシンプルにまとまった。

# 整数 標準数値書式編
整数と小数を区切る文字や、整数の桁を区切る文字などの設定は、`NumberFormatInfo`オブジェクトに含まれる。
現在のカルチャで指定されている値は`NumberFormatInfo.CurrentInfo`で求めることができる。

```csharp
> int i = 1234;

> $"{i:D}"                    // 10進数整数表記
"1234"

> $"{i:D6}"                   // 10進数整数表記 + 最小桁数指定
"001234"

> $"{i:X}"                    // 16進数表記
"4D2"

> $"{i:X4}"                   // 16進数表記 + 最小桁数指定
"04D2"

> $"{i:N}"                    // 区切り記号
"1,234.00"

> $"{i:N4}"                   // 区切り記号 + 小数部の桁数指定
"1,234.0000"

> $"{i:C}"                    // 通貨記号&区切り記号
"¥1,234"

> $"{i:C4}"                   // 通貨記号&区切り文字 + 小数部の桁数指定
"¥1,234.0000"

> $"{i:F}"                    // 固定小数点
"1234.00"

> $"{i:F4}"                   // 固定小数点 + 小数部の桁数指定
"1234.0000"

> $"{i:E}"                    // 指数(E)。小数部の桁数は6
"1.234000E+003"

> $"{i:e}"                    // 指数(e)。小数部の桁数は6
"1.234000e+003"

> $"{i:E4}"                   // 指数(E) + 小数部の桁数指定
"1.2340E+003"

> $"{i:P}"                    // パーセント
"123,400.00%"

> $"{i:P4}"                   // パーセント + 小数部の桁数指定
"123,400.0000%"

> $"{i:G}"                    // 固定小数点表記、または、指数表記のうち簡潔なほう
"1234"

> $"{i:G4}"                   // 固定小数点表記、または、指数表記のうち簡潔なほう + 桁数指定
"1234"

> $"{i:G6}"                   // 固定小数点表記、または、指数表記のうち簡潔なほう + 桁数指定
"1234"

> $"{i:G2}"                   // 固定小数点表記、または、指数表記のうち簡潔なほう + 桁数指定
"1.2E+03"

> BigInteger b = BigInteger.Parse("1234567890123456789012345678901234567890123456789");

> $"{b:R}"                    // ラウンドトリップ
"1234567890123456789012345678901234567890123456789"

> $"{b:R8}"                   // ラウンドトリップ + 桁数指定(無視される)
"1234567890123456789012345678901234567890123456789"
```
# 整数 カスタム数値書式編
エスケープ文字をどうしても使いたい場合、`$@"..."`という書き方もできるのがおもしろい。

```csharp
> int i = 1234;

> $"{i:00000}"                // ゼロプレースホルダー
"01234"

> $"{i:00000.00}"             // ゼロプレースホルダー + 小数部指定
"01234.00"

> $"{i:#####}"                // 桁プレースホルダー
"1234"

> $"{i:#####.##}"             // 桁プレースホルダー + 小数部指定
"1234"

> $"{0:#####.##}"             // 桁プレースホルダーのゼロの扱い
""

> $"{1234567890:#,0}"         // 桁区切り記号
"1,234,567,890"

> $"{1234567890:0,}"          // 数値の位取り 1000で除算される
"1234568"

> $"{1234567890:0,,}"         // 数値の位取り 1000000で除算される
"1235"

> $"{1234567890:#,0,}"        // 桁区切り記号 + 数値の位取り
"1,234,568"

> $"{1234567890:#,0,,}"       // 桁区切り記号 + 数値の位取り
"1,235"

> $"{i:#.0%}"                 // パーセントプレースホルダー
"123400.0%"

> $"{i:#.0‰}"                 // パーミルプレースホルダー
"1234000.0‰"

> $"{i:0.0e0}"                // 指数表記
"1.2e3"

> $"{i:0.0E+0}"               // 指数表記
"1.2E+3"

> $"{i:#' degrees'}"          // リテラル文字列。$"{i:#} degrees"と書かない理由がない
"1234 degrees"

> $"{i:\\###00\\#}"           // エスケープ文字。ほとんどの人が$"#{i:##00}#"と書くだろう
"#1234#"

> $@"{i:\###00\#}"            // エスケープ文字。なんと、こうも書ける
"#1234#"

> $"{i:00000;00000000;-0-}"   // 正の値、負の値、ゼロにそれぞれ別の書式を割り当てる
"01234"                       // 正の場合は先頭の書式が使用される
> $"{-i:00000;00000000;-0-}"
"00001234"                    // 負の場合は真ん中の書式が使用される
> $"{0:00000;00000000;-0-}"
"-0-"                         // ゼロの場合は最後の書式が使用される
```

# 小数 標準数値書式編
```csharp
> double d = 1234.56789;

> $"{d:N}"                    // 区切り記号
"1,234.57"

> $"{d:N4}"                   // 区切り記号 + 小数部の桁数指定
"1,234.5679"

> $"{d:C}"                    // 通貨記号&区切り記号
"¥1,235"

> $"{d:C4}"                   // 通貨記号&区切り記号 + 小数部の桁数指定
"¥1,234.5679"

> $"{d:F}"                    // 固定小数点
"1234.57"

> $"{d:F4}"                   // 固定小数点 + 小数部の桁数指定
"1234.5679"

> $"{d:E}"                    // 指数(E)
"1.234568E+003"

> $"{d:e}"                    // 指数(e)
"1.234568e+003"

> $"{d:E4}"                   // 指数(E) + 小数部の桁数指定
"1.2346E+003"

> $"{d:P}"                    // パーセント表記
"1,234.57%"

> $"{d:P4}"                   // パーセント表記 + 小数部の桁数指定
"1,234.5678%"

> $"{d:R}"                    // ラウンドトリップ
"1234.56789"

> $"{d:R4}"                   // ラウンドトリップ + 桁数指定(無視される)
"1234.56789"

> $"{d:G}"                    // 固定小数点表記、または、指数表記のうち簡潔なほう
"1234.56789"

> $"{d:G4}"                   // 固定小数点表記、または、指数表記のうち簡潔なほう + 桁数指定
"1235"

> $"{d:G6}"                   // 固定小数点表記、または、指数表記のうち簡潔なほう + 桁数指定
"1234.57"

> $"{d:G2}"                   // 固定小数点表記、または、指数表記のうち簡潔なほう + 桁数指定
"1.2E+03"
```

# 小数 カスタム数値書式編

```csharp
> double d = 1234.56789;

> $"{d:00000}"                 // ゼロプレースホルダー
"01235"

> $"{d:00000.00}"              // ゼロプレースホルダー + 小数部指定
"01234.57"

> $"{d:00000.000000}"          // ゼロプレースホルダー + 小数部指定
"01234.567890"

> $"{d:#####}"                 // 桁プレースホルダー
"1235"

> $"{d:#####.##}"              // 桁プレースホルダー + 小数部指定
"1234.57"

> $"{d:#####.######}"          // 桁プレースホルダー + 小数部指定
"1234.56789"

> $"{0.0:#####.##}"            // 桁プレースホルダーのゼロの扱い
""

> $"{12345678.56789:#,#.##}"   // 桁区切り記号
"12,345,678.57"

> $"{12345678.56789:#,.##}"    // 数値の位取り 1000で除算される
"12345.68"

> $"{12345678.56789:#,,.##}"   // 数値の位取り 1000000で除算される
"12.35"

> $"{12345678.56789:#,#,.##}"  // 桁区切り記号 + 数値の位取り
"12,345.68"

> $"{d:#.0%}"                  // パーセントプレースホルダー
"123456.8%"

> $"{d:#.0‰}"                  // パーミルプレースホルダー
"1234567.9‰"

> $"{d:0.0e0}"                 // 指数表記(e)。指数部が負の場合にのみ符号文字が入る
"1.2e3"

> $"{0.123456789:0.0E0}"       // 指数表記(E)。指数部が負の場合にのみ符号文字が入る
"1.2E-1"

> $"{d:0.0E+0}"                // 指数表記(E+)。指数部に符号文字が入る
"1.2E+3"

> $"{d:0.0E-0}"                // 指数表記(E-)。指数部が負の場合にのみ符号文字が入る
"1.2E3"

> $"{0.123456789:0.0E-0}"      // 指数表記(E-)。指数部が負の場合にのみ符号文字が入る
"1.2E-1"

> $"{d:#' degrees'}"           // リテラル文字列。$"{d:#} degrees"と書かない理由がない
"1235 degrees"

> $"{d:\\###00.00##\\#}"       // エスケープ文字。ほとんどの人が$"#{d:##00.00##}#"と書くだろう
"#1234.5679#"

> $@"{d:\###00.00##\#}"        // エスケープ文字。なんと、こうも書ける
"#1234.5679#"

> $"{d:00000;00000000;-0-}"    // 正の値、負の値、ゼロにそれぞれ別の書式を割り当てる
"01235"                        // 正の場合は先頭の書式が使用される
> $"{-d:00000;00000000;-0-}"
"00001235"                     // 負の場合は真ん中の書式が使用される
> $"{0.0:00000;00000000;-0-}"
"-0-"                          // 負の場合は真ん中の書式が使用される
```

# DateTime 標準日時書式編
使用される書式の設定は、`DateTimeFormatInfo`オブジェクトに含まれる。
現在のカルチャで指定されている値は`DateTimeFormatInfo.CurrentInfo`で求めることができる。

```csharp
> DateTime d = new DateTime(2000, 1, 2, 10, 20, 30);

> $"{d:d}"                    // = ToShortDateString()
"2000/01/02"

> $"{d:D}"                    // = ToLongDateString()
"2000年1月2日"

> $"{d:t}"                    // = ToShortTimeString()
"10:20"

> $"{d:T}"                    // = ToLongTimeString()
"10:20:30"

> $"{d:f}"                    // D + t
"2000年1月2日 10:20"

> $"{d:F}"                    // D + T
"2000年1月2日 10:20:30"

> $"{d:g}"                    // d + t
"2000/01/02 10:20"

> $"{d:G}"                    // d + T
"2000/01/02 10:20:30"

> $"{d:M}"                    // mでも同様
"1月2日"

> $"{d:Y}"                    // yでも同様
"2000年1月"

> $"{d:U}"                    // UTCの値に変換して出力される。Fと同じ書式
"2000年1月2日 1:20:30"
```

以下はどのカルチャの環境下でも同じ動作をするはずの書式。

```csharp
> $"{d:O}"                    // ISO 8601。oでも同様
"2000-01-02T10:20:30.0000000"

> $"{d:R}"                    // RFC1123。rでも同様。値をGMTに変換したりはしないことに注意
"Sun, 02 Jan 2000 10:20:30 GMT"

> $"{d:s}"                    // ISO 8601
"2000-01-02T10:20:30"

> $"{d:u}"                    // ZはUTCの意味。値をUTCへ変換したりはしないことに注意
"2000-01-02 10:20:30Z"
```

#DateTime カスタム日時書式指定編

```csharp
> var d = new DateTime(2018, 1, 2, 3, 4, 5, 100);  // 2018-01-02T03:04:05.10000

> $"{d:%g}"                   // 時期、時代、年号。単独使用でなければ%は不要
"西暦"

> $"{d:gg}"                   // 時期、時代、年号
"西暦"

> $"{d:%y}"                   // 年(0-99)。単独使用でなければ%は不要
"18"
  
> $"{d:yy}"                   // 年(00-99)
"18"

> $"{d:yyy}"                  // 年(3桁以上)
"2018"

> $"{d:yyyy}"                 // 年(4桁の数値)
"2018"

> $"{d:yyyyy}"                // 年(5桁の数値)
"02018"

> $"{d:%M}"                   // 月(1-12)。単独使用でなければ%は不要
"1"

> $"{d:MM}"                   // 月(01-12)
"01"

> $"{d:MMM}"                  // 月の省略名
"1"

> $"{d:MMMM}"                 // 月の完全な名前
"1月"

> $"{d:%d}"                   // 月の日にち(1-31)。単独使用でなければ%は不要
"2"

> $"{d:dd}"                   // 月の日にち(01-31)
"02"

> $"{d:ddd}"                  // 曜日の省略名
"火"

> $"{d:dddd}"                 // 曜日の完全な名前
"火曜日"

> $"{d:%t}"                   // AM/PM指定子の最初の一文字。単独使用でなければ%は不要
"午"

> $"{d:tt}"                   // AM/PM指定子の完全な名前
"午前"

> $"{d:%H}"                   // 24時間形式の時間(0-23)。単独使用でなければ%は不要
"3"

> $"{d:HH}"                   // 24時間形式の時間(00-23)
"03"

> $"{d:%h}"                   // 12時間形式の時間(1-12)。単独使用でなければ%は不要
"3"

> $"{d:hh}"                   // 12時間形式の時間(01-12)
"03"

> $"{d:%m}"                   // 分(0-59)。単独使用でなければ%は不要
"4"

> $"{d:mm}"                   // 分(00-59)
"04"

> $"{d:%s}"                   // 秒(0-59)。単独使用でなければ%は不要
"5"

> $"{d:ss}"                   // 秒(00-59)
"05"

> $"{d:%f}"                   // 日時値の秒部分の1/10。0埋めされる。単独使用でなければ%は不要
"1"

> $"{d:ff}"                   // 日時値の秒部分の1/100。0埋めされる。以下、fff,ffffなども同様
"10"

> $"{d:%F}"                   // 日時値の秒部分の1/10。0埋めされない。単独使用でなければ%は不要
"1"

> $"{d:FF}"                   // 日時値の秒部分の1/100。0埋めされない。以下、FFF,FFFFなども同様
"1"

> d = new DateTime(2018, 1, 2, 3, 4, 5, 0);
> $"{d:%F}"                   // Fの場合、0の値は出力されない
""

> d = new DateTime(2018, 1, 2, 3, 4, 5, DateTimeKind.Unspecified);
> $"{d:%K}"                   // タイムゾーン情報。Unspecifiedなら空文字。単独使用でなければ%は不要
""

> d = new DateTime(2018, 1, 2, 3, 4, 5, DateTimeKind.Utc);
> $"{d:%K}"                   // タイムゾーン情報。UtcならZ。単独使用でなければ%は不要
"Z"

> d = new DateTime(2018, 1, 2, 3, 4, 5, DateTimeKind.Local);
> $"{d:%K}"                   // タイムゾーン情報。Localならzzzに相当。単独使用でなければ%は不要
"+09:00"

> $"{d:%z}"                   // 基準とする時間単位のオフセット(1桁)。単独使用でなければ%は不要
"+9"

> $"{d:zz}"                   // 基準とする時間単位のオフセット(先行ゼロつきの1桁)
"+09"

> $"{d:zzz}"                  // 基準とする時間および分単位のオフセット
"+09:00"
```

# DateTimeOffset 標準日時書式編

DateTimeと同様の結果が得られる書式については省略する。

```csharp
> DateTimeOffset d = new DateTimeOffset(2000, 1, 2, 10, 20, 30, TimeZoneInfo.Local.BaseUtcOffset);

> $"{d:U}"                    // DateTimeではUTCの値に変換して出力されるが、DateTimeOffsetだとエラー
入力文字列の形式が正しくありません。
  + System.DateTimeFormat.ExpandPredefinedFormat(string, ref System.DateTime, ref System.Globalization.DateTimeFormatInfo, ref System.TimeSpan)
  + System.DateTimeFormat.Format(System.DateTime, string, System.Globalization.DateTimeFormatInfo, System.TimeSpan)
  + System.Text.StringBuilder.AppendFormatHelper(System.IFormatProvider, string, System.ParamsArray)
  + string.FormatHelper(System.IFormatProvider, string, System.ParamsArray)
  + string.Format(string, object)

> $"{d:F}"                    // UTCの値に変換して出力される。DateTimeではUと同じ結果になったが……
"2000年1月2日 10:20:30"

> $"{d:R}"                    // RFC1123。rでも同様。DateTimeと違い、値がUTCへ変換される
"Sun, 02 Jan 2000 01:20:30 GMT"

> $"{d:u}"                    // ZはUTCの意味。DateTimeと違い、値がUTCへ変換される
"2000-01-02 01:20:30Z"
```
#DateTimeOffset カスタム日時書式指定編

DateTimeと同様の結果が得られる書式については省略する。

```csharp
> var d = new DateTimeOffset(2018, 1, 2, 3, 4, 5, 100, TimeZoneInfo.Local.BaseUtcOffset);

> $"{d:%K}"                   // タイムゾーン情報。zzzに相当。DateTimeKind.LocalのDateTimeと同じ動作
"+09:00"

> $"{d:%z}"                   // 基準とする時間単位のオフセット(1桁)。単独使用でなければ%は不要
"+9"

> $"{d:zz}"                   // 基準とする時間単位のオフセット(先行ゼロつきの1桁)
"+09"

> $"{d:zzz}"                  // 基準とする時間および分単位のオフセット
"+09:00"
```

# 複合書式指定のアライメント指定編

```csharp
> int i = 1234;

> $"{i,9}"                    // 右揃え
"     1234"

> $"{i,-9:00000}"             // 左揃え + 書式指定
"01234    "

> $"{i,3}"                    // 桁数が指定値をオーバーした
"1234"
```

# カルチャを指定した整形編

文字列補間式を`IFormattable`で受け取ることで、`IFormattable.ToString(string, IFormatProvider)`を使用できる。
これを利用することで、現在使用している環境とは違うカルチャで文字列を整形することができる。
下記では、カルチャに依存しない`CultureInfo`オブジェクトである`CultureInfo.InvariantCulture`を指定している。

```csharp
> var i = 1234;
> IFormattable format = $"{i:C}";                                         // 一度IFormattable型の変数にとる
> format.ToString(null, CultureInfo.InvariantCulture)
"¤1,234.00"

> ((IFormattable)$"{i:C}").ToString(null, CultureInfo.InvariantCulture)   // キャストでも可能。ただし、asだとコンパイルエラー
"¤1,234.00"

> var d = new DateTime(2017, 1, 2, 3, 4, 5);
> ((IFormattable)$"{d:F}").ToString(null, CultureInfo.InvariantCulture)   // Datetimeの例
"Monday, 02 January 2017 03:04:05"

> $"{d:F}".ToString(CultureInfo.InvariantCulture)                         // うまくいかない
"2017年1月2日 3:04:05"
```

最後の式がうまくいかないのは、`$"{d:F}"`を評価した段階で現在のカルチャが適用された文字列に変換されてしまうからだ。

# 和暦編

上記の`IFormattable`を使用する方法で、昭和だとか平成といった元号を使った年表記の文字列を取得できる。
`DateTime.ToString(string, IFormatProvider)`を使用したほうが短かく記述できるが、文字列補間を使用すると書式をコンパイル時にチェックできるという利点がある。

```csharp
> var d = new DateTime(2018, 1, 2, 3, 4, 5);

> var cul = new System.Globalization.CultureInfo("ja-JP");

> cul.DateTimeFormat.Calendar = new System.Globalization.JapaneseCalendar();

> IFormattable format = $"{d:ggyy年MM月dd日}";                            // 一度IFormattable型の変数にとる
> format.ToString(null, cul)
"平成30年01月02日"

> ((IFormattable)$"{d:ggyy年MM月dd日}").ToString(null, cul)               // キャストでも可能。ただし、asだとコンパイルエラー
"平成30年01月02日"

> d.ToString("ggyy年MM月dd日", cul)                                       // ToStringの方が記述量は少ない
"平成30年01月02日"
```

#参考にした情報
標準の数値書式指定文字列
https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/standard-numeric-format-strings
カスタム数値書式指定文字列
https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-numeric-format-strings
標準の日時書式指定文字列
https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/standard-date-and-time-format-strings
カスタム日時書式指定文字列
https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/custom-date-and-time-format-strings
書式を指定して数値を文字列に変換する
https://dobon.net/vb/dotnet/string/inttostring.html
日時（DateTimeオブジェクト）を文字列に変換する
https://dobon.net/vb/dotnet/string/datetimeformat.html
西暦と和暦を変換するには？
http://www.atmarkit.co.jp/ait/articles/0306/06/news004.html
特殊な文字列リテラル
http://ufcpp.net/study/csharp/st_string.html
.ToString(IFormatProvider) on an interpollated string should evaluate to FormattableString.ToString(IFormatProvider)
https://github.com/dotnet/roslyn/issues/12298