Java输入输出流
java.io 包有两种流。一种是面向字符的流,一种是面向字节的流。
字符流的读写单位是字符,并且只能处理字符类型的数据。
字节流的读写单位是字节(8bit),能处理所有类型的数据(图片、视音频…)。
字符流中的输入主要使用 Reader 类实现,输出主要使用 Writer 类实现。
字节流中输入数据使用 InputStream ,输出使用 OutputStream 。
当然,无论是字符流还是字节流,它们的子类都可以分为两大类:
- 节点流: 从数据源读入数据/往目的地写入数据。
- 处理流: 对数据执行某种处理。
面向字符的流
我们先来探讨这么一个概念:“标准输入输出流”。那么什么是标准输入输出流呢?
我们已经熟悉到不能再熟悉了,它呢,对应的是键盘和屏幕,下面简要概述:
- System 类的静态成员变量
- System.in
- System.out
- System.err
- printf 方法
- Scanner
贼强,下面看主要内容:
面向字符的抽象流类有两个。一个是 Reader,另一个是 Writer 。
先谈 Writer ,即写文本文件。看下面代码:
import java.io.*;
public class FileWriterTest {
public static void main(String[] args) throws IOException{
String fileName = "Hello.txt";
FileWriter writer = new FileWriter(fileName);
writer.write("Hello!\n");
writer.write("This is my first text file.\n");
writer.write("Bye");
writer.close();
}
}
结果如下:
这个 Hello.txt 文件会写在当前工作空间。这里有一个问题:代码中有 \n 换行符,但是打开该文件却发现并没有换行,这是为什么?
这是因为 \n 在不同平台的解释会不一样,所以没有得到我们想要的结果也是很正常的事情。上面这个代码并没有达到跨平台的目的。
为了达到跨平台的目的,我们要学习一个叫做 BufferedWriter 的类。在 BufferedWriter 下进行缓冲,可以提高效率。
BufferedWriter 和 FileWriter 类都用于输出字符流,包含的方法几乎是一样的。但 BufferedWriter 多提供了一个 newLine() 的方法用于换行。(它是跨平台的,可以输出当前系统中正确的换行符)
改进代码如下:
import java.io.*;
public class BufferedWriterTest {
public static void main(String[] args) throws IOException{
String fileName = "newHello.txt";
BufferedWriter out = new BufferedWriter(new FileWriter(fileName));
out.write("Hello!");
out.newLine();
out.write("This is another text file using BufferedWriter.");
out.newLine();
out.write("Bye");
out.close();
}
}
结果在这里:
再谈 Reader ,即读文本文件。
看下面在这个程序(从文本数据中读取数据,并在显示器上显示出来):
import java.io.*;
public class BufferedReaderTest {
public static void main(String[] args) {
String fileName = "newHello.txt";
String line;
try {
BufferedReader in = new BufferedReader(new FileReader(fileName));
line = in.readLine(); // 读取一行内容
while(line != null) {
System.out.println(line);
line = in.readLine();
}
}catch(IOException iox) {
System.out.println("Problem reading " + fileName);
}
}
}
运行结果:
这个代码中有一个 BufferedReader 类,顾名思义,它是一个和 BufferedWriter 类似的东西。
BufferedReader 是读文本文件的缓冲器类,具有 readline() 方法,可以对换行符进行鉴别,一行一行地读取输入流中的内容。
面向字节的流
用 写入二进制文件 和 读取二进制文件 来探讨。
写入二进制文件
我们都知道二进制的写比文本文件的写速度更快,同样的信息写成二进制文件所用的存储空间也更小,而且不是纯文本的文件我们只能把这些信息以二进制的形式写到文件中去。
如果我们想要把数据以二进制方式写到二进制文本中去,我们就要使用抽象类OutputStream 的一些子类,比如它的派生类 FileOutputStream 和 DataOutputStream 。
FileOutputStream 类用于一般目的输出(非字符输出),并且按照成组的字节去输出;DataOutputStream 类具有写各种基本类型数据的方法(这个类也是一个处理流,它不是直接去执行写操作的。它是对数据按照类型进行一下处理,然后将数据传给另外一个输出流,由那个输出流金星真正的写的动作)。它在所有计算机平台上使用同样的数据格式。而且它其中的 size 方法可以用作计数器,统计写入的字节数。
我们来看一个例题,将 int 数据写到二进制文件中去(将三个int型数字 255、0、-1 写入数据文件 DataOne.dat 中:
import java.io.*;
public class FileOutputStreamTest {
public static void main(String[] args) {
String fileName = "DataOne.dat";
int value0 = 255;
int value1 = 0;
int value2 = -1;
try {
DataOutputStream out = new DataOutputStream(new FileOutputStream(fileName));
out.writeInt(value0);
out.writeInt(value1);
out.writeInt(value2);
out.close();
}catch(IOException iox){
System.out.println("Problem writing " + fileName);
}
}
}
这个代码中的 FileOutputStream 只是识别的原生字节流,DataOutputStream 才是识别某种类型的处理流。
生成了下面这个文件:
这个文件因为不是纯文本文件,所以不能用记事本打开。我们要用一个能阅读二进制的编辑器打开它,我用的是 Hex Editor Neo ,打开之后是这个样子:
当我们写大量数据的时候,用缓冲流类 BufferedOutputStream 能提高写的效率。这个类也是一个处理流,它不是直接进行写操作的。
用法示例:
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
下面看一个例子,这个例子按类型写入了多种类型的数据:
import java.io.*;
public class BufferedOutputStreamTest {
public static void main(String[] args) throws IOException{
String fileName = "mixedTypes.dat";
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
out.writeInt(0);
System.out.println(out.size() + "bytes have been written.");
out.writeDouble(3.14);
System.out.println(out.size() + "bytes have been written.");
out.writeBytes("Java");
System.out.println(out.size() + "bytes have been written.");
out.close();
}
}
生成了这个文件:
和运行结果:
打开是这样的:
读取二进制文件
看下面这个例题(读取二进制文件中的三个int型数字并相加):
import java.io.*;
public class DataInputStreamTest {
public static void main(String[] args) throws IOException{
String fileName = "DataOne.dat";
int sum = 0;
try {
DataInputStream instr = new DataInputStream(new BufferedInputStream(new FileInputStream(fileName)));
sum += instr.readInt();
sum += instr.readInt();
sum += instr.readInt();
System.out.println("The sum is: " + sum);
instr.close();
}catch(IOException iox) {
System.out.println("Problem reading " + fileName);
}
}
}
运行结果截图:
对于上面的代码,如果我们不知道里面有多少数,我们使用循环时如何判断读到文件尾了呢? 用捕获异常的方法:
import java.io.*;
public class DataInputStreamTest {
public static void main(String[] args) throws IOException{
String fileName = "DataOne.dat";
int sum = 0;
try {
DataInputStream instr = new DataInputStream(new BufferedInputStream(new FileInputStream(fileName)));
try {
while(true)
sum += instr.readInt(); // 遇到文件尾自然跳出去,因为读不了了
}catch(EOFException eof) {
System.out.println("The sum is: " + sum);
instr.close();
}
}catch(IOException iox) {
System.out.println("Problem reading " + fileName);
}
}
}
这样就可以了。当然这个代码还有很多问题,比如没有考虑程序有可能不是在文件尾停止,而是在中间某地方停止……
好了,就这些。