3.7.2 格式化输出
可以使用System.out.printf(x)将数值x输出到控制台上。这条命令将以x对应的数据类型所允许的最大非0数字位数打印输出x。例如:
double x = 10000.0 / 3.0;
System.out.printf(x);
打印
3333.3333333333335
如果希望显示美元、美分等符号,则有可能会出现问题。
在早期的Java中,格式化数值曾引起过一些争议。庆幸的是,Java SE 5.0沿用了C语言库函数中的printf方法。例如,调用
System.out.printf("%8.2f", x);
可以用8个字符的宽度和小数点后两个字符的精度打印x。也就是说,打印输出一个空格和7个字符,如下所示:
3333.33
在printf中,可以使用多个参数,例如:
System.out.printf("Hello, %s. Next year, you'll be %d", name, age);
每一个以%字符开始的格式说明符都用相应的参数替换。格式说明符尾部的转换符将指示被格式化的数值类型:f表示浮点数,s表示字符串,d表示十进制整数。表3-5列出了所有转换符。
另外,还可以给出控制格式化输出的各种标志。表3-6列出了所有的标志。例如,逗号标志增加了分组的分隔符。即
System.out.printf("%,.2f", 10000.0/3.0);
打印
3,333.33
可以使用多个标志,例如,"%,(.2f"使用分组的分隔符并将负数括在括号内。
注释:可以使用s转换符格式化任意的对象。对于任意实现了Formattable接口的对象都将调用formatTo方法;否则将调用toString方法,它可以将对象转换为字符串。在第5章中将讨论toString方法,在第6章中将讨论接口。
可以使用静态的String.format方法创建一个格式化的字符串,而不打印输出:
String message = String.format("Hello, %s. Next year, you'll be %d", name, age);
尽管在第4章之前,没有对Date类型进行过详细地描述,但基于完整性的考虑,还是简略地介绍一下printf方法中日期与时间的格式化选项。在这里,使用以t开始,以表3-7中任意字母结束的两个字母格式。例如,
System.out.printf("%tc", new Date());
这条语句将用下面的格式打印当前的日期和时间:
Mon Feb 09 18:05:19 PST 2004
从表3-7可以看到,某些格式只给出了指定日期的部分信息。例如,只有日期或月份。如果需要多次对日期操作才能实现对每一部分进行格式化的目的就太笨拙了。为此,可以采用一个格式化的字符串指出要被格式化的参数索引。索引必须紧跟在%后面,并以$终止。例如,
System.out.printf("%1$s %2$tB %2$te %2$tY", "Due date:",new Date());
打印
Due date: February 9, 2004
还可以选择使用<标志。它指示前面格式说明中的参数将被再次使用。也就是说,下列语句将产生与前面语句同样的输出结果。
System.out.printf("%s %tB %<te %<tY", "Due date:",new Date());
提示:参数索引值从1开始,而不是从0开始,%1$...对第1个参数格式化。这就避免了与0标志混淆。
现在,已经了解了printf方法的所有特性。图3-6给出了格式说明符的语法图。
注释:许多格式化规则是本地环境特有的。例如,在德国,十进制的分隔符是句号而不是逗号,Monday被格式化为Montag。在卷II中将介绍如何控制应用程序与地域有关的行为。
3.7.3 文件输入与输出
要想对文件进行读取,就需要一个用File对象构造一个Scanner对象,如下所示:
Scanner in = new Scanner(Paths.get("myfile.txt"));
如果文件名中包含反斜杠符合,就要记住在每个反斜杠之前再加一个额外的反斜杠:"c:\\mydirectory\\myfile.txt"。
现在,就可以利用前面介绍的任何一个Scanner方法对文件进行读取。
要想写入文件,就需要构造一个PrintWriter对象。在构造器中,只需要提供文件名:
PrintWriter printWriter = new PrintWriter("myfile.txt");
如果文件不存在,创建该文件。可以像输出到System.out一样使用print、println以及printf命令。
警告:可以构造一个带有字符串参数的Scanner,但这个Scanner将字符串解释为数据,而不是文件名。例如,如果调用:
Scanner in = new Scanner("myfile.txt");//ERROR?
这个scanner会将参数作为包含10个字符的数据:‘m’,‘y’,‘f’等等。在这个示例中所显示的并不是人们所期望的效果。
注释:当指定一个相对文件名时,例如,“myfile.txt”,“mydirectory/myfile.txt”或“../myfile.txt”,文件位于Java虚拟机启动路径的相对位置。如果在命令行方式下用下列命令启动程序:
java MyProg
启动路径就是命令解释器的当前路径。然而,如果使用集成开发环境,那么启动路径将由IDE控制。可以使用下面的调用方式找到路径的位置:
String dir = System.getProperty("user.dir");
如果觉得定位文件比较烦恼,则可以考虑使用绝对路径,例如:“c:\\mydirectory\\myfile.txt”或者“/home/me/mydirectory/myfile.txt”。
正如读者所看到的,访问文件与使用System.in和System.out一样容易。要记住一点:如果用一个不存在的文件构造一个Scanner,或者用一个不能被创建的文件名构造一个PrintWriter,那么就会发生异常。Java编译器认为这些异常比“被零整除”异常更严重。在第11章中,将会学习各种处理异常的方式。现在,应该告知编译器:已经知道有可能出现“找不到文件”的异常。这需要在main方法中用throws子句标记,如下所示:
public static void main(String[] args) throws FileNotFoundException {
Scanner printWriter = new Scanner(new File("myfile.txt"));
}
现在读者已经学习了如何读写包含文本数据的文件。对于更加高级的技术,例如,处理不同的字符编码、处理二进制数据、读取目录以及编写压缩文件,请参看卷II第1章。
注释:当采用命令行方式启动一个程序时,可以利用重定向将任意文件捆绑到System.in和System.out:
java MyProg < myfile.txt > output.txt
这样,就不必担心处理FileNotFoundException异常了
API java.util.Scanner 5.0
- Scanner(File f)
构造一个从给定文件读取数据的Scanner。
- Scanner(String data)
构造一个从给定字符串读取数据的Scanner。
API java.io.PrintWriter 1.1
- PrintWriter(String fileName)
构造一个将数据写入文件的PrintWriter。文件名由参数指定。
API java.nio.file.Paths 7
- static Path get(String pathname)
根据给定的路径名构造一个Path。