Menu

C# 字符串插值与 String.Format:格式说明符、对齐与区域性

C# 中 $"..." 字符串插值的工作方式:花括号里的表达式,F2、N0、C、D5、X 等格式说明符,用于表格的对齐,花括号转义,string.Format 与复合格式,逐字和原始插值字符串,以及按指定区域性格式化。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

字符串插值用字面文本和表达式拼出一个字符串。在字面量前加 $,把任意 C# 表达式放进花括号,它的值就会被转换成文本插入进去:

输出:

Hello, Maya!
3 items at 4.5 = 13.5
Name has 4 letters, upper: MAYA
Free shipping: yes

插值是 C# 6 引入的。编译器会把它转换成 string.Format 或 string.Concat 调用(从 C# 10 起,会转换成更高效的处理程序代码),所以它并不比手动拼接字符串慢。

最后一行中的条件表达式需要加括号。在插值的花括号里,冒号表示格式说明符的开始,所以不加括号的 {x > 10 ? "yes" : "no"} 会导致编译错误。

格式说明符

在表达式后面加一个冒号和格式字符串,就能控制值的写法。同样的格式字符串也适用于 ToString("...") 和 string.Format。

输出:

1234567.89
1,234,567.89
1,234,568
00042
FF
00ff
1.23E+006
0.813
81.3%
1,234,567.89
007.5
3.1

标准格式代码,每个后面都可以跟一个精度数字:

代码名称示例结果
F 或 F2定点{3.14159:F2}3.14
N 或 N0带分组分隔符的数字{1234.56:N0}1,235
C货币(区域性的符号和格式)en-US 下的 {9.5:C}$9.50
D5用零补齐的整数{42:D5}00042
X、x十六进制{255:X}FF
E2科学计数法{1234.5:E2}1.23E+003
P1百分比(乘以 100)en-US 下的 {0.256:P1}25.6%
G常规(最短形式){2.50m:G}2.50
R往返(double){0.1:R}0.1

自定义格式由占位符组成:0 是始终显示的数字位,# 是仅在有意义时才显示的数字位,. 是小数点,数字中间的 , 启用分组分隔符,% 会乘以 100。"0.##" 最多显示两位小数并去掉末尾的零;"#,##0.00" 是经典的会计格式。

所有数字格式化都会对显示的值进行四舍五入,变量本身不变。当值正好处于中间(2.25 保留一位小数)时,不同的运行时和类型对这种平局的舍入可能不同,所以如果规则很重要,先用 Math.Round(value, 1, MidpointRounding.AwayFromZero) 显式舍入,再进行格式化。

插值中的日期

DateTime 值以同样的方式接受日期格式字符串:

输出:

2026-09-24
24/09/2026 16:05
Thu, Sep 24
Shipped at 16:05 on Thursday
ETA 02:10
2.2 hours

TimeSpan 的自定义格式要求用反斜杠转义字面字符,在普通字符串字面量里写作 \\:。完整的日期代码列表(yyyy、MM、HH、tt...)以及大家常犯的错误(mm 是分钟,MM 是月份)见 DateTime 格式。

对齐与补齐

表达式后面的逗号设置最小字段宽度。正数表示右对齐,负数表示左对齐。和格式结合使用,就能在纯文本中把表格排整齐:

输出:

Item          |  Price| Sold
----------------------------
Espresso      |   2.40|  118
Latte         |   3.60|   64
Hot chocolate |   4.25|    9

语法是 {expression,alignment:format}:先写对齐,再写格式。比宽度长的值不会被截断,而是把这一行剩下的内容往后推。在插值之外,PadLeft 和 PadRight 也能实现同样的补齐。

转义花括号

要输出字面的花括号,就写两次:{{ 和 }}。输出 JSON、CSS 或代码时经常会用到:

输出:

{ "id": 42, "name": "Ana" }
Set literal: {1, 2, 3}
{42}

像这样手写 JSON 用来快速打一行日志没问题;真正的 JSON 请使用序列化器,它能正确处理 name 里的引号和特殊字符。

string.Format 与复合格式

在 C# 6 之前,格式化靠 string.Format 完成,它接受带编号的占位符和一组参数。Console.WriteLine、StringBuilder.AppendFormat 和 TextWriter.Write 也接受同样的复合格式语法:

输出:

Notebook x4: 14.00
Notebook  |  3.50
echo echo !
Dear Leo, your order #000731 has shipped.

