文件流
文件字节流FileInputStream和FileOutputStream
- FileInputStream和FileOutputStream是字节流,是节点流,数据源和目的地是文件。
- 复制文件需要分别创建一个输入流和输出流进行文件读写。
- 需要创建一个中转站,借助循环和中转站完成文件复制。
- 流使用后一定要关闭,这和垃圾回收没有关系。
【示例1】:复制文件
public class TestCopy1 {
public static void main(String[] args) throws IOException {
//1.创建一个输入流和输出流
InputStream fis = new FileInputStream(new File("e:/readme.txt"));
OutputStream fos = new FileOutputStream(new File("e:/readme2.txt"));
//2.使用输入流和输出流完成文件复制
//2.1准备一个中转站(水杯)
int n;
//2.2先读一个字节
n = fis.read();//读取一个字节,赋给n
while(n != -1){
//2.3再写一个字节
fos.write(n);
//2.4在读一个字节
n = fis.read();
}
//3.关闭输入流和输出流
fis.close();
fos.close();
}
}
- 缺点:中转站太小,速度慢,效率低;复制更大的文件时效果更明显;可以将中转站从一个字节变为一个字节数组,减少读写硬盘的次数。
- 问题:如果不是覆盖文件,而是追加内容,应该如何实现?
【示例2】复制文件(中转站是一个字节数组)
public class TestCopy2 {
public static void main(String[] args) throws IOException {
//1.创建流
// InputStream fis = new FileInputStream(new File("e:/readme.txt"));
// OutputStream fos = new FileOutputStream(new File("e:/readme.txt"));
InputStream fis = new FileInputStream("e:/readme.txt");
//OutputStream fos = new FileOutputStream("e:/readme2.txt");
OutputStream fos = new FileOutputStream("e:/readme2.txt", true);//追加而不是覆盖
//2.使用流
//2.1准备一个中转站(水桶)
byte [] buf = new byte[1024]; //2500=1024+1024+452
//2.2 使用输入流从数据源读取数据到中转站
//读取数据到中转站,数据在数组中转站中,返回的真正读取的字节数
int len = fis.read(buf);
while(len !=-1 ){
//2.3使用输出流把中转站数据写到到目的文件
//fos.write(buf);//将buf中转站内容写到文件中
fos.write(buf, 0, len);
//2.4在读一次
len = fis.read(buf);
}
//3.关闭流
fis.close();
fos.close();
}
}
文件字符流FileReader和FileWriter
- FileReader和FileWriter是字符流,节点流,数据源和目的地是文件。
【示例3】复制文件(中转站是一个字符)
public class TestCopy3 {
public static void main(String[] args) throws IOException {
//创建流
Reader fr = new FileReader("e:/readme.txt");
Writer fw = new FileWriter(new File("e:/readme2.txt"));
//使用流
//中转站是一个字符
//先读一个
int n= fr.read();//使用输入流读取一个字符char !!,不是字节
while(n!=-1){
//输出
System.out.println((char)n);
//写一个
fw.write(n);
//再读一个
n = fr.read();
}
//关闭流
fr.close();
fw.close();
}
}
【示例4】复制文件(中转站是一个字符数组,并进行异常处理)
public class TestCopy4 {
public static void main(String[] args) {
//创建流
Reader fr = null;
Writer fw = null;
try {
fr = new FileReader("e:/readme.txt");
fw = new FileWriter(new File("e:/readme2.txt"));
//使用流
//更大的中转站
char[] cbuf = new char[1024];
//读取的多个字符放入cbuf,返回的是读取的字符的个数
int len = fr.read(cbuf);
while (len != -1) {
//写
//fw.write(cbuf);
//fw.write(cbuf, 0, len);
fw.write(new String(cbuf, 0, len));
//再读
len = fr.read(cbuf);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
try {
if (fr != null) {
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fw != null) {
fw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 其实只有字节流,没有字符流。字符流的底层还是字节流,进行了封装转换,使开发者可以更简单的来处理非英文字符。
- 字节流可以完成所有类型文件的复制(文本、音频、视频、图片、chm);字符流只能完成文本文件的复制(txt、java),doc不是文本文件;字符流一般用来处理包含中文的文本文件。
- 异常处理的分析:创建流、使用流要使用一次try-catch语句,关闭流要分开进行异常处理。
缓冲流
缓冲字节流BufferedInputStream和BufferedOutputStream
【示例5】复制文件(使用缓冲流字节流提高效率)
public class TestCopy5 {
public static void main(String[] args) throws IOException {
//1.创建一个输入流和输出流
InputStream fis =
new FileInputStream(new File("e:/JDK_API.CHM"));
OutputStream fos =
new FileOutputStream(new File("e:/JDK_API2.CHM"));
//默认输入缓冲区大小8192
BufferedInputStream bis = new BufferedInputStream(fis);
//默认输出缓冲区大小8192
BufferedOutputStream bos = new BufferedOutputStream(fos);
//2.使用输入流和输出流完成文件复制
//2.1准备一个中转站(水杯)
int n;
//2.2先读一个字节
n = bis.read();//读取一个字节,赋给n
while(n != -1){
//在控制台显示该字节
//System.out.println((char)n);
//2.3再写一个字节
bos.write(n);
//2.4在读一个字节
n = bis.read();
}
//3.关闭输入流和输出流
//bos.flush();
bis.close();
bos.close();
}
}
- 缓冲流的原理
- 只要关闭高层流即可,底层流不用手动关闭;因为高层的关闭方法就是将底层流关闭。
- 如何刷新输出缓冲区(让缓冲区内容写入硬盘,保证一致)
1.满了就自动刷新
2.bos.close()先flush,再关闭(关闭的时候会先刷新)
3.手动刷新flush()
缓冲字符流BufferedReader和BufferedWriter
- 问题:之前的文件读写都是按照字节、字符或者数组来实现的,对于文本文件而言,能否按照行,一行行读写呢?
- 提供了BufferedReader和BufferedWriter实现按行读写。
【示例6】复制文件(按行读写)
public class TestCopy6 {
public static void main(String[] args) throws IOException {
//创建两个流
BufferedReader br =
new BufferedReader(new FileReader(new File("e:/sqlnet.log")));
BufferedWriter bw =
new BufferedWriter(new FileWriter("e:/sqlnet2.log"));
//使用两个流完成按行读取的功能
//中转站就是一个字符串,存储一行数据
//先读一行
String str = br.readLine();
while(str != null ){
//再写一行
bw.write(str);
//bw.write("\r\n");不同操作系统中换行符是不同的
bw.newLine();
//再读一行
str = br.readLine();//!!!
}
//关闭两个流
br.close();
bw.close();
}
}
总结1:BufferedReader和BufferedWriter的优点
1.速度快
2.简化编程
总结2:readLine()底层的原理
底层还是一个一个字符的读取,append()放入到StringBuilder(或者char[] )中,遇到换行符 ,将StringBuilder(char[])转换成String并返回
总结3:不同的操作系统中换行符是不同的
Unix系统里,每行结尾只有“<换行>”,即“\n”;
Windows系统里面,每行结尾是“<回车><换行>”,即“\r\n”;
Mac系统里,每行结尾是“<回车>”,即“\r”。