目录
FileInputStream&FileOutputStream
BufferedOutputStream&BufferedInputStream
FileReader&FileWriter&BufferedReader&BufferedWriter&
IO流按流向:输入流(读取数据),输出流(写出数据)
按数据类型:字节流,字符流
综上,有以下几大类:
字节输入流(读取数据)--- InputStream
字节输出流(写出数据)--- OutputStream
字符输入流(读取数据)--- Reader
字符输出流(写出数据)--- Writer
字节流
InputStream和OutputStream为字节流的抽象类,其进行文件操作的实现类分别为FileInputStream和FileOutputStream。
以下代码展示了它们的基本使用:
FileInputStream
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class fisDemo {
private File file = new File("E:/data/javaIO/a.txt");
// 单个字节的读
@Test
public void fis1() {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
int by = 0;
while ((by = fis.read()) != -1) {
System.out.print((char) by);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 定义数组读
@Test
public void fis2() {
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
System.out.print(new String(bytes, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileOutputStream
import org.junit.Test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class fosDemo{
private File file = new File("E:/data/javaIO/a.txt");
@Test
public void fos() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
fos.write("a test".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileInputStream&FileOutputStream
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class CopyFileDemo {
private static File infile = new File("E:/data/javaIO/a.txt");
private static File outfile = new File("E:/data/javaIO/b.txt");
// 单个字节的读写
@Test
public void cf1() throws Exception {
FileInputStream fis = new FileInputStream(infile);
FileOutputStream fos = new FileOutputStream(outfile);
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fos.close();
fis.close();
}
// 指定数组长度读写
@Test
public void cf2() throws Exception {
FileInputStream fis = new FileInputStream(infile);
FileOutputStream fos = new FileOutputStream(outfile);
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();
fis.close();
}
}
到这里,不难发现,指定数组长度去读取和写入,效率比单个字节要高很多(废话,毕竟一次读多个字节要少循环上千倍啊!),java在设计时,也考虑到了这个问题,就专门提供了带缓冲区的字节类。这种类被称为缓冲区类。
BufferedOutputStream&BufferedInputStream
在使用以上类时,传入的是一个OutputStream或InputStream对象,而不是文件或文件路径,那是因为字节缓冲区流仅仅提供缓冲区,为高效而设计,但真正的读写操作还得依靠基本的流对象实现。
话不多说,把它们放一起比较比较下效率,(方法上面的注释就是我的实际运行结果)
import org.junit.Before;
import org.junit.Test;
import java.io.*;
public class EfficiencyCompare{
private File inFile = new File("E:/data/javaIO/a.avi");
private File outFile = new File("E:/data/javaIO/b.avi");
@Before
public void init() throws IOException {
if (!outFile.exists()) {
boolean res = outFile.createNewFile();
System.out.println(res);
}
}
// 共耗时115616毫秒
@Test
public void NoBufferedByte() throws IOException {
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream(inFile);
FileOutputStream fos = new FileOutputStream(outFile);
int by = 0;
while ((by = fis.read()) != -1) {
fos.write(by);
}
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒");
}
// 共耗时237毫秒
@Test
public void NoBufferedBytes() throws IOException {
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream(inFile);
FileOutputStream fos = new FileOutputStream(outFile);
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒");
}
// 共耗时1168毫秒
@Test
public void BufferedByte() throws IOException {
long start = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outFile));
int by = 0;
while ((by = bis.read()) != -1) {
bos.write(by);
}
bos.close();
bis.close();
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒");
}
// 共耗时137毫秒
@Test
public void BufferedBytes() throws IOException {
long start = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outFile));
byte[] bytes = new byte[1024];
int len = 0;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bos.close();
bis.close();
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒");
}
}
事实证明,带Buffered的就是霸道,再配合指定数据长度读写,效率杠杠的。
PrintStream
先晒张图,System.err和System.out的申明,就是PrintStream。(本篇不打算铺开讲这里的设计,后面有机会再说)。
官方都在用的东西,自然是好东西。且先看看基本用法:
import java.io.File;
import java.io.PrintStream;
public class Main {
public static void main(String[] args) throws Exception {
File file = new File("D:\\data\\data.txt");
PrintStream ps = new PrintStream(file);
ps.println("jordan");
ps.println(1);
ps.print('1');
ps.close();
}
很简洁!以下的println()和print(),均将内容打印到File中去了,我们上面说了,这是个好东西,简洁只是“好”的一方面,继续查看源码,追踪print函数,我们会发现:
这里原来运用了装饰模式,将变量textOut申明为字符流BufferedWriter,所以我们调用print()和println()方法,实际是调用了BufferedWriter中的
write(String s, int off, int len)
write(char cbuf[], int off, int len)
上面的那些Buffer,一层层的套,写着多麻烦,有了PrintStream,还要啥自行车,功能增强,它不香吗?对咯,我们在这里可以先把字符流的一些活儿给干咯,比如要指定字符集,直接这样就可以了:
PrintStream ps = new PrintStream(new FileOutputStream(file), false, StandardCharsets.ISO_8859_1.name());
字符流
FileReader&FileWriter&BufferedReader&BufferedWriter&
PrintWriter
字符流FileReader和FileWriter的用法分别对应字节流的FileInputStream和FileOutputStream。BufferedReader和BufferedWriter的用法则对应字节流的BufferedInputStream和BufferedOutputStream。而PrintWiter其实在PrintStream中已经运用过了。这里也跟上面一样,做了下读写文件的效率对比(这里的文本数据文件大小为2043KB,如果你的文件越大,效果就会越明显。)。另外,如果要将内容打印到控制台,且看到String类型的结果,那么你需要将char类型的数据进行一次转换后再打印。
import org.junit.Before;
import org.junit.Test;
import java.io.*;
public class EfficiencyCompare {
private File inFile = new File("E:/data/javaIO/a.csv");
private File outFile = new File("E:/data/javaIO/b.csv");
@Before
public void init() throws IOException {
if (!outFile.exists()) {
boolean res = outFile.createNewFile();
System.out.println(res);
}
}
// 共耗时541毫秒。
@Test
public void NoBufferedChar() throws IOException {
long start = System.currentTimeMillis();
FileReader fr = new FileReader(inFile);
FileWriter fw = new FileWriter(outFile);
int ch = 0;
while ((ch = fr.read()) != -1) {
fw.write(ch);
}
fw.close();
fr.close();
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒。");
}
// 共耗时87毫秒。
@Test
public void NoBufferedChars() throws IOException {
long start = System.currentTimeMillis();
FileReader fr = new FileReader(inFile);
FileWriter fw = new FileWriter(outFile);
int ch = 0;
char[] chars = new char[1024];
while ((ch = fr.read(chars)) != -1) {
fw.write(chars, 0, ch);
}
fw.close();
fr.close();
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒。");
}
// 共耗时235毫秒。
@Test
public void BufferedChar() throws IOException {
long start = System.currentTimeMillis();
BufferedReader br = new BufferedReader(new FileReader(inFile));
BufferedWriter bw = new BufferedWriter(new FileWriter(outFile));
int ch = 0;
while ((ch = br.read()) != -1) {
bw.write(ch);
}
bw.close();
br.close();
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒。");
}
// 共耗时77毫秒。
@Test
public void BufferedChars() throws IOException {
long start = System.currentTimeMillis();
BufferedReader br = new BufferedReader(new FileReader(inFile));
BufferedWriter bw = new BufferedWriter(new FileWriter(outFile));
int len = 0;
char[] chars = new char[1024];
while ((len = br.read(chars)) != -1) {
bw.write(chars, 0, len);
}
bw.close();
br.close();
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒。");
}
}
字节流和字符流之间的转换
我们不难发现,如果一个文本要转码写入另一个文件,比如原先为UTF-8编码,现写入的文件却要用GBK编码。或者,你要读取(写入)的一个文件为非IO流默认编码。此时,我们的转换流就开始发挥作用了。
InputStreamReader--接收FileInputStream和编码格式--FileReader的父类
OutputStreamWriter--接收FileOutputStream和编码格式--FileWriter的父类
下面来看看对它的使用:
import org.junit.Before;
import org.junit.Test;
import java.io.*;
public class ISRAndOSW {
private File inFile = new File("E:/data/javaIO/a.csv");
private File outFile = new File("E:/data/javaIO/b.csv");
@Before
public void init() throws IOException {
if (!outFile.exists()) {
boolean res = outFile.createNewFile();
System.out.println(res);
}
}
// 共耗时115毫秒。
@Test
public void NoBufferedChar() throws IOException {
long start = System.currentTimeMillis();
InputStreamReader isr = new InputStreamReader(new FileInputStream(inFile), "UTF-8");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(outFile), "GBK");
char[] chars = new char[1024];
int len = 0;
while ((len = isr.read(chars)) != -1) {
osw.write(chars, 0, len);
}
osw.close();
isr.close();
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒。");
}
// 共耗时85毫秒。
@Test
public void BufferedChars() throws IOException {
long start = System.currentTimeMillis();
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(inFile), "UTF-8"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "GBK"));
int len = 0;
char[] chars = new char[1024];
while ((len = br.read(chars)) != -1) {
bw.write(chars, 0, len);
}
bw.close();
br.close();
long end = System.currentTimeMillis();
System.out.println("共耗时" + (end - start) + "毫秒。");
}
}
在使用IO流读取和写入文本文件时,最好是使用字符流BufferedReader、BufferedWriter去接收一个InputStreamReader、OutputStreamWriter的输出。也就是上面代码的第二种方式。这样可以在使用转换流的时候指定你的编码格式,有效避免乱码问题。
如果在转换过程中,字节流也用上buffer以更好的提升效率,那么这部分代码将变成这样:
import java.io.*;
import java.nio.charset.StandardCharsets;
public class Main {
public static void main(String[] args) throws Exception {
File file = new File("D:\\data\\data.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(file)), StandardCharsets.UTF_8));
char[] chars = new char[1024];
int len = 0;
while ((len = br.read(chars)) != -1) {
System.out.println(new String(chars, 0, len));
}
br.close();
}
}
这一句:BufferedReader br = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(file)), StandardCharsets.UTF_8));仿佛俄罗斯套娃一般,相对应的,BufferedWriter和输出流的写法,也类似。
综上,从简洁和效率上来说,输出流直接使用PrintStream是更明智的选择。