1、缓冲输入文件
如果想打开一个文件用于字符输入,可以使用String或者File对象为参数的FileReader。为了提高速度,我们希望对那个文件进行缓冲,那么我们将产生的引用传给一个BufferedReader构造器。由于BufferedReader也提供了readLine()方法,所以这是我们最终对象和进行读取的接口。当readLine()将返回null时,也就达到了文件的末尾。
package 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的readLine方法每次读取一个字符串
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.println(BufferedInputFile.read("src/io/BufferedInputFile.java"));
}
}
字符串sb用来累计文件的全部内容(包括必须添加的换行符,因为readLine()已将它们删掉)。
2、从内存输入
import java.io.IOException;
import java.io.StringReader;
public class MemoryInput {
public static void main(String[] args) throws IOException {
StringReader in = new StringReader(BufferedInputFile
.read("src/io/MemoryInput.java"));
int c;
while ((c = in.read()) != -1)
System.out.print((char) c);
}
}
3、格式化的内存输入
要读取格式化数据,可以使用DataInputStream,它是一个面向字节的I/O类(不是面向字符)。因此我们必须使用InputStream类而不是Reader类。
package io;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
public class FormattedMemoryInput {
public static void main(String[] args) throws IOException{
try {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(
BufferedInputFile.read("src/io/FormattedMemoryInput.java")
.getBytes()));
while(true)
System.out.print((char)in.readByte());
} catch (IOException e) {
System.err.println("End of Stream");
}
}
}
必须为ByteArrayInputStream提供字节数组。
如果我们从DataInputStream用readByte()一次一个字节的读取字符,那么任何字节的值都是合法的结果,因为返回值不能用来检测输入是否结束。相反,我们可以使用available()方法检查看还有多少可供存取的字符。
package io;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
public class TestEOF {
public static void main(String[] args) throws IOException {
DataInputStream in = new DataInputStream(new ByteArrayInputStream(
BufferedInputFile.read("src/io/TestEOF.java").getBytes()));
//available方法來自FilterInputStream,返回的是下一次对此输入流调用的方法
//可以不受阻塞(或跳过)的估计剩余字节数
while ((in.available()) != 0)
System.out.print((char) in.readByte());
}
}
4、基本的文件输出
FileWriter对象可以向文件写入数据。首先,创建一个与指定文件链接的FileWriter。实际上,我们通常会用BufferedWriter将其包装起来用以缓冲输出。
package io;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
public class BasicFileOutput {
static String file = "BasicFileOutput.out";
public static void main(String[] args) throws IOException{
BufferedReader in = new BufferedReader(new StringReader(BufferedInputFile.read("src/io/BasicFileOutput.java")));
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
//也可以用下面的快捷方式
//PrintWriter out = new PrintWriter(file);
int lineCount = 1;
String s;
while((s = in.readLine()) != null){
out.println(lineCount++ + ": " + s);
}
out.close();
System.out.println(BufferedInputFile.read(file));
}
}
一旦读完输入数据流,readLine()就返回null。要为out显示的调用close()。如果我们不为所有的输出文件调用close(),就会发现缓冲区内容不会被刷新清空,那么它们也就不完整。
5、存储和恢复数据
PrintWriter可以对数据进行格式化,以便人们阅读。但是为了输出一个可供另一个“流”恢复的数据,我们需要用DataOutputStream写入数据,并用DataInputStream恢复数据。
package io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class StroringAndRecoveringData {
public static void main(String[] args) throws IOException{
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("Data.txt")));
out.writeDouble(3.14159);
out.writeUTF("This is 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());
}
}
如果我们使用DataOutputStream写入数据,Java保证我们可以使用DataInputStream准确读取数据--无论读和写数据的平台多么不同。
当我们使用DataOutputStream时,写字符串并且让DataInputStream能够恢复它的唯一可靠做法就是使用UTF-8编码,在这里用writeUTF()和readUTF()来实现。UTF-8是一种多字节格式,其编码长度根据实际使用的字符集会有变化。如果我们使用的只是ASCII或者几乎都是ASCII字符(只占7位),那么就显得极其浪费空间和带宽,所以UTF-8将ASCII字符编码成单一字节的形式,而非ASCII字符则编码成两到三个字节的形式。
6、从标准输入中读取
按照标准的I/O模型,Java提供了System.in System.out和System.err。其中System.out已经事先被包装成了printStream对象,System.err也同样如此,但是System.in确实一个没有被包装过的未经加工的InputStream。这意味尽管我们可以立即使用System.out和System.err,但是在读取System.in之前必须对其进行包装。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Echo {
public static void main(String[] args) throws IOException{
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String s;
while((s = reader.readLine()) != null && s.length() != 0)
System.out.println(s);
}
}
7、标准I/O的重定向
Java的System类提供了一些简单的静态方法调用,以允许我们对标准输入、输出和错误I/O流进行重定向:
setIn(InputStream);
setOut(PrintStream);
setErr(PrintStream);
如果我们突然开始在显示器上创建大量输出,而这些输出滚动得太快以为无法阅读时,重定向输出就显得极为有用。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
public class Redirecting {
public static void main(String[] args) throws IOException{
PrintStream console = System.out;
BufferedInputStream in = new BufferedInputStream(new FileInputStream("src/io/Redirecting.java"));
PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream("redirect.out")));
System.setIn(in);
System.setOut(out);
System.setErr(out);
BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
String s;
while((s = bReader.readLine()) != null)
System.out.println(s);
out.close();
System.setOut(console);
}
}
I/O重定向操纵的是字节流,而不是字符流;因此我们使用的是InputStream和OutputStream,而不是Reader和Writer。