Java字符串格式化
Posted 刘润森!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java字符串格式化相关的知识,希望对你有一定的参考价值。
前言
本文详细描述了字符串格式化的语法与使用案例。
版本约定
- JDK 版本:1.8.0_231
- Java SE API Documentation:https://docs.oracle.com/javase/8/docs/api/
正文
字符串格式化包含两个部分,格式字符串和参数列表。格式字符串是一个 String 串,它可以包含固定文本以及一个或多个嵌入的格式说明符。参数列表是一系列需要填充到格式字符串中的参数值。
官方文档参考:
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html
官方文档看着人晕乎晕乎的,我结合网上一些文章,稍作整理,以更加便于理解的布局方式呈现。文中如果存在歧义的地方,你也可以参考官方文档。
在 Java 的 String 类中,提供了 format() 方法格式化字符串,该方法有两种重载形式。
- String.format(String format, Object… args)
- String.format(Locale locale, String format, Object… args)
两者的唯一区别是前者使用本地语言环境,后者使用指定语言环境。
规类型格式化
转换可以支持的参数类型如下所示:
- General:可以应用于任何参数类型;
- Character:可以应用于代表 Unicode 字符的基本类型:char,Character,byte,Byte,short 和 Short,当Character.isValidCodePoint(int) 返回 true 时,这种转换也可以应用于 int 和 Integer 类型。
- Numeric
a. Integral:可以应用于 Java 整数类型:byte,Byte,short,Short,int,Integer,long,Long 和 BigInteger(但不适用 char 和 Character 类型);
b. Floating Point:可以应用于 Java 浮点类型:float,Float,double,Double 和 BigDecimal; - Date/Time:可以应用于能够对日期或时间进行编码的 Java 类型:long、Long、Calendar、Date 和TemporalAccessor;
- Percent:产生一个的 “%”(‘\\u0025’);
- Line Separator:产生平台专用的行分隔符。
转换符 | 参数类别 | 说明 |
---|---|---|
‘b’, ‘B’ | general | 如果参数 arg 为 null,则结果为 false;如果 arg 是 boolean 或者 Boolean,则会执行String.valueOf(arg) 方法,返回结果;否则,结果为 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 | 其结果被格式化为一个十六进制的浮点数,带有一个显数和一个指数。BigDecimal 类型不支持这种转换,尽管后者属于浮点参数类别。 |
‘t’, ‘T’ | date/time | 日期和时间转换字符的前缀。 |
‘%’ | percent | 结果是文字 ‘%’。‘n’ line separator 结果是特定于平台的行分隔符。 |
搭配例子来理解上面的语法内容,这里不会覆盖所有的转换符,只介绍几个常用的。有特殊需求的,大家结合上面的语法,自己实现例子,在使用的时候,多写写 Unit Test,可以避免很多错误。
比如转换符 ‘b’, ‘B’,一般是用来处理 Boolean 类型的,测试几个例子,看看是否和上面的语法说明能对应上。
public static void main(String[] args)
System.out.println(String.format("转换符是 b,参数是 null,格式化结果:%b", null));
System.out.println(String.format("转换符是 b,参数是 false,格式化结果:%b", false));
System.out.println(String.format("转换符是 b,参数是 new Boolean(true),格式化结果:%b", new Boolean(true)));
System.out.println(String.format("转换符是 b,参数是空字符串,格式化结果:%b", ""));
System.out.println(String.format("转换符是 b,参数是 1,格式化结果:%b", 1));
运行程序,输出:
转换符是 b,参数是 null,格式化结果:false
转换符是 b,参数是 false,格式化结果:false
转换符是 b,参数是 new Boolean(true),格式化结果:true
转换符是 b,参数是空字符串,格式化结果:true
转换符是 b,参数是 1,格式化结果:true
确实符合上面的语法描述,特别是最后一条,对于其他类型的参数,格式化后的结果都是 true。
接下来,我们测一下转换符 ‘s’, ‘S’,这个转换符一般用来处理字符串类型,同样的对应上面的语法说明,我们测试几个例子。因为我这里没有实现了 Formattable 接口的类,所以这个就不测试了。
public static void main(String[] args)
System.out.println(String.format("转换符是 s,参数是 null,格式化结果:%s", null));
System.out.println(String.format("转换符是 s,参数是 \\"测试\\",格式化结果:%s", "测试"));
运行程序,输出:
转换符是 s,参数是 null,格式化结果:null
转换符是 s,参数是 "测试",格式化结果:测试
其他的转换符,大家有需要的,在使用前,自己写几个例子测试一下。
Argument Index
这个比较好理解,表示参数在参数列表中的位置。参教索引值从 1 开始,而不是从 0 开始,比如,1$ 表示第一个参数,2$ 表示第二个参数,以此类推,举个例子。
public static void main(String[] args)
Calendar c = Calendar.getInstance();
String s = String.format("张三的生日是:%1$tY-%1$tm-%1$te", c);
System.out.println(s);
运行程序,输出:
张三的生日是:2021-05-25
这里格式化字符串中取的都是第一个参数。
需要注意的是,一旦我们在格式化字符串中使用了 argument_index,则所有占位符都要加上。
Flags
flags 也是可选参数,用于控制输出的格式,比如左对齐、金额用逗号隔开等。
结合例子来理解,当然下面的例子没有覆盖所有的情况,所以如果有同学有更好的案例,更好的使用场景,可以告诉我,我一并整理进来,特别是我对 ‘#’ 这个 flags 不是特别理解,官网描述的也不清晰。
public static void main(String[] args)
System.out.println(String.format("flags 是 -,参数是 1,格式化结果:%-8d.", 1));
System.out.println(String.format("flags 是 #,参数是 99,格式化结果:%#x.", 99));
System.out.println(String.format("flags 是 #,参数是 99,格式化结果:%#o.", 99));
System.out.println(String.format("flags 没有,参数是 10.0,格式化结果:%f.", 10.0));
System.out.println(String.format("flags 是 #,参数是 10.0,格式化结果:%#f.", 10.0));
System.out.println(String.format("flags 没有,参数是 10.1234564,格式化结果:%f.", 10.1234564));
System.out.println(String.format("flags 是 #,参数是 10.1234564,格式化结果:%#f.", 10.1234564));
System.out.println(String.format("flags 没有,参数是 10.1234565,格式化结果:%f.", 10.1234565));
System.out.println(String.format("flags 是 #,参数是 10.1234565,格式化结果:%#f.", 10.1234565));
System.out.println(String.format("flags 是 +,参数是 10,格式化结果:%+d.", 10));
System.out.println(String.format("flags 是 +,参数是 -10,格式化结果:%+d.", -10));
System.out.println(String.format("flags 是 +,参数是 10,格式化结果:% 6d.", 10));
System.out.println(String.format("flags 是 +,参数是 -10,格式化结果:% 6d.", -10));
System.out.println(String.format("flags 是 +,参数是 10,格式化结果:%06d.", 10));
System.out.println(String.format("flags 是 +,参数是 -10,格式化结果:%06d.", -10));
System.out.println(String.format("flags 是 +,参数是 9999999,格式化结果:%,6d.", 9999999));
System.out.println(String.format("flags 是 +,参数是 -9999999,格式化结果:%,6d.", -9999999));
System.out.println(String.format("flags 是 +,参数是 -9999999.1234567,格式化结果:%,6f.", -9999999.1234567));
System.out.println(String.format("flags 是 (,参数是 -10,格式化结果:%(d.", -10));
运行程序,输出:
flags 是 -,参数是 1,格式化结果:1 .
flags 是 #,参数是 99,格式化结果:0x63.
flags 是 #,参数是 99,格式化结果:0143.
flags 没有,参数是 10.0,格式化结果:10.000000.
flags 是 #,参数是 10.0,格式化结果:10.000000.
flags 没有,参数是 10.1234564,格式化结果:10.123456.
flags 是 #,参数是 10.1234564,格式化结果:10.123456.
flags 没有,参数是 10.1234565,格式化结果:10.123457.
flags 是 #,参数是 10.1234565,格式化结果:10.123457.
flags 是 +,参数是 10,格式化结果:+10.
flags 是 +,参数是 -10,格式化结果:-10.
flags 是 +,参数是 10,格式化结果: 10.
flags 是 +,参数是 -10,格式化结果: -10.
flags 是 +,参数是 10,格式化结果:000010.
flags 是 +,参数是 -10,格式化结果:-00010.
flags 是 +,参数是 9999999,格式化结果:9,999,999.
flags 是 +,参数是 -9999999,格式化结果:-9,999,999.
flags 是 +,参数是 -9999999.1234567,格式化结果:-9,999,999.123457.
flags 是 (,参数是 -10,格式化结果:(10).
Width
也是可选参数,关于这个参数,上面的例子中也有用到,就是用于控制输出的宽度。
public static void main(String[] args)
System.out.println(String.format("width 是 6,不满 6 位用 0 填充,参数是 10,格式化结果:%06d.", 10));
运行程序,输出:
width 是 6,不满 6 位用 0 填充,参数是 10,格式化结果:000010.
需要注意的是 width 不能设置为 0,否则可能会报错。比如上面的例子,我们把 width 设置为 0:
public static void main(String[] args)
System.out.println(String.format("width 是 0,不满 6 位用 0 填充,参数是 10,格式化结果:%00d.", 10));
运行程序,输出:
Exception in thread "main" java.util.DuplicateFormatFlagsException: Flags = '0'
at java.util.Formatter$Flags.parse(Formatter.java:4443)
at java.util.Formatter$FormatSpecifier.flags(Formatter.java:2640)
at java.util.Formatter$FormatSpecifier.<init>(Formatter.java:2709)
at java.util.Formatter.parse(Formatter.java:2560)
at java.util.Formatter.format(Formatter.java:2501)
at java.util.Formatter.format(Formatter.java:2455)
at java.lang.String.format(String.java:2940)
at Test9.Test5.main(Test5.java:10)
Precision
也是可选参数,用来限定输出的精度,用于格式化浮点数类型。
public static void main(String[] args)
System.out.println(String.format("precision 是 2,设置精度为 2,参数是 9.1234,格式化结果:%.2f.", 9.1234));
运行程序,输出:
precision 是 2,设置精度为 2,参数是 9.1234,格式化结果:9.12.
需要注意的是不能对整型类型参数设置精度,否则会抛出异常。
public static void main(String[] args)
System.out.println(String.format("precision 是 2,设置精度为 2,参数是 10,格式化结果:%.2d.", 10));
运行程序,输出:
Exception in thread "main" java.util.IllegalFormatPrecisionException: 2
at java.util.Formatter$FormatSpecifier.checkInteger(Formatter.java:2984)
at java.util.Formatter$FormatSpecifier.<init>(Formatter.java:2729)
at java.util.Formatter.parse(Formatter.java:2560)
at java.util.Formatter.format(Formatter.java:2501)
at java.util.Formatter.format(Formatter.java:2455)
at java.lang.String.format(String.java:2940)
at Test9.Test5.main(Test5.java:10)
日期时间类型格式化
String.format() 方法提供了很多关于日期时间类型的格式化,极大的方便了日常的开发。
在语法部分,我们知道日期时间格式化相比常规类型格式化少了 precision(精度),同时 conversion 是由两个字符组成,且第一个字符固定为 ‘t’ 或 ‘T’。
那么第二个字符,日期时间格式化的转换符可以分为如下三类:
- 时间格式化转换符;
- 日期格式化转换符;
- 日期时间格式化转换符。
时间格式化转换符
下表是用于格式化时间的转换字符:
转换符 | 说明 | 示例 |
---|---|---|
H | 2 位数字 24 时制的小时,不足 2 位前面补 0。 | 15 |
I | 2 位数字 12 时制的小时,不足 2 位前面补 0。 | 03 |
k | 2 位数字 24 时制的小时,前面不补 0。 | 15 |
l | 2 位数字 12 时制的小时,前面不补 0。 | 3 |
M | 2 位数字的分钟,不足 2 位前面补 0。 | 03 |
S | 2 位数字的秒,不足 2 位前面补 0。 | 09 |
L | 3 位数字的毫秒,不足 3 位前面补 0。 | 015 |
N | 9 位数字的毫秒,不足 9 位前面补 0。 | 562000000 |
p | 小写字母的上午或下午标记。 | 中:下午 英:pm |
z | 相对于 GMT 的 RFC822 时区的偏移量。 | +0800 |
Z | 时区缩写字符串。 | CST |
s | 1970-1-1 00:00:00 到现在所经过的秒数。 | 1193468128 |
Q | 1970-1-1 00:00:00 到现在所经过的毫秒数。 | 1193468128984 |
结合例子来理解上面的语法。
public static void main(String[] args)
Date date = new Date();
System.out.println(String.format("2 位数字 24 时制的小时,不足 2 位前面补 0:%tH", date));
System.out.println(String.format("2 位数字 12 时制的小时,不足 2 位前面补 0:%tI", date));
System.out.println(String.format("2 位数字 24 时制的小时,前面不补 0:%tk", date));
System.out.println(String.format("2 位数字 12 时制的小时,前面不补 0:%tl", date));
System.out.println(String.format("2 位数字的分钟,不足 2 位前面补 0:%tM", date));
System.out.println(String.format("2 位数字的秒,不足 2 位前面补 0:%tS", date));
System.out.println(String.format("3 位数字的毫秒,不足 3 位前面补 0:%tL", date));
System.out.println(String.format("9 位数字的毫秒,不足 9 位前面补 0:%tN", date));
System.out.println(String.format(Locale.US, "小写字母的上午或下午标记(英):%tp", date));
System.out.println(String.format("小写字母的上午或下午标记(中):%tp", date));
System.out.println(String.format("相对于 GMT 的 RFC822 时区的偏移量:%tz", date));
System.out.println(String.format("时区缩写字符串:%tZ", date));
System.out.println(String.format("1970-1-1 00:00:00 到现在所经过的秒数:%ts", date));
System.out.println(String.format("1970-1-1 00:00:00 到现在所经过的毫秒数:%tQ", date));
运行程序,输出:
2 位数字 24 时制的小时,不足 2 位前面补 0:23
2 位数字 12 时制的小时,不足 2 位前面补 0:11
2 位数字 24 时制的小时,前面不补 0:23
2 位数字 12 时制的小时,前面不补 0:11
2 位数字的分钟,不足 2 位前面补 0:31
2 位数字的秒,不足 2 位前面补 0:30
3 位数字的毫秒,不足 3 位前面补 0:421
9 位数字的毫秒,不足 9 位前面补 0:421000000
小写字母的上午或下午标记(英):pm
小写字母的上午或下午标记(中):pm
相对于 GMT 的 RFC822 时区的偏移量:+0800
时区缩写字符串:CST
1970-1-1 00:00:00 到现在所经过的秒数:1621956690
1970-1-1 00:00:00 到现在所经过的毫秒数:1621956690421
日期格式化转换符
下表是用于格式化日期的转换字符:
转换符 | 说明 | 示例 |
---|---|---|
‘b’ or ‘h’ | 月份简称。 | 中:十月 英:Oct |
‘B’ | 月份全称。 | 中:十月英:October |
‘a’ | 星期的简称。 | 中:星期六 英:Sat |
‘A’ | 星期的全称。 | 中:星期六 英:Saturday |
‘C’ | 年的前两位数字,不足两位前面补 0。 | 20 |
‘y’ | 年的后两位数字,不足两位前面补 0。 | 21 |
‘Y’ | 4 位数字的年份,不足 4 位前面补 0。 | 2021 |
‘j’ | 一年中的天数,即年的第几天。 | 300 |
‘m’ | 两位数字的月份,不足两位前面补 0。 | 01 |
‘d’ | 月份的日,两位数字,不足两位前面补 0。 | 05 |
‘e’ | 月份的日,前面不补 0。 | 5 |
结合例子来理解上面的语法。
public static void main(String[] args)
Date date = new Date();
System.out.println(String.format(Locale.US, "月份简称,英文:%tb", date));
System.out.println(String.format(Locale.US, "月份简称,英文:%th", date));
System.out.println(String.format(Locale.SIMPLIFIED_CHINESE, "月份简称,中文:%tb", date));
System.out.println(String.format(Locale.SIMPLIFIED_CHINESE, "月份简称,中文:%th", date));
System.out.println(String.format(Locale.US, "月份全称,英文:%tB", date));
System.out.println(String.format(Locale.SIMPLIFIED_CHINESE, "月份全称,中文:%tB", date));
System.out.println(String.format(Locale.US, "星期的简称,英文:%ta", date));
System.out.println(String.format(Locale.SIMPLIFIED_CHINESE, "星期的简称,英文:%ta", date));
System.out.println(String.format(Locale.US, "星期的全称,英文:%tA", date));
System.out.println(String.format(Locale.SIMPLIFIED_CHINESE, "星期的全称,英文:%tA", date));
System.out.println(String.format("年的前两位数字,不足两位前面补 0:%tC", date));
System.out.println(String.format("年的后两位数字,不足两位前面补 0:%ty", date));
System.out.println(String.format("4 位数字的年份,不足 4 位前面补 0:%tY", date));
System.out.println(String.format("一年中的天数,即年的第几天:%tj", date));
System.out.println(String.format("两位数字的月份,不足两位前面补 0:%tm", date));
System.out.println(String.format("月份的日,两位数字,不足两位前面补 0:%td", date));
System.out.println(String.format("月份的日,前面不补 0:%te", date));
运行程序,输出:
月份简称,英文:May
月份简称,英文:May
月份简称,中文:五月
月份简称,中文:五月
月份全称,英文:May
月份全称,中文:五月
星期的简称,英文:Wed
星期的简称,英文:星期三
星期的全称,英文:Wednesday
星期的全称,英文:星期三
年的前两位数字,不足两位前面补 0:20
年的后两位数字,不足两位前面补 0:21
4 位数字的年份,不足 4 位前面补 0:2021
一年中的天数,即年的第几天:146
两位数字的月份,不足两位前面补 0:05
月份的日,两位数字,不足两位前面补 0:26
月份的日,前面不补 0:26
日期时间格式化转换符
下表是用于格式化日期时间的转换字符:
转换符 | 说明 | 示例 以上是关于Java字符串格式化的主要内容,如果未能解决你的问题,请参考以下文章 |
---|