在代码中优先使用插值:值就写在它出现的位置,占位符不可能指向错误的参数。当模板本身是数据时,比如从资源文件加载的翻译消息,string.Format 仍然是合适的工具。占位符索引没有对应的参数(只有两个参数却写了 {2})时,会在运行时抛出 FormatException。

区域性:控制分隔符和货币

插值、ToString 和 string.Format 默认都使用线程的当前区域性。在设置为德语的机器上,{1234.5:N2} 得到 1.234,50;在美国则得到 1,234.50。货币格式 C 使用区域性的货币符号。要显式选择区域性,就把插值字符串捕获为 FormattableString,再用一个提供程序来格式化:

输出:

Total: 1,234.50
Total: 1.234,50
$1,234.50
1.234,50
1234.5,0.75

给人读的文本使用用户的区域性,给其他程序读的内容使用 CultureInfo.InvariantCulture。在德语机器上用当前区域性写出的 CSV,数字中带有逗号,换个地方就无法读取。在 .NET 6 及更高版本中,string.Create(CultureInfo.InvariantCulture, $"...") 的效果与 FormattableString.Invariant 相同,但不会创建中间对象。

逐字插值字符串和原始插值字符串

$ 可以和 @ 组合成逐字插值字符串:反斜杠按字面处理,字符串可以跨多行。C# 8 及更高版本接受任意顺序($@ 或 @$);C# 6 和 7 要求写 $@:

输出:

C:\Users\ana\Reports\2026\summary.txt
Report for ana
Year: 2026

C# 11 新增了原始插值字符串。用一个 $ 时,花括号照常表示表达式。用 $$ 时,单个花括号是字面字符,表达式需要两个花括号,这让 JSON 和代码模板更易读:

// C# 11 and later
int id = 42;
string name = "Ana";
string json = $$"""
    {
      "id": {{id}},
      "name": "{{name}}"
    }
    """;

C# 11 还允许在插值的花括号内换行,所以长表达式或 switch 表达式可以拆成多行来写。

常见错误

  • 忘了写 $。 "Total: {total}" 会原样输出花括号,编译器不会给出警告。
  • 花括号里的三元表达式没加括号:冒号会被当成格式分隔符。
  • 用当前区域性格式化给机器读的输出。 小数逗号会破坏 CSV、JSON 和 SQL。请使用固定区域性。
  • 金额用不带格式的 ToString()。 2.5m 会输出 2.5,而不是 2.50。请用 F2、N2 或 C。
  • 在循环里用插值构建大字符串。 每次迭代都会创建一个新字符串;请使用 StringBuilder 或 string.Join。

常见问题

C# 字符串插值是什么?

一种以 $ 开头的字符串字面量,花括号里的表达式会被求值并插入其中:$"Total: {price * qty}"。它在 C# 6 中加入,取代了大多数 string.Format 和 + 拼接的用法。花括号里可以放任何表达式,包括方法调用和属性访问。

C# 中如何把数字格式化为保留 2 位小数?

在冒号后面加格式说明符:$"{price:F2}" 输出小数点后 2 位,$"{price:N2}" 效果相同,但带千位分隔符。在插值之外,同样的格式代码也可以用于 price.ToString("F2")。两者都只是为了显示而四舍五入,不会改变值本身。

C# 插值字符串中如何转义花括号?

写两次:{{ 输出 {,}} 输出 }。例如 $"{{ \"id\": {id} }}" 输出 { "id": 42 }。在 C# 11 的原始插值字符串中,也可以用 $$ 开头,这样单个花括号就是字面字符,而 {{expr}} 表示表达式。

string.Format 和字符串插值有什么区别?

它们用同样的格式代码得到同样的结果。string.Format("{0} costs {1:F2}", name, price) 按位置引用参数;$"{name} costs {price:F2}" 把表达式直接写在字符串里,更易读,也不会弄错参数顺序。当格式字符串本身来自资源文件或数据库时,仍然需要 string.Format。

为什么插值出来的数字用逗号而不是小数点?

插值使用当前线程的区域性来格式化,而很多区域性用逗号作为小数分隔符。对于给程序读取的输出(文件、JSON、URL),请用固定区域性格式化:FormattableString.Invariant($"{value:F2}"),或在 .NET 6 及更高版本中使用 string.Create(CultureInfo.InvariantCulture, $"...")。

Coddy 编程语言插图

和 Coddy 一起学编程

开始