文本输入与输出
保存数据时,可以选择二进制或文本格式。整数1234存储成二进制时,写成由字节00 00 04 D2构成的序列(十六进制表示法。)存储文本格式时,被存成字符串"1234"。
尽管二进制格式的I/O高速且高效,但是不宜人来阅读。
存储文本字符串时,需要考虑字符编码方式。Java内部使用UTF-16编码方式。
字符串“1234”UTF-16编码为 00 31 00 32 00 33 00 34
UTF-8编码:4A 6F 73 C3 A9,其中并没有用于前3个字母的任何0字节,而字符é占用两个字节。
OutputStreamWriter类使用选定的字符编码方式,把Unicode码元的输出流转换为字节流。
InputStreamReader类将包含字节(用某种字符编码方式表示的字符)的输入流转换为可以产生Unicode码元的读入器。
将一个输入读入器从控制台读入键盘敲击信息,并将其转换为Unicode。
代码语言:java复制// 默认使用主机系统所使用的默认字符编码方式
Reader in = new InputStreamReader(System.in);
// 可以指定编码方式
Reader in = new InputStreamReader(System.in, StandardCharsets.UTF_8);
如何写出文本输出
PrintWriter:拥有以文本格式打印字符串和数字的方法。
代码语言:java复制PrintWriter out = new PrintWriter("employee.txt", "UTF-8");
// 等价于
PrintWriter out =
new PrintWriter(new FileOutputStream("employee.txt"), "UTF-8");
输出到打印写出器,需要使用与使用System.out时相同的print、println和printf方法。
输出到写出器out,之后这些字符将会被转换成字节并最终写入employee.txt中。
println方法在行中添加了目标系统来说恰当的行结束符(Windows系统是"rn",UNIX系统是"n"),也就是通过调用System.getProperty("line.separator")而获得的字符串。
如果写出器设置为自动冲刷模式,那么只要println被调用,缓冲区的所有字符都被发送到它们的目的地(打印写出器总是带缓冲区的)。 默认情况下,自动冲刷机制是禁用的,可以通过PrintWriter(Writer out, Boolean autoFlush)来启用或禁用自动冲刷机制:
代码语言:java复制PrintWriter out = new PrintWriter(
new OutputStreamWriter(new FileOutputStream("employee.txt"), "UTF-8"),
true
);
print方法不抛出异常,可以调用checkError方法来查看输出流是否出现了某些错误。
如何读入文本输入
Scanner类:最简单的文本处理方式。
处理短小的文本文件到字符串中:String content = new String(Files.readAllBytes(path), charset);
文件一行行读入:List<String> lines = Files.readAllLines(path, charset);
如果文件太大,可以惰性处理Stream<String>对象:
代码语言:java复制try(Stream<String> lines = Files.lines(path, charset)){
...
}
早期的Java版本,处理文本的唯一方式就是通过BufferedReader类。
readLine:产生一行文本,无法获取更多的输入时返回null。
代码语言:java复制InputStream inputStream = ...;
try(BufferedReader in = new BufferedReader(
new InputSteamReader(inputStream, StandardCharset.UTF_8))){
String line;
while((line = in.readLine()) != null){
do somthing
}
}
如今,BufferReader类又有了一个lines方法,产生一个Stream<String>对象。
与Scanner不同,BufferedReader没有用于任何读入数字的方法。
以文本格式存储对象。
案例的形式操作。存储一个employee对象,和读取文本。
字符编码方式
输入和输出流都是用于字节序列的,但是在许多情况下,希望操作的是文本,即字符序列。
Java针对字符使用的是Unicode标准。
每个字符或“编码点”都具有一个21位的整数。有多种不同的字符编码方式, 也就是说,将这些21位数字包装成字节的方法有多种。
UTF-8,会将每个Unicode编码点编码位1到4个字节的序列。
UTF-8好处是传统的包含了英文中用到的所有字符的ASCII字符集中的每个字符都只会占用一个字节。
UTF-16,会将每个Unicode编码点编码位1个或2个16位值。这是一种Java字符串中使用的编码方式。
有两种形式的UTF-16,“高位优先”和“低位优先”。
例如16位值0x2122,高位优先,高位会先出现:ox21 ox22;地位优先:ox22 0x21
为了表示使用的是哪一种格式,文件可以以“字节顺序标记”开头,这个标记位16位数值0xFEFF。读入器可以使用这个值来确定字节顺序,然后丢弃它。
警告:有些程序,包括Microsoft Notepa(微软记事本)在内,都在UTF-8编码的文件开头添加了一个字节顺序标记。这并不需要,UTF-8中,并不存在顺序的问题。但是Unicode标准允许这样做,甚至认为这是一种好的做法,因为这种可以使编码机制不留疑惑。Java并没有这样做。
最好的做法是将输入中发现所有先导的uFEFF都剥离掉。
ISO8859-1:单字节编码,包含了西欧各种语言中用到的带有重音符号的字符。
Shift-JIS:用于日文字符的可变长编码。
不存在任何可靠的方式可以自动地探测出字节流中所使用的字符编码方式。
平台使用的编码方式可以由静态方法Charset.defaultCahrset返回。静态方法Charset.availableCharsets会返回所有可用的Charset实例,返回结果是一个字符集的规范名称到Charset对象的映射表。
Oracle的Java实现有一个用户覆盖平台默认值的系统属性file.encoding。它是非官方支持的属性,并且Java库的Oracle实现的所有部分并非都以一致方式处理该属性,因此,不应该设置它。
StandardCharsets类具有类型为Charset的静态变量,用于表示Java虚拟机都必须支持的字符编码方式。
为了获得另一种编码方式的Charset,可以使用静态的forName方法
代码语言:java复制Charset shiftJIS = Charset.forName("shiftJIS");
警告:在不指定任何编码方式时,有些方法(String(byte[])构造器)会使用默认的平台编码方式,而其他方法(Files.readAllLines)会使用UTF-8