IO流的典型使用方式
《Java编程思想》(第4版)18.6节学习笔记
缓冲输入文件
FileReader可打开文件进行字符输入,为了对文件进行缓冲以提高速度,可用BufferedReader进行包装。BufferedReader提供readLine方法,返回null时就达到了文件末尾。
package com.cas.io;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedInputFile {
public static String read(String filename) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(filename));
String s;
StringBuilder strBuilder = new StringBuilder();
while ((s = reader.readLine()) != null) {
strBuilder.append(s + "\n");
}
reader.close();
return strBuilder.toString();
}
public static void main(String[] args) throws IOException {
System.out.println(read("test.txt"));
}
}
从内存输入
示例:使用以上的BufferedInputFile读取文件内容到字符串,String结果用来创建一个StringReader,然后调用read()每次读取一个字符并打印到控制台。
package com.cas.io;
import java.io.IOException;
import java.io.StringReader;
public class MemoryInput {
public static void main(String[] args) throws IOException {
StringReader reader = new StringReader(BufferedInputFile.read("test.txt"));
int c;
//read以int形式返回下一字节,打印时要转为char类型
while ((c = reader.read()) != -1) {
System.out.print(((char) c));
}
}
}
格式化的内存输入
要读取格式化数据,可以使用面向字节的I/O类DataInputStream。
package com.cas.io;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
public class FormattedMemoryInput {
public static void main(String[] args) throws IOException {
try {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(
BufferedInputFile.read("test.txt").getBytes()));
while (true) {
System.out.print((char) in.readByte());
}
} catch (EOFException e) {
System.err.println("End of Stream");
}
}
}
输出示例:
Hello,
World!
End of Stream
如果从DataInputStream一次一个字节地读取字符,那么任何字节的值都是合法的结果,因此返回值不能用来检测输入是否结束,此时可用available()方法查看还有多少可供存取的字符。
package com.cas.io;
import java.io.*;
public class TestEOF {
public static void main(String[] args) throws IOException {
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("test.txt")));
while (in.available() != 0) {
System.out.print(((char) in.readByte()));
}
}
}
注意,available()的工作方式会随着所读取的媒介类型的不同而有所不同,字面意思是“在没有阻塞的情况下所能读取的字节数”。对于文件,意味着整个文件;但对于不同类型的流,可能不是这样,因此要谨慎使用。
基本的文件输出
FileWriter指定文件,BufferedWriter用以缓冲输出,PrintWriter提供格式化机制。
package com.cas.io;
import java.io.*;
public class BasicFileOutput {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new StringReader(BufferedInputFile.read("test.txt")));
PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter("test_out.txt")));
int lineCount = 1;
String line;
while ((line = reader.readLine()) != null) {
writer.println(lineCount++ + ": " + line);
}
// 刷新清空缓冲区内容
writer.close();
System.out.println(BufferedInputFile.read("test_out.txt"));
}
}
文本文件输出的快捷方式
Java SE5在PrintWriter中添加了一个辅助构造器,可以简化文本文件写出的装饰工作。
// PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter("test_out.txt")));
PrintWriter writer = new PrintWriter("test_out.txt");
存储和恢复数据
为了输出可供另一个流恢复的数据(如Double),需要用DataOutputStream写入数据,并用DataInputStream恢复数据,这些流可以是任何形式。
如果使用DataOutputStream写入数据,Java保证可以使用DataInputStream准确的读取数据—无论读和写数据的平台多么不同(XML是在不同计算平台之间移动数据的另一种方式)。
package com.cas.io;
import java.io.*;
public class StoringAndRecoveringData {
public static void main(String[] args) throws IOException {
//写出数据
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("data.txt")));
out.writeDouble(Math.sqrt(2));
out.writeUTF("根号2的值");
out.close();
//读取数据
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("data.txt")));
System.out.println(in.readDouble());
System.out.println(in.readUTF());
}
}
注意,writeUTF和readUTF使用的是适合于Java的UTF-8变体。
读写随机访问文件
RandomAccessFIle实现了DataInput和DataOutput接口,类似于DataInputStream和DataOutputStream的组合。另外,利用seek()可以在文件中到处移动,并修改文件中的某个值。
使用RandomAccessFIle时,必须知道文件排版,以便正确地操作它。RandomAccessFIle拥有读取基本类型和UTF-8字符串的各种具体方法。
示例代码:
package com.cas.io;
import java.io.IOException;
import java.io.RandomAccessFile;
public class UsingRandomAccessFile {
static String file = "test.dat";
/**
* 读取并显示3个Double值
*/
static void display() throws IOException {
// 只读方式打开
RandomAccessFile rf = new RandomAccessFile(file, "r");
for (int i = 0; i < 3; i++) {
System.out.println("Value " + i + ": " + rf.readDouble());
}
System.out.println(rf.readUTF());
rf.close();
}
public static void main(String[] args) throws IOException {
// 读写方式打开
RandomAccessFile rf = new RandomAccessFile(file, "rw");
for (int i = 0; i < 3; i++) {
rf.writeDouble(i * 1.2);
}
rf.writeUTF("The end of the file");
rf.close();
display();
// 将2号位的Double值增至10倍
rf = new RandomAccessFile(file, "rw");
rf.seek(2 * 8);
double tmp = rf.readDouble();
rf.seek(2 * 8);
rf.writeDouble(10 * tmp);
rf.close();
display();
}
}
输出结果:
Value 0: 0.0
Value 1: 1.2
Value 2: 2.4
The end of the file
Value 0: 0.0
Value 1: 1.2
Value 2: 24.0
The end of the file
管道流
PipedInputStream、PipedOutputStream、PipedReader和PipedWriter属于管道流,用于多线程中任务之间的通信。