根本原因是由于人和机器的差距:机器擅长处理二进制的机器码,而人只能处理特殊的字符(如"a",“你好”)。于是,采用字符流来作为人能读的文件
与机器能读的文件
之间的转换桥梁。
诞生背景:
- 字节流对所有的文件操作都是按照一个个二进制位的形式;
- 然而,字节流在操作字符时,可能会有中文导致的乱码,所以由字节流引申出了字符流。
为什么要诞生字符流:
- 机器擅长处理二进制的机器码,而人只能处理特殊的字符(如"a",“你好”)。
- 于是,采用字符流来作为
人能读的文件
与机器能读的文件
之间的转换桥梁。
字符流的功能:
- 把读取到的二进制数据进行对应的编码和解码工作;从而把二进制位转换成程序员期待的字符集。
- 字符流=字节流+编码(解码);
字符流的弊端:
- 一:无法拷贝图片和视频。
- 二:拷贝文件使用字节流而不使用字符流,因为字符流读文件涉及到解码,会先解码,写文件的时候又涉及到编码,这些操作多余,而且读和写的码表不对应还容易引发问题。
- 例如FileReader 读文件,我们没有指定编码时,默认是按照系统编码gbk进行操作,如果读到utf-8的文件也是按照gbk编码进行解码,那就会出现问题。
字符流体系结构
输入字符流:
---------| Reader
所有输入字符流的基类。 抽象类。
----------------| FileReader
读取文件数据的输入字符流。
----------------|BufferedReader
缓冲输入字符流。该类出现的目的是为了提高读取文件数据的效率与拓展FileReader的(readLine)功能。这个类的也只不过是在内部维护了一个8kb的字符数组而已。
输出字符流:
---------| Writer
所有输出字符流的基类。 抽象类
----------------| FileWriter
向文件输出数据的输出字符流
----------------| BufferedWriter
缓冲输出字符流。该类出现的目的是为了提高写文件数据的效率与拓展FileWriter的(newLine)功能。
FileReader
FileReader类操作步骤、方法与FileInputStream类相同;缓冲流的使用方法与FileInputStream类相同,即bufferedReader包装FileReader
读取方法:
1,int read()
:一次读取一个字符。返回的是读到的那个字符的int值。如果读到流的末尾,返回-1.
2,int read(char[])
:将读到的字符存入指定的数组中,返回的是读到的字符个数,也就是往数组里装的元素的个数。如果读到流的末尾,返回-1.
快速理解FileReader的用法:
import java.io.IOException;
import java.io.FileReader;
public class Main {
public static void main(String[] args) throws IOException {
readOneCharacter(); //读取一个字符
readCharacterArray(); //读取一个字符数组
}
public static void readOneCharacter() throws IOException {
FileReader fr = new FileReader("c:\\users\\kyles\\desktop\\a.txt");
int ch;
while((ch=fr.read())!=-1) {
System.out.print((char)ch);
}
fr.close();
}
public static void readCharacterArray() throws IOException {
FileReader fr = new FileReader("c:\\users\\kyles\\desktop\\a.txt");
char[] chs = new char[1024]; //这里可以是1024及其整数倍
int len;
while((len=fr.read(chs))!=-1) {
System.out.print(new String(chs,0,len));
}
fr.close();
}
}
FileWriter
使用FileWriter写数据的时候
- FileWriter内部是维护了一个
1024个字符数组
的,写数据的时候会先写入到它内部维护的字符数组中,如果需要把数据真正写到硬盘上,需要调用flush或者是close方法或者是填满了内部的字符数组
。 - 如果目标文件不存在,那么会自动创建目标文件。
- 如果目标文件已经存在了,那么默认情况会先清空文件中的数据,然后再写入数据。
向磁盘文件写数据的五种方法:(五种write方法的重载)
public void write(int c) throws IOException 写入的字符包含在给定整数值的16个低位中; 忽略16个高位。
public void write(char[] cbuf) throws IOException
public abstract void write(char[] cbuf, int off, int len) throws IOException
public void write(String str) throws IOException
public void write(String str, int off, int len) throws IOException
向一个文件中写入一个字符串
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException { //为了快速理解,异常直接向函数外抛
FileWriter fw = new FileWriter("C:\\users\\kyles\\desktop\\a.txt");
fw.write("IO流你好"); //数据没有直接写到文件,其实是写到了内存缓冲区
fw.flush(); //把缓冲区的数据写入到文件
fw.close(); //先刷新缓冲区,然后通知系统释放资源。
}
}
五种write方法示例
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//创建输出流对象
FileWriter fw = new FileWriter("b.txt");
//void write(String str):写一个字符串数据
fw.write("abcde");
//void write(String str,int index,int len):写一个字符串中的一部分数据
fw.write("abcde",0,5);
fw.write("abcde",1,3);
//void write(int ch):写一个字符数据,这里写int类型的好处是既可以写char类型的数据,也可以写char对应的int类型的值。'a',97
fw.write('a');
fw.write(97);
//void write(char[] chs):写一个字符数组数据
char[] chs = {'a','b','c','d','e'};
fw.write(chs);
//void write(char[] chs,int index,int len):写一个字符数组的一部分数据
fw.write(chs,0,5);
fw.write(chs,2,3);
//释放资源
fw.close();
}
}
文件追加
- 在构造函数时就设置为追加模式:
FileWriter(String fileName, boolean append)
FileWriter fw = new FileWriter("c.txt",true); //表示追加写入,默认是false
FileWriter(File file, boolean append)
- 使用append方法
Writer append(char c); //将制定字符添加到此Writer
Writer append(CharSequence csq); //将制定字符序列添加到此Writer
Writer append(CharSequence csq, int start, int end);
示例:
import java.io.FileWriter;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("c:\\users\\kyles\\desktop\\a.txt");
for(int x=0; x<10; x++) {
fw.write("hello"+x);
fw.write("\r\n");
}
fw.close();
}
}
关于换行符
\n
可以实现换行,但是windows系统自带的记事本打开并没有换行,这是为什么呢?——因为windows识别的换行不是\n
,而是\r\n
- windows:\r\n
- linux:\n
- mac:\r
字符缓冲流
Reader有一个子类BufferedReader。子类继承父类显然子类可以重写父类的方法,也可以增加自己的新方法。例如一次读一行就是常用的操作。那么BufferedReader 类就提供了这个方法,可以查看readLine()方法具备 一次读取一个文本行的功能。很显然,该子类可以对功能进行增强。
两类字符缓冲流:
缓冲流的特殊功能(特殊方法):
- BufferedWriter
void newLine():写一个换行符,这个换行符由系统决定 - BufferedReader
String readLine():一次读取一行数据,但是不读取换行
缓冲流使用示例
import java.io.IOException;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
public class Main {
public static void main(String[] args) throws IOException {
Wr();
Re();
}
public static void Wr() throws IOException {
File path = new File("c:\\users\\kyle\\desktop\\a.txt");
FileWriter fw = new FileWriter(path);
BufferedWriter bw = new BufferedWriter(fw);
for(int i=0;i<10;i++) {
bw.write(i + ": hello");
bw.newLine();//相当于:bw.write("\r\n");
bw.flush();
}
bw.close();
}
public static void Re() throws IOException {
File path = new File("c:\\users\\kyle\\desktop\\a.txt");
FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr);
String line;
while((line = br.readLine())!=null) {
System.out.println(line);
}
fr.close();
}
}