1.缓冲输入文件
FileReader
BufferedReader
FileReader可以直接对文件进行读操作。但是简化编程,加快读取速度,我们加入了缓冲机制,使用了BufferedReader。BufferedReader内置了一个char[]数组(大小为8192)作为缓冲区,每次调用fill()函数将该缓冲区尽可能填满。而我们自己的程序在调用BufferedReader提供的方法时,实质上是从该缓冲区读取的。
BufferedReader中的fill()方法
private void fill() throws IOException {
int dst;
if (markedChar <= UNMARKED) {
/* No mark */
dst = 0;
} else {
/* Marked */
int delta = nextChar - markedChar;
if (delta >= readAheadLimit) {
/* Gone past read-ahead limit: Invalidate mark */
markedChar = INVALIDATED;
readAheadLimit = 0;
dst = 0;
} else {
if (readAheadLimit <= cb.length) {
/* Shuffle in the current buffer */
System.arraycopy(cb, markedChar, cb, 0, delta);
markedChar = 0;
dst = delta;
} else {
/* Reallocate buffer to accommodate read-ahead limit */
char ncb[] = new char[readAheadLimit];
System.arraycopy(cb, markedChar, ncb, 0, delta);
cb = ncb;
markedChar = 0;
dst = delta;
}
nextChar = nChars = delta;
}
} int n;
do {
n = in.read(cb, dst, cb.length - dst);//该方法调用了read方法
} while (n == 0);
if (n > 0) {
nChars = dst + n;
nextChar = dst;
}
}
BufferedReader中的read()方法
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
for (;;) {
if (nextChar >= nChars) {
fill(); //调用内置的fill()方法
if (nextChar >= nChars)
return -1;
}
if (skipLF) {
skipLF = false;
if (cb[nextChar] == '\n') {
nextChar++;
continue;
}
}
return cb[nextChar++]; //返回给用户的是个缓冲区
}
}
}
从上面的代码中可以看出BufferedReader对我们普通的Reader进行了包装,通过缓冲区机制提高了用户读写的速度。
使用方式:
package com.dy.xidian; import java.io.BufferedReader;
import java.io.FileReader; public class BufferedInputFile {
public static String read(String filename) throws Exception {
BufferedReader in = new BufferedReader(new FileReader(filename));
String s;
StringBuilder sb = new StringBuilder();
while ((s = in.readLine()) != null) {
sb.append(s);
}
in.close();
return sb.toString();
} public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.out.println("Usage: BufferedInputFile filepath!");
}
String s = read(args[0]);
System.out.println(s);
}
}
注意readline()的使用:
- 读入的数据要注意有/r或/n或/r/n
- 没有数据时会阻塞,在数据流异常或断开时才会返回null
- 使用socket之类的数据流时,要避免使用readLine(),以免为了等待一个换行/回车符而一直阻塞
2.从内存输入
StringReader的输入是一个String对象,所以StringReader是从内存读取数据的。上一个例子输出的是个String对象,我们恰好可以利用一下。
public class MemoryInput {
public static void main(String[] args) throws Exception {
String filename = "E:/html/111.php";
StringReader sr = new StringReader(BufferedInputFile.read(filename));
int c;
while ((c = sr.read()) != -1)
System.out.print((char) c);
}
}
3.格式化的内存输入
要读取格式化数据,可以使用DataInputStream,它是一个面向字节的I/O类。
package com.dy.xidian; import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; class BufferedInputFile {
BufferedReader br; public BufferedInputFile(String path) throws FileNotFoundException {
br = new BufferedReader(new FileReader(path));
} public String read() throws IOException {
StringBuilder sb = new StringBuilder();
String s;
while ((s = br.readLine()) != null)
sb.append(s);
return sb.toString();
}
}
public class FormattedMemoryInput {
public static void main(String[] args) throws IOException {
String path = "E:/html/utf-8.php";
DataInputStream in = new DataInputStream(new ByteArrayInputStream(
path.getBytes()));
while (in.available() != 0)
System.out.print((char) in.readByte());
}
}
4.基本的文件输出
FileWriter对象可以向文件中写入数据。首先创建一个与指定文件连接的FileWriter。然后我们对其进行装饰。为了提高写的效率,加入缓冲机制,使用BufferedWriter类对其进行包装。为了能够格式化输出,使用PrintWriter类包装。
package com.dy.xidian; import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter; public class BasicFileOutput {
static String File = "E:/html/utf-8.php";
public static void main(String[] args) throws IOException {
PrintWriter os = new PrintWriter(new BufferedWriter(
new FileWriter(File)));
String s1 = "hello world!";
String s2 = "世界 你好";
os.println(s1);
os.print(s2);
os.close();
}
}
更为简单的方式:利用PrintWriter提供的辅助构造器
public PrintWriter(String fileName) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),
false);
} /* Private constructor */
private PrintWriter(Charset charset, File file)
throws FileNotFoundException
{
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)),
false);
}
package com.dy.xidian; import java.io.IOException;
import java.io.PrintWriter; public class BasicFileOutput {
static String File = "E:/html/utf-8.php";
public static void main(String[] args) throws IOException {
PrintWriter os = new PrintWriter(File);
String s1 = "hello world!";
String s2 = "世界 你好";
os.println(s1);
os.print(s2);
os.close();
}
}
5.储存和恢复数据
DataInputStream和DataOutputStream一般用来对对象的成员属性读取和输出操作。这两个类都是面向字节流的。
package com.dy.xidian; 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 StoringAndRecoveringData {
public static void main(String args[]) throws IOException {
String path = "E:/html/utf-8.php";
FileOutputStream fs = new FileOutputStream(path);
DataOutputStream os = new DataOutputStream(new BufferedOutputStream(fs));
os.writeDouble(3141.5);
os.writeUTF("这是Pi1"); //使用UTF-8编码
os.close();
DataInputStream is = new DataInputStream(new BufferedInputStream(
new FileInputStream(path)));
System.out.println(is.readDouble()); //先读double
System.out.println(is.readUTF()); //在读字符串
is.close();
}
}
从输出文件的内容我们可以发现是DataOutputStream输出的是一大堆编码,这些编码只适合DataInputStream来读取。这种方式可以用来进行对象的序列化存储。并且读取的顺序要与存储的顺序保持一致。
6.随机访问文件
RandomAccessFile提供了随机访问的文件的方法,它可以看成是DataInputStream和DataOutputStream的组合。RandomAccessFile也是面向字节流的,所以我们想修改文件某处的值时,需要计算出该值的偏移量(单位为字节),通过seek方法,将文件指针指向该处,才可以修改成功。
package com.dy.xidian; import java.io.IOException;
import java.io.RandomAccessFile; public class UsingRandomAccessFile {
static String file = "E:/html/utf-8.php";
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("文件末尾");
rf.close();
display();
rf = new RandomAccessFile(file, "rw");
rf.seek(5 * 8);
rf.writeDouble(47.0001);
rf.close();
display();
}
}
我们想修改第六个Double值。首先算出偏移量,每个double类型8个字节,所以第六个double起始位置的偏移量为40=5*8,所以应该调用seek(5*8)将文件指针指向该处,在调用write方法来修改该值。
7.读写文件工具类
package com.dy.xidian; import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.TreeSet; public class TextFile extends ArrayList<String> { /**
*
*/
private static final long serialVersionUID = 1L; /**
*
* @param 文件名
* @param 字符集编码
* @return 文件内容
*/
public static String read(String fileName, String Charset) {
StringBuilder sb = new StringBuilder();
try {
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(fileName), Charset));
try {
String s;
while ((s = br.readLine()) != null) {
sb.append(s);
sb.append("\n");
}
} finally {
br.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return sb.toString();
} /**
*
* @param 文件名
* @param 文本内容
*/
public static void write(String fileName, String text) {
try {
PrintWriter out = new PrintWriter(
new File(fileName).getAbsoluteFile());
try {
out.print(text);
} finally {
out.close();
}
} catch (IOException e) {
throw new RuntimeException();
}
} public TextFile(String fileName, String splitter) {
super(Arrays.asList(read(fileName, "utf-8").split(splitter)));
// split()会留一个空字符串在开始的位置
if (get(0).equals(""))
remove(0);
} public TextFile(String fileName) {
this(fileName, "\n");
} public void write(String fileName) {
try {
PrintWriter out = new PrintWriter(
new File(fileName).getAbsoluteFile());
try {
for (String item : this) {
out.print(item);
}
} finally {
out.close();
}
} catch (IOException e) {
throw new RuntimeException();
}
} public static void main(String[] args) {
String file = TextFile.read("com/dy/xidian/TextFile.java", "utf-8");
TextFile.write("test.txt", file);
TextFile text = new TextFile("test.txt");
text.write("test2.txt");
TreeSet<String> words = new TreeSet<String>(new TextFile(
"com/dy/xidian/TextFile.java", "\\W+"));
for (String string : words) {
System.out.println(string);
}
System.out.println(words.headSet("a"));
}
}