首页 分享 Java 字符串格式化详解

Java 字符串格式化详解

来源:花匠小妙招 时间:2024-12-07 18:54

Java 字符串格式化详解

版权声明:本文为博主原创文章,未经博主允许不得转载。
微博:厉圣杰
文中如有纰漏,欢迎大家留言指出。

在 Java 的 String 类中,可以使用 format() 方法格式化字符串,该方法有两种重载形式: String.format(String format, Object... args) 和 String.format(Locale locale, String format, Object... args)。两者的唯一区别是前者使用本地语言环境,后者使用指定语言环境
查看源码可以发现,该方法最终调用 java.util.Formatter 类的 format 方法。

public static String format(String format, Object... args) {

return new Formatter().format(format, args).toString();

}

public static String format(Locale l, String format, Object... args) {

return new Formatter(l).format(format, args).toString();

}

所以,掌握了 Formatter 的使用,也就掌握了 String.format 的使用,从此 Java 中格式化字符串再无敌手~ 所以我们这里先来讲解 Formatter 的用法。

参考 Java Api 中关于 Formatter 的使用说明,我们可以发现 format 方法的第一个参数是有固定格式的。其格式如下:

%[argument_index$][flags][width][.precision]conversion

argument_index: 可选,是一个十进制整数,用于表明参数在参数列表中的位置。第一个参数由 "1$" 引用,第二个参数由 "2$" 引用,依此类推。

flags: 可选,用来控制输出格式

width: 可选,是一个正整数,表示输出的最小长度

precision:可选,用来限定输出的精度

conversion:必须,用来表示如何格式化参数的字符

参考文档,可以发现 Java 其实把格式化划分为两大类:常规类型格式化和时间日期格式化,下面我们就先来介绍一下常规类型的格式化。

补充:Java 中对格式化其实还有根据类型来分类的,但这里为了方便讲述,只简单的依据格式化的参数类型来讲述,如果以后有机会,会开一篇更详细的博客。

常规类型格式化

在开始之前,这里先放一段 Api 中提供的示例代码,你可以带着示例中的问题去看接下来的内容,也可以看完之后回来看示例,你看懂了多少呢?

StringBuilder sb = new StringBuilder();

Formatter formatter = new Formatter(sb, Locale.US);

formatter.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d")

formatter.format(Locale.FRANCE, "e = %+10.4f", Math.E);

formatter.format("Amount gained or lost since last statement: $ %(,.2f",

balanceDelta);

System.out.format("Local time: %tT", Calendar.getInstance());

System.err.printf("Unable to open file '%1$s': %2$s",

fileName, exception.getMessage());

Calendar c = new GregorianCalendar(1995, MAY, 23);

String s = String.format("Duke's Birthday: %1$tb %1$te, %1$tY", c);

conversion

从上述内容可以发现,只有 conversion 这个参数是必选的。 conversion 是用来表示如何格式化参数的字符。先来看个例子:

System.out.println(String.format("大家好,我叫:%s","小明"));

%s 是一个占位符,s 是一个转换符,指明将参数格式化为字符串。值得注意的是,占位符代表的格式化类型必须与参数的类型相兼容,否则运行时会抛出异常,如:

java System.out.println(String.format("大家好,我叫:%d","小明"));
运行这段代码,就会抛出如下异常:

Exception in thread "main" java.util.IllegalFormatConversionException: d != java.lang.String at java.util.Formatter$FormatSpecifier.failConversion(Formatter.java:4302) at java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:2793) at java.util.Formatter$FormatSpecifier.print(Formatter.java:2747) at java.util.Formatter.format(Formatter.java:2520) at java.util.Formatter.format(Formatter.java:2455) at java.lang.String.format(String.java:2940)

那么,转换符除了 s ,还有哪些呢?那我们就来看下代码:

