bigywater.tech
Java · 184

Formatter:格式化字符串背后的解析过程

String.format 背后的格式说明符怎么解析?各种写法会得到什么、出错时抛什么?这篇用同一个小程序把几十种写法的实际输出打印出来(正则回溯在前一篇,String.format 的耗时在已有文章里)。

HALF_UP
%.0f 把 2.5 变成 3、3.5 变成 4
1.234.567 / 1234,50
de_DE 下 %,d 与 %.2f 的输出
MissingFormatArgumentException
参数不够时抛出的异常

说明符语法

实测 · 本机执行输出
  语法:%[参数序号$][标志][宽度][.精度]转换符(以下 Locale.US)
    %d                     → "42"
    %5d|                   → "   42|"
    %-5d|                  → "42   |"
    %05d                   → "00042"
    %+d                    → "+42"
    %,d                    → "1,234,567"
    %x                     → "ff"
    %X                     → "FFFFFFFF"
    %o                     → "10"
    %(d                    → "(42)"
    %f                     → "3.141590"
    %.2f                   → "3.14"
    %8.3f                  → "   3.142"
    %08.3f                 → "-003.142"
    %e                     → "1.234568e+04"
    %.3g                   → "0.000123"
    %.0f                   → "3"
    %.0f                   → "4"
    %.1f                   → "0.3"
    %.1f                   → "0.4"
    %s                     → "hi"
    %10s|                  → "        hi|"
    %-10s|                 → "hi        |"
    %.2s                   → "he"
    %S                     → "HI"
    %s                     → "null"
    %s                     → "[1, 2]"
    %b                     → "true"
    %b                     → "false"
    %c                     → "A"
    %c                     → "😀"
    %%                     → "%"
    %h                     → "d01"

异常

实测 · 本机执行输出
    %d                     → IllegalFormatConversionException: d != java.lang.Double
    %d                     → IllegalFormatConversionException: d != java.lang.String
    %s %s                  → MissingFormatArgumentException: Format specifier '%s'
    %z                     → UnknownFormatConversionException: Conversion = 'z'
    %#d                    → FormatFlagsConversionMismatchException: Conversion = d, Flags = #
    %-d                    → MissingFormatWidthException: %-d
    %5                     → UnknownFormatConversionException: Conversion = '5'
    %.2d                   → IllegalFormatPrecisionException: 2
    %,s                    → FormatFlagsConversionMismatchException: Conversion = s, Flags = ,
    %d                     → "10"
    %d                     → "123456789012345678901234567890"
    %f                     → IllegalFormatConversionException: f != java.lang.Integer
    %d                     → "null"
    %s                     → "1"
  多余的参数不报错(最后一行 %s 只用了第一个);缺少参数、类型不符、格式符不认识、标志冲突才抛异常

Locale

实测 · 本机执行输出
    en_US    %,d → 1,234,567        %.2f → 1234.50
    de_DE    %,d → 1.234.567        %.2f → 1234,50
    fr_FR    %,d → 1 234 567        %.2f → 1234,50
    hi_IN    %,d → 1,234,567        %.2f → 1234.50
    ar_EG    %,d → ١٬٢٣٤٬٥٦٧        %.2f → ١٢٣٤٫٥٠
    默认 Locale = en_CN;String.format("%.2f", 1234.5) = 1234.50(不指定 Locale 时用默认 Locale)
    大写转换也受 Locale 影响:Locale(tr) 下 String.format("%S", "i") = İ,US 下 = I

参数序号

实测 · 本机执行输出
    %2$s %1$s              → "hello world"
    %1$s %1$s %1$s         → "echo echo echo"
    %s %<s %s              → "a a b"
    %2$s %s %s             → "y x y"
    %s %2$s %s             → "x y y"
    %3$s                   → MissingFormatArgumentException: Format specifier '%3$s'
  规则:普通的 %s 按顺序取参数,不受 %2$s 这种显式序号的影响;%<s 重复上一个格式符用过的参数
    %n 与 \n:a\nb vs a\nb(%n 是平台的行分隔符,macOS/Linux 上是 \n)
    Formattable 对象配合 %-10.3s:<自定义:flags=1,width=10,precision=3>

分步演示

String.format
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,本机默认 Locale 是 en_CN
    • 没有验证:JDK 17 与 27 的行为差异、%g 的细节、日期时间转换符 %t、Formatter 的缓存与耗时(见已有的 String.format 开销文章)、MessageFormat 与 DecimalFormat(在后面的篇目)
    京ICP备2026067502号-1京公网安备11010502063487号