Formatter类提供了对布局对齐和排列的支持,以及对数值、字符串和日期、时间数据的常规格式和特定于语言环境的输出的支持。通用Java类型,诸如byte、BigDecimal和Calendar都被支持。
Java语言格式化后的字符串很像C语言的printf函数,但比C语言严谨,比如,C语言忽略无效参数,而Java则会抛出异常。同时它也不是为多线程设计的,程序员需要自己考虑同步的问题。
一. 语法
1. 常规类型、字符类型和数值类型的格式说明符语法如下:
%[argument_index$][flags][width][.precision]conversion
2. 用来表示日期和时间类型的格式说明符语法如下:
%[argument_index$][flags][width]conversion
3. 与参数不对应的格式说明符语法如下:
%[flags][width]conversion
-argument_index 可选参数,是一个十进制整数,用于表明参数在参数列表中的位置。第一个参数由 "1$" 表示,第二个参数由 "2$" 表示,依此类推。
-flags 可选参数,是修改输出格式的字符集。有效标志集取决于转换类型。
-width 可选参数,是一个非负十进制整数,表明要向输出中写入的最少字符数。
-precision 可选参数,是一个非负十进制整数,通常用来限制字符数。特定行为取决于转换类型。
-conversion 必须参数,是一个表明应该如何格式化参数的字符。给定参数的有效转换集取决于参数的数据类型。
二. 参数详解
1. Argument Index
是一个十进制整数,用于表明参数在参数列表中的位置。第一个参数由 "1$" 表示,第二个参数由 "2$" 表示,依此类推。
2. Flags
Flag | General | Character | Integral | Floating Point | Date/Time | Description |
'-' | y | y | y | y | y | 结果将是左对齐的 |
'#' | y1 | - | y3 | y | - |
结果应该使用依赖于转换类型的替换形式 |
'+' | - | - | y4 | y | - |
结果总是包括一个符号 |
' ' | - | - | y4 | y | - |
对于正值,结果中将包括一个前导空格 |
'0' | - | - | y | y | - |
结果将用零来填充 |
',' | - | - | y2 | y5 | - | 结果将包括特定于语言环境的组分隔符 |
'(' | - | - | y4 | y5 | - |
结果将是用圆括号括起来的负数 |
y1 取决于 Formattable 的定义。
y2 只适用于 'd' 转换。
y3 只适用于 'o'、'x' 和 'X' 转换。
y4 对 BigInteger 应用 'd'、'o'、'x' 和 'X' 转换时,或者对 byte 及 Byte、short 及 Short、int 及 Integer、long 及 Long 分别应用 'd' 转换时适用。
y5 只适用于 'e'、'E'、'f'、'g' 和 'G' 转换。
任何未显式定义为标志的字符都是非法字符,并且都被保留,以供扩展使用。
3. Width
width表示输出字符的最少字符数。被格式化参数用precision截取器截取后与width相比,被格式化参数字符数如果小于width,则加字符则到width。在行分隔符转换(line separator conversion)中,width被禁止使用。
4. Precision
- 对于常规类型,precision表示输出字符的最大字符数。
- 对浮点类型转换'e', 'E',和'f',precision小数点的几位数字。如果转换是'g'或'G',precision表示取整后的全部数字。如果转换是'a'或'A',precision被禁止使用。
- 对字符、整型的、日期和时间类型,precision被禁止使用。
5. Conversions
转换字符大致可分为如下几类:
- 常规(General)- 可应用于任何参数类型
- 字符(Character)- 可应用于表示 Unicode 字符的基本类型:char、Character、byte、Byte、short 和 Short。当 Character.isValidCodePoint(int) 返回 true 时,可将此转换应用于 int 和 Integer 类型。
- 数值(Numeric)
1)整数(Integral)- 可应用于 Java 的整数类型:byte、Byte、short、Short、int、Integer、long、Long 和 BigInteger
2)浮点(Floating Point)- 可用于 Java 的浮点类型:float、Float、double、Double 和 BigDecimal - 日期/时间(Date/Time)- 可应用于 Java 的、能够对日期或时间进行编码的类型:long、Long、Calendar 和 Date
- 百分比(Percent)- 产生字面值 '%' ('u0025')
- 行分隔符(Line Separator)- 产生特定于平台的行分隔符
大写转换符(也就是: 'B', 'H', 'S', 'C', 'X', 'E', 'G', 'A', and 'T')在功能上与其小写是一样的,只不过在输出时将结果转换为大写。相当于对结果调用:String.toUpperCase()
。
Conversion | Argument Category | Description |
'b', 'B' | 常规(General) | 如果参数 arg 为 null,则结果为 "false"。如果 arg 是一个 boolean 值或 Boolean,则结果为 String.valueOf() 返回的字符串。否则结果为 "true"。 |
'h', 'H' | 常规(General) | 如果参数 arg 为 null,则结果为 "null"。否则,结果为调用 Integer.toHexString(arg.hashCode()) 得到的结果。 |
's', 'S' | 常规(General) | 如果参数 arg 为 null,则结果为 "null"。如果 arg 实现 Formattable,则调用 arg.formatTo。否则,结果为调用 arg.toString() 得到的结果。 |
'c', 'C' | 字符(Character) |
结果是一个 Unicode 字符 |
'd' | 整数(Integral) |
结果被格式化为十进制整数 |
'o' | 整数(Integral) |
结果被格式化为八进制整数 |
'x', 'X' | 整数(Integral) |
结果被格式化为十六进制整数 |
'e', 'E' | 浮点(Floating Point) |
结果被格式化为用计算机科学记数法表示的十进制数 |
'f' | 浮点(Floating Point) | 结果被格式化为十进制数 |
'g', 'G' | 浮点(Floating Point) | 结果被格式化为十进制数 |
'a', 'A' | 浮点(Floating Point) |
结果被格式化为带有效位数和指数的十六进制浮点数 |
't', 'T' | 日期/时间(Date/Time) |
日期和时间转换字符的前缀,后缀参考下面的时间、日期转换字符 |
'%' | 百分比(Percent) |
结果为字面值 '%' |
'n' | 行分隔符(Line Separator) |
结果为特定于平台的行分隔符 |
下面三张表分别表示日期、时间、通用转换字符后缀。作为上表't','T'转换字符的补充。
5.1 时间转换字符
'H' | 两位数表示的24小时制时间格式,也就是:00-23 |
'I' | 两位数表示的12小时制时间格式,也就是:01-12 |
'k' | 24小时制时间格式,也就是:0-23 |
'l' | 12小时制时间枨,也就是1-12 |
'M' | 两位数表示的分钟格式,也就是:00-59 |
'S' | 两位数表示的秒格式,也就是:00-60(("60" is a special value required to support leap seconds) |
'L' | 三位数表示的毫秒格式,也就是:000-999 |
'N' | 九位数表示的纳秒格式,也就是:000000000-999999999 |
'p' | 与区域(Locale)相关的上下午标志,如:am/pm |
'z' | RFC 822 样式时差设置,如:-0800 |
'Z' | 表示时区的字符串,代替默认时区 |
's' | 从1 January 1970 00:00:00 UTC到现在秒数 |
'Q' | 从1 January 1970 00:00:00 UTC到现在的毫秒数 |
5.2 日期转换字符
'B' | 区域相关的月分名称,如:"January", "February". |
'b' | 区域相关的月分名称缩写,如:"Jan", "Feb". |
'h' | 同'b' |
'A' | 区域相关的星期几,如:"Sunday", "Monday" |
'a' | 区域相关的星期几缩写,如:"Sun", "Mon" |
'C' | 两位表示四位年数除以100的值,也就是:00-99 |
'Y' | 至少四位数字表示的年份 |
'y' | 两位数字表示的年份,也就是:00-99 |
'j' | 一年中的第几天,也就是:001-366 |
'm' | 两位数字表示的月份,也就是:01-12 |
'd' | 两位数字表示的该月第几天,也就是:01-31 |
'e' | 该月第几天,也就是:1-31 |
5.3 日期与时间通用转移字符
'R' | 时间格式化为24小时制,"%tH:%tM" |
'T' | 时间格式化为24小时制,"%tH:%tM:%tS" |
'r' | 时间格式化为12小时制,"%tI:%tM:%tS %Tp" |
'D' | 日期格式化为"%tm/%td/%ty" |
'F' | ISO 8601完整日期格式为"%tY-%tm-%td" |
'c' | 日期时间格式为"%ta %tb %td %tT %tZ %tY",如:"Sun Jul 20 16:17:00 EDT 1969" |
所有在上述日期与时间转换字符中未显式定义为转换字符的字符都将被视为非法字符,被予以保留,以供扩展使用。
三. 示范代码
System.out.format,System.err.printf,String.format都是通用Formatter实现的。
package com.clzhang.sample.thinking; import java.io.*; import java.util.*; import static java.util.Calendar.*; import org.junit.Test; // System.out.format,System.err.printf,String.format都是通用Formatter实现的。 public class JDK15Formatter { @Test public void testFormatter() { Formatter formatter = new Formatter(System.out); // 倒序输出各参数 // %4$表示参数位置;2表示宽度;s表示toString输出。 formatter.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d"); System.out.println(); // -> " d c b a" // 可选区域参数作为第一个参数,可以用来格式化区域相关的格式数字;precision与width参数用来取整与对齐值。 // Locale.CHINA为区域参数;%参数;+表示结果总包括一个符号;10表示宽度(不够补空格);.4表示小数点后位数;f表示结果被格式化为十进制数 formatter.format(Locale.CHINA, "e = %+10.4f", Math.E); System.out.println(); // -> "e = +2,7183" // '('数值符号可以用来代替负数符号'-',组分隔符自动插入。 // %参数;(表示圆括号括起来的是负数;,组分隔符;.2表示小数点后保留两位;f表示结果被格式化为十进制数 double balanceDelta = -6217.58231; formatter.format("Amount gained or lost since last statement: $ %(,.2f", balanceDelta); System.out.println(); // -> "Amount gained or lost since last statement: $ (6,217.58)" // Writes a formatted string to System.out. System.out.format("Local time: %tT", Calendar.getInstance()); System.out.println(); // -> Local time: 14:38:39 // Writes formatted output to System.err. String filename = "C:\notexists\food.txt"; File file = new File(filename); try { if(!file.exists()) throw new IOException("文件不存在!"); } catch (Exception e) { System.err.printf("Unable to open file '%1$s': %2$s", filename, e.getMessage()); System.err.println(); // -> Unable to open file 'C: otexistsfood.txt': 文件不存在! } // Format a string containing a date. Calendar c = new GregorianCalendar(1995, MAY, 23); String s = String.format("Duke's Birthday: %1$tm %1$te,%1$tY", c); System.out.println(s); // -> Duke's Birthday: 05 23,1995 formatter.close(); } }