```java
private static void formatConversion() {
System.out.println(String.format("'b':将参数格式化为boolean类型输出,'B'的效果相同,但结果中字母为大写。%b", false));
System.out.println(String.format("'h':将参数格式化为散列输出,原理:Integer.toHexString(arg.hashCode()),'H'的效果相同,但结果中字母为大写。%h", "ABC"));
System.out.println(String.format("'s':将参数格式化为字符串输出,如果参数实现了 Formattable接口,则调用 formatTo方法。'S'的效果相同。%s", 16));
System.out.println(String.format("FormatImpl类实现了Formattable接口:%s", new FormatImpl()));
System.out.println(String.format("'c':将参数格式化为Unicode字符,'C'的效果相同。%c", 'A'));
System.out.println(String.format("'d':将参数格式化为十进制整数。%d", 11));
System.out.println(String.format("'o':将参数格式化为八进制整数。%o", 9));
System.out.println(String.format("'x':将参数格式化为十六进制整数。%x", 17));
System.out.println(String.format("'e':将参数格式化为科学计数法的浮点数,'E'的效果相同。%E", 10.000001));
System.out.println(String.format("'f':将参数格式化为十进制浮点数。%f", 10.000001));
System.out.println(String.format("'g':根据具体情况,自动选择用普通表示方式还是科学计数法方式,'G'效果相同。10.01=%g", 10.01));
System.out.println(String.format("'g':根据具体情况,自动选择用普通表示方式还是科学计数法方式,'G'效果相同。10.00000000005=%g", 10.00000000005));
System.out.println(String.format("'a':结果被格式化为带有效位数和指数的十六进制浮点数,'A'效果相同,但结果中字母为大写。%a", 10.1));
System.out.println(String.format("'t':时间日期格式化前缀,会在后面讲述"));
System.out.println(String.format("'%%':输出%%。%%"));
System.out.println(String.format("'n'平台独立的行分隔符。System.getProperty("line.separator")可以取得平台独立的行分隔符,但是用在format中间未免显得过于烦琐了%n已经换行"));
}

private static class FormatImpl implements Formattable {

@Override
public void formatTo(Formatter formatter, int flags, int width, int precision) {
formatter.format("我是Formattable接口的实现类");
}
}
```

输出如下:

'b':将参数格式化为boolean类型输出,'B'的效果相同,但结果中字母为大写。false 'h':将参数格式化为散列输出,原理:Integer.toHexString(arg.hashCode()),'H'的效果相同,但结果中字母为大写。fc42 's':将参数格式化为字符串输出,如果参数实现了 Formattable接口,则调用 formatTo方法。'S'的效果相同。16 FormatImpl类实现了Formattable接口:我是Formattable接口的实现类 'c':将参数格式化为Unicode字符,'C'的效果相同。A 'd':将参数格式化为十进制整数。11 'o':将参数格式化为八进制整数。11 'x':将参数格式化为十六进制整数。11 'e':将参数格式化为科学计数法的浮点数,'E'的效果相同。1.000000E+01 'f':将参数格式化为十进制浮点数。10.000001 'g':根据具体情况,自动选择用普通表示方式还是科学计数法方式,'G'效果相同。10.01=10.0100 'g':根据具体情况,自动选择用普通表示方式还是科学计数法方式,'G'效果相同。10.00000000005=10.0000 'a':结果被格式化为带有效位数和指数的十六进制浮点数,'A'效果相同,但结果中字母为大写。0x1.4333333333333p3 't':时间日期格式化前缀,会在后面讲述 '%':输出%。% 'n':平台独立的行分隔符。System.getProperty("line.separator")可以取得平台独立的行分隔符,但是用在format中间未免显得过于烦琐了 已经换行

补充:

对于浮点转换符 'e' 、'E' 和 'f',精度是小数点分隔符后的位数。如果转换符是 'g' 或 'G',那么精度是舍入计算后所得数值的所有位数。如果转换是 'a' 或 'A',则不必指定精度。对于部分转换符,如 'b' 和 'B' ,两者转换效果是相似的,但是 'B' 会把输出中的字母都转换为大写,其它相似特性的转换符应该还有好多,大家可以去找一下O(∩_∩)O哈哈~ argument_index

还记得前面那个小明的例子嘛?现在我们来改变一下输出,要求输出如下内容:

大家好,我叫:小明,今年:25岁。小明是小小明的爸爸。

看到这个,大家会怎么做呢?也许,你会写成:

System.out.println(String.format("大家好,我叫:%s,今年:%d岁。%s是%s的爸爸。", "小明", 25, "小明", "小小明"));

恩,这样做输出的确没错,但是我们却重复输入参数“小明”。这里,就要用到 argument_index 这个参数。使用 argument_index 可以指定使用第几个参数来替换占位符,一旦使用 argument_index 用于指出参数在参数列表中位置,则所有占位符都要加上,否则会出错。修改之后的代码如下:

System.out.println(String.format("大家好,我叫:%1$s,今年:%2$d岁。%1$s是%3$s的爸爸。", "小明", 25, "小小明"));

补充:对于

