尽管可以通过不同的方式组合I/O流泪,但可能也就只用到其中的几种组合。下面的例子可以作为典型的I/O流用法的基本参考,这些示例中异常处理简化为简单的抛出。
1、缓冲输入文件
如果想要打开一个文件用于字符输入,可以使用以String或File对象作为文件名的FileInputReader,为了提高速度,对那个文件进行缓冲,可将产生的引用传给一个BufferedReader构造器。当readLine()返回null时,就达到了文件的末尾。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedInputFile {
public static String read(String filename) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(filename));
String s;
StringBuilder sb = new StringBuilder();
while ((s = in.readLine()) != null) {
sb.append(s+"\n");
}
in.close();
return sb.toString();
}
public static void main(String[] args) throws IOException {
System.out.print(read("BufferedInputFile.java"));
}
}
字符串sb用来累积文件的全部内容(包括必须添加的换行符,因为readLine()已将它们删除),最后调用close()关闭文件。
2、从内存输入
import java.io.*;
public class MemoryInput {
public static void main(String[] args) throws IOException {
StringReader in = new StringReader(read("MemoryInput.java"));
int c;
while ((c = in.read()) != -1)
System.out.print((char) c);
}
public static String read(String filename) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(filename));
String s;
StringBuilder sb = new StringBuilder();
while ((s = in.readLine()) != null) {
sb.append(s + "\n");
}
in.close();
return sb.toString();
}
}
注意read()是以int形式返回一下字节,因此必须类型转换为char才能正确打印。
3、格式化的内存输入
要读取格式化数据,可以使用DataInputStream,它是一个面向字节的I/O类,因此必须使用InputStream类而不是Reader类。当然,可以用InputStream以字节形式读取任何数据(例如一个文件),不过这里使用的是字符串。
import java.io.*;
public class FormattedMemoryInput {
public static String read(String filename) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(filename));
String s;
StringBuilder sb = new StringBuilder();
while ((s = in.readLine()) != null) {
sb.append(s + "\n");
}
in.close();
return sb.toString();
}
public static void main(String[] args) throws IOException{
try {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(read("FormattedMemoryInput.java").getBytes()));
while (true) {
System.out.print((char)in.readByte());
}
} catch (EOFException e) {
System.err.println("End of stream");
}
}
}
如果从DataInputStream用readByte()一次一个字节地读取字符,那么任何字节的值都是合法的结果,因此返回值不能用来检测输入是否结束。相反可以使用available()方法查看还有多少个可供存取的字符。下面的例子演示了怎样一次一个字节地读取文件:
import java.io.*;
public class FormattedMemoryInput {
public static String read(String filename) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(filename));
String s;
StringBuilder sb = new StringBuilder();
while ((s = in.readLine()) != null) {
sb.append(s + "\n");
}
in.close();
return sb.toString();
}
public static void main(String[] args) throws IOException{
try {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(read("FormattedMemoryInput.java").getBytes()));
while (in.available() != 0) {
System.out.print((char)in.readByte());
}
} catch (EOFException e) {
System.err.println("End of stream");
}
}
}
注意,available()的工作方式会随着所读取的媒介的类型的不同而有所不同;字面意思就是“在没有阻塞的情况下所能读取的字节数”。对应文件,这意味着整个文件,但是对于不同类型的流,可能就不是这样的,因此要谨慎使用。
也可以通过异常捕获来检测输入的末尾。但是,使用异常进行流控制,被认为是对异常特性的错误使用。
4、基本的文件输出
FileWriter对象可以向文件写入数据。首先,创建一个与指定文件连接的FileWriter,实际上通常会用BufferedWriter将其包装起来用以缓冲输出,本例中为了提供格式化机制,它被装饰成PrintWriter。按照这种方式创建的数据文件可作为普通文本文件读取。
import java.io.*;
public class BasicFileOutput {
public static String read(String filename) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(filename));
String s;
StringBuilder sb = new StringBuilder();
while ((s = in.readLine()) != null) {
sb.append(s + "\n");
}
in.close();
return sb.toString();
}
static String file = "BasicFileOutput.out";
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new StringReader(read("BasicFileOutput.java")));
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
int lineCount = 1;
String s;
while ((s = in.readLine()) != null) {
out.println(lineCount++ + ": "+s);
}
out.close();
System.out.println(read(file));
}
}
Java SE5在PrintWriter中添加了一个辅助构造器,使得不必每次希望创建文本文件并向其中写入时,都去执行所有的装饰工作,如下例所示:
import java.io.*;
public class FileOutputShortcut {
public static String read(String filename) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(filename));
String s;
StringBuilder sb = new StringBuilder();
while ((s = in.readLine()) != null) {
sb.append(s + "\n");
}
in.close();
return sb.toString();
}
static String file = "FileOutputShortcut.out";
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new StringReader(read("FileOutputShortcut.java")));
// Here's the shortcut:
PrintWriter out = new PrintWriter(file);
int lineCount = 1;
String s;
while ((s = in.readLine()) != null)
out.println(lineCount++ + ": " + s);
out.close();
// Show the stored file:
System.out.println(BufferedInputFile.read(file));
}
}
5、存储和恢复数据
PrintWriter可以对数据进行格式化,以便人们阅读。但是为了输出可供另一个“流”恢复的数据,需要用DataOutputStream写入数据,并用DataInputStream恢复数据。注意DataOutputStream和DataInputStream是面向字节的,因此要使用InputStream和OutputStream。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(3.1415926);
out.writeUTF("That was pi");
out.writeDouble(1.41413);
out.writeUTF("Square root of 2");
out.close();
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("Data.txt")));
System.out.println(in.readDouble());
System.out.println(in.readUTF());
System.out.println(in.readDouble());
System.out.println(in.readUTF());
}
}
6、读写随机访问文件
使用RandomAccessFile,类似于组合使用了DataInputStream和DataOutputStream(因为它实现了相同接口)。利用seek()可以在文件中到处移动,并修改文件中的某个值。在使用RandomAccessFile时,必须知道文件排版,这样才能正确地操作它。RandomAccessFile拥有读取基本类型和UTF-8字符串的各种具体方法。下面是示例:import java.io.*;
public class UsingRandomAccessFile {
static String file = "rtest.dat";
static void display() throws IOException{
RandomAccessFile rf = new RandomAccessFile(file, "r");
for (int i = 0; i < 7; 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 < 7; i++) {
rf.writeDouble(i*1.414);
}
rf.writeUTF("The end of the file");
rf.close();
display();
rf = new RandomAccessFile(file, "rw");
rf.seek(5*8);
rf.writeDouble(47.0001);
rf.close();
display();
}
}
因为double是8字节长,所以为了用seek()查找第5个双精度值,只需用5*8来产生查找位置。
7、文件读写的实用工具
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
public class TextFile extends ArrayList<String> {
/**
* Read a file as a single string
* @param fileName
* @return
* @throws IOException
*/
public static String read(String fileName) throws IOException {
StringBuilder sb = new StringBuilder();
BufferedReader in = new BufferedReader(new FileReader(new File(fileName).getAbsoluteFile()));
try {
String s;
while ((s = in.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
} finally {
in.close();
}
return sb.toString();
}
/**
* Write a single file
* @param fileName
* @param text
* @throws IOException
*/
public static void write(String fileName,String text) throws IOException {
PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
try{
out.print(text);
} finally{
out.close();
}
}
/**
* Read a file ,split by any regular expression
* @param fileName
* @param splitter
* @throws IOException
*/
public TextFile(String fileName,String splitter) throws IOException{
super(Arrays.asList(read(fileName).split(splitter)));
if (get(0).equals("")) {
remove(0);
}
}
public TextFile(String fileName) throws IOException{
this(fileName, "\n");
}
public void write(String fileName) throws IOException {
PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
try{
for (String item:this) {
out.println(item);
}
} finally{
out.close();
}
}
/**
*
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException {
String fileName = "TextFile.java";
String file = read(fileName);
write("test.txt", file);
TextFile text = new TextFile("test.txt");
text.write("test2.txt");
TreeSet<String> words = new TreeSet<String>(new TextFile(fileName, "\\W+"));
System.out.println(words.headSet("a"));
//下面代码可以统计文档里各个字母出现的次数
TextFile text2 = new TextFile(fileName,"\\W+");
Map<Character, Integer> charsStat = new HashMap<Character, Integer>();
for (String word:text2) {
for (int i = 0; i < word.length(); i++) {
Character ch = word.charAt(i);
Integer freq = charsStat.get(ch);
charsStat.put(ch, freq == null ? 1:freq+1);
}
}
List<Character> keys = Arrays.asList(charsStat.keySet().toArray(new Character[0]));
Collections.sort(keys);
for (Character key:keys) {
System.out.println(key +" =>"+charsStat.get(key));
}
}
}
读取二进制文件
import java.io.*;
public class BinaryFile {
public static byte[] read(File bFile) throws IOException{
BufferedInputStream bf = new BufferedInputStream(
new FileInputStream(bFile));
try {
byte[] data = new byte[bf.available()];
bf.read(data);
return data;
} finally {
bf.close();
}
}
public static byte[]
read(String bFile) throws IOException {
return read(new File(bFile).getAbsoluteFile());
}
}