System.out.println(String.format("大家好,我叫:%s,今年:%d岁。%s是%s的爸爸。", "小明", 25, "小明", "小小明"));

在 Java 中执行可能没什么问题,但如果把 "大家好,我叫:%s,今年:%d岁。%s是%s的爸爸。" 放到 Android 的 strings.xml 中,则会出现错误,解决办法就是指明每个参数在参数列表中位置。

flags

flags是可选参数,用于控制输出的格式,比如左对齐、金额用逗号隔开。

'-' 在最小宽度内左对齐,不可以与“用0填充”同时使用

'+' 结果总是包括一个符号

' ' 正值前加空格,负值前加负号

'0' 结果将用零来填充

',' 每3位数字之间用“,”分隔(只适用于fgG的转换)

'(' 若参数是负数,则结果中不添加负号而是用圆括号把数字括起来(只适用于eEfgG的转换)

private static void formatFlags() {

System.out.println("'-':在最小宽度内左对齐,不可与"用0填充"同时使用。");

System.out.println(String.format("设置最小宽度为8为,左对齐。%-8d:%-8d:%-8d%n", 1, 22, 99999999));

System.out.println(String.format("'0':结果将用零来填充。设置最小宽度为8,%08d:%08d:%08d", 1, -22, 99999990));

System.out.println(String.format("'+':结果总是包括一个符号。%+d:%+d:%+d", 1, -2, 0));

System.out.println(String.format("' ':正值前加空格,负值前加负号。% d:% d:% d", 1, -2, 0));

System.out.println(String.format("',':每3位数字之间用“,”分隔(只适用于fgG的转换)。%,d:%,d:%,d", 1, 100, 1000));

System.out.println(String.format("'(':若参数是负数,则结果中不添加负号而是用圆括号把数字括起来(只适用于eEfgG的转换)。%(d:%(d", 1, -1));

}

输出如下:

'-':在最小宽度内左对齐,不可与"用0填充"同时使用。

设置最小宽度为8为,左对齐。1 :22 :99999999

'0':结果将用零来填充。设置最小宽度为8,00000001:-0000022:99999990

'+':结果总是包括一个符号。+1:-2:+0

' ':正值前加空格,负值前加负号。 1:-2: 0

',':每3位数字之间用“,”分隔(只适用于fgG的转换)。1:100:1,000

'(':若参数是负数,则结果中不添加负号而是用圆括号把数字括起来(只适用于eEfgG的转换)。1:(1)

width

width是可选参数,用于控制输出的宽度。示例如下:

System.out.println(String.format("设置最小宽度为8,不满8位用0填充:%08d:%08d", 1, -10000000));

输出如下:

设置最小宽度为8,不满8位用0填充:00000001:-10000000

但是 width 的值不能为 0 ,否则会抛出

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)

precision

precision是可选参数,用来限定输出的精度,用于浮点数。示例如下:

private static void formatPrecision() {

System.out.println(String.format("设置精度为2位:%.2f", 1f));

}

输出如下:

设置精度为2位:1.00

值得注意的是,如果对整型数据设置精度,则会抛出如下异常:

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)

时间日期格式化

在平时开发中,经常会碰到要显示时间日期的。以前写过一篇 Android 时间、日期相关类和方法 的博客,里面对 Android 中经常出现的时间日期格式做了总结,但觉得还是过于繁琐,这次总结 Java 中格式化输出,没想到 Java 中已经包含了如此多关于时间的转换符,完全能应对日常开发需要,而且不用复杂的计算。

Java 中时间日期格式化的转换符可以分为三类,分别是:时间格式化转换符、日期格式化转换符、时间日期格式化转换符。相比于日期和时间日期格式化转换符,时间格式化转换符就相对多一点。

时间日期格式化字符串的格式如下:

%[argument_index$][flags][width]conversion

相对于普通的格式,时间日期格式化少了 precision ,而 conversion 是由两个字符组成,且第一个字符固定为 t 或 T 。

网上部分博文是贴了转换符说明的表格,但是写完代码之后突然发现,Java 格式化输出这部分内容,看代码和输出其实比看表格更直观,谁让我们是程序员呢?下面就用代码来讲述一下时间日期格式化转换符的三种类别。

格式化时间

示例代码如下:

private static void formatTime() {

System.out.println("这是格式化时间相关的,具体输出跟你执行代码时间有关");

Calendar calendar = Calendar.getInstance();

System.out.println(String.format("'H':2位数24小时制,不足两位前面补0:%tH(范围:00-23)", calendar));

System.out.println(String.format("'I':2位数12小时制,不足两位前面补0:%tI(范围:01-12)", calendar));

System.out.println(String.format("'k':24小时制,不足两位不补0:%tk(范围:0-23)", calendar));

System.out.println(String.format("'l':12小时制,不足两位不补0:%tl(范围:1-12)", calendar));

System.out.println(String.format("'M':2位数的分钟,不足两位前面补0:%tM(范围:00-59)", calendar));

System.out.println(String.format("'S':分钟中的秒,2位数,不足两位前面补0,60是支持闰秒的一个特殊值:%tS(范围:00-60)", calendar));

System.out.println(String.format("'L':3位数的毫秒,不足三位前面补0:%tL(范围:000-999)", calendar));

System.out.println(String.format("'N':9位数的微秒,不足九位前面补0:%tN(范围:000000000-999999999)", calendar));

System.out.println(String.format("'p':输出本地化的上午下午,例如,Locale.US为am或pm,Locale.CHINA为上午或下午", calendar));

System.out.println(String.format(Locale.US, "Local.US=%tp", calendar));

System.out.println(String.format(Locale.CHINA, "Local.CHINA=%tp", calendar));

System.out.println();

System.out.println(String.format("'z':时区:%tz", calendar));

System.out.println(String.format("'Z':时区缩写字符串:%tZ", calendar));

System.out.println(String.format("'s':从1970-1-1 00:00到现在所经历的秒数:%ts", calendar));

System.out.println(String.format("'Q':从1970-1-1 00:00到现在所经历的豪秒数:%tQ", calendar));

}

输出结果如下:

这是格式化时间相关的,具体输出跟你执行代码时间有关

Local.US=pm

Local.CHINA=下午

格式化日期

示例代码如下:

private static void formatDate() {

System.out.println("-----------------------我是微笑的分割线O(∩_∩)O哈哈~-----------------------------");

System.out.println("这是格式化时间相关的,具体输出跟你执行代码时间有关");

Calendar calendar = Calendar.getInstance();

System.out.println(String.format("'B':本地化显示月份字符串,如:January、February"));

System.out.println(String.format("'b':本地化显示月份字符串的缩写,如:Jan、Feb"));

System.out.println(String.format("'h':本地化显示月份字符串的缩写,效果同'b'"));

System.out.println(String.format(Locale.US, "Locale.US 月份=%1$tB,缩写=%1$tb", calendar));

System.out.println(String.format(Locale.CHINA, "Locale.CHINA 月份=%1$tB,缩写=%1$tb", calendar));

System.out.println(String.format("'A':本地化显示星期几字符串,如:Sunday、Monday"));

System.out.println(String.format("'a':本地化显示星期几字符串的缩写,如:Sun、Mon"));

System.out.println(String.format(Locale.US, "Locale.US 星期几=%1$tA,缩写=%1$ta", calendar));

System.out.println(String.format(Locale.CHINA, "Locale.CHINA 星期几=%1$tA,缩写=%1$ta", calendar));

System.out.println(String.format("'C':年份除以100的结果,显示两位数,不足两位前面补0:%tC(范围:00-99)", calendar));

System.out.println(String.format("'Y':显示四位数的年份,格利高里历,即公历。不足四位前面补0:%tY", calendar));

System.out.println(String.format("'y':显示年份的后两位:%ty(范围:00-99)", calendar));

System.out.println(String.format("'j':显示当前公历年的天数:第%tj天(范围:001-366)", calendar));

System.out.println(String.format("'m':显示当前月份:%tm月(范围:01-13?怎么会有13个月?)", calendar));

System.out.println(String.format("'d':显示是当前月的第几天,不足两位前面补0:%1$tm月第%1$td天(范围:01-31)", calendar));

System.out.println(String.format("'e':显示是当前月的第几天:%1$tm月第%1$te天(范围:1-31)", calendar));

}

输出结果如下:

这是格式化时间相关的,具体输出跟你执行代码时间有关

'B':本地化显示月份字符串,如:January、February

'b':本地化显示月份字符串的缩写,如:Jan、Feb

'h':本地化显示月份字符串的缩写,效果同'b'

Locale.US 月份=October,缩写=Oct

Locale.CHINA 月份=十月,缩写=十月

'A':本地化显示星期几字符串,如:Sunday、Monday

'a':本地化显示星期几字符串的缩写,如:Sun、Mon

Locale.US 星期几=Wednesday,缩写=Wed

Locale.CHINA 星期几=星期三,缩写=星期三

'C':年份除以100的结果,显示两位数,不足两位前面补0:20(范围:00-99)

'Y':显示四位数的年份,格利高里历,即公历。不足四位前面补0:2016

'y':显示年份的后两位:16(范围:00-99)

'j':显示当前公历年的天数:第293天(范围:001-366)

'm':显示当前月份:10月(范围:01-13?怎么会有13个月?)

'd':显示是当前月的第几天,不足两位前面补0:10月第19天(范围:01-31)

'e':显示是当前月的第几天:10月第19天(范围:1-31)

补充:在文档中发现一个略微奇怪的问题,就是 m 转换符,文档上的大意是:该转换符用于显示当前月是这一年的第几个月,文档里给的范围竟然是01-13,一年怎么会有13个月,告诉我不是我理解错了?

格式化时间日期

示例代码如下:

private static void formatTimeAndDate() {

System.out.println("-----------------------我是微笑的分割线O(∩_∩)O哈哈~-----------------------------");

System.out.println("这是格式化时间相关的,具体输出跟你执行代码时间有关");

Calendar calendar = Calendar.getInstance();

System.out.println(String.format("'R':将时间格式化为:HH:MM(24小时制)。输出:%tR", calendar));

System.out.println(String.format("'T':将时间格式化为:HH:MM:SS(24小时制)。输出:%tT", calendar));

System.out.println(String.format("'r':将时间格式化为:09:23:15 下午,跟设置的语言地区有关。输出:%tr", calendar));

System.out.println(String.format("'D':将时间格式化为:10/19/16。输出:%tD", calendar));

System.out.println(String.format("'F':将时间格式化为:2016-10-19。输出:%tF", calendar));

System.out.println(String.format("'c':将时间格式化为"Sun Jul 20 16:17:00 EDT 1969"。输出:%tc", calendar));

}

输出结果如下:

-----------------------我是微笑的分割线O(∩_∩)O哈哈~-----------------------------

这是格式化时间相关的,具体输出跟你执行代码时间有关

'R':将时间格式化为:HH:MM(24小时制)。输出:21:26

'T':将时间格式化为:HH:MM:SS(24小时制)。输出:21:26:44

'r':将时间格式化为:09:23:15 下午,跟设置的语言地区有关。输出:09:26:44 下午

'D':将时间格式化为:10/19/16。输出:10/19/16

'F':将时间格式化为:2016-10-19。输出:2016-10-19

'c':将时间格式化为"Sun Jul 20 16:17:00 EDT 1969"。输出:星期三 十月 19 21:26:44 CST 2016

总结

系统的介绍了 Java 中格式化字符串的方式及相关的转换符。不过只是对此有印象,方便日后温习,还得在日常中不断加强使用。

虽然 Formatter 中提供了很多关于时间日期的转换符,而且能满足日常的绝大部分使用,但还是存在限制,所以对于有特殊要求的时间格式,还是要学会自己定制。

String.format 这个方法很实用,但如果是大批量进行字符串格式化,就需要考虑到性能方面的问题,因为每次调用 format() 方法都会 new 一个 Formatter 对象。而在 Java 中频繁创建对象需要大量时间,而且还要花时间对这些对象进行垃圾回收和处理。最好的办法就是自己把 DateFormat 、 NumberFormat 、 MessageFormat 这些类封装成静态工具。

相关知识

Python变量和数据类型Number、String、转义字符、字符串格式化
1.字符串详解
JAVA 日期格式化
Java中Date日期以及日期格式化
Python 字符串格式化输出的3种方式
PrintWriter打印流示例
KMP 字符串匹配 SDNU 1100 字符串查找 HDU 2087 剪花布条
Sublime Text 2 有像UE那样,查找时列出包含字符串的行吗
校园内有个圆形花坛,围绕花坛有n个连续的格子,LCZ开始在第1个格子开始向前跳,第i次跳跃会越过ci个格子落到一个新格子上。(注意,花坛是圆形的,第n个格子与第1个格子相连) 已知LCZ一共跳了m次,问花坛的所有格子中有多少个格子没到达过。Python代码
字符串

网址: Java 字符串格式化详解 https://www.huajiangbk.com/newsview948935.html

所属分类:花卉
上一篇: ECSHOP支付宝手机支付接口插
下一篇: 关于最新最热的guns框架的to

推荐分享