IO字符流的使用
使用字节流写字符
可以使用,但是需要先把字符串转成字节数组,再存储到文件中,比较麻烦
使用字节流读取字符
如果是纯英文,可以一次读取一个字节
如果是纯中文,可以一次读取两个字节(GBK)
如果是纯中文,而且UTF-8的编码一次性读取三个字节
如果是中英文混杂,每次不知道读取多少个字节,因此无论字节数组准备多大,都会出现乱码
UTF-8中汉字占三个字节
GBK中汉字占2个字节
英文一般只占一个字节
//IO字符流的使用
System.out.println("a".getBytes().length); //英文字符占用长度
System.out.println("你".getBytes().length); //汉字字符占用长度
//IO字符流 每次读入的是一个字符
//构造方法 用法基本上和字节流一致
Reader reader = new FileReader("");
Writer writer = new FileWriter("");
reader.close();
writer.close();
常用方法
reader.read(); //也有如下方法
writer.write(cbuf); //也可写入字符数组,格式为writer(char[] b,int start, int len);
//从数组索引的第几个开始写,写几个
Reader r1 = new FileReader("cc.txt");
int i1; //int i1; 不可换为char i1; 会出错
while ((i1 = r1.read())!=-1) { //读入一个字符 但输出使用的是i1 所以显示编码
System.out.println(i1); //要转为char 需要加入(char) 在i1前
}
r1.close();
FileReader rf1 = new FileReader("cc.txt");
char[] c1 = new char[1]; //定义字符数组接收
int f1;
while ((f1 = rf1.read(c1))!=-1) {
System.out.print(new String(c1)); //输出的还是字符形式
}
rf1.close();
字符流拷贝
使用字符输入流读取信息,使用字符输出流写出信息,完成文件的使用字符流拷贝
没有必要使用字符流来进行拷贝,因为字符流会先将字节信息转成字符,读取到内存中,最后还要再把字符信息转成字节信息,写出到另外一个文件中。
中间做了两次没有意义的相反的操作,浪费了时间
//字符流 拷贝
// 如果在读取到字符之后,需要人为的阅读和修改这个字符,那么就需要使用字符流
// 如果只是简单的将信息进行转移或者拷贝,不供阅读,就不需要转成字符
Reader r2 = new FileReader("cc.txt");
Writer w2 = new FileWriter("d.txt");
char[] c2 = new char[2];
int i;
while ((i = r2.read(c2))!=-1) {
w2.write(c2,0,i); //读多少写多少
}
r2.close();
w2.close();
非纯文本文件不能使用字符流拷贝
当字符流读取到一个字节信息之后,需要查询编码表转成字符信息,如果是非纯文本文件,就可能读取到的字节信息无法转成对应的字符,因为这个字节信息在编码表中没有对应的字符,就只能使用英文的?来代替这个字符,这一步中,就对信息进行篡改。接下来将篡改之后的?字符信息,通过编码表转成字节信息(这一步虽然正确,但是已经没有意义),转成的字节信息,就和当初字符输入流读取的字节信息不一致了,写出到目标文件,就是错误信息
字符流小数组拷贝
不使用小数组进行拷贝,发现效率也比较高,因为在FileWriter中就有一个缓冲区数组,所以写出的效率就比较高。
在我们使用小数组之后,还可以提升读取字符的速度,最终也可以提升拷贝效率
//不使用小数组的拷贝
Reader r3 = new FileReader("cc.txt"); //16毫秒 6394字节(byte)
Writer w3 = new FileWriter("d.txt");
int i3;
while ((i3 = r3.read())!=-1) {
w3.write(i3);
}
r3.close();
w3.close();
//使用小数组拷贝
Reader r4 = new FileReader("cc.txt"); //4毫秒 6394字节(byte)
Writer w4 = new FileWriter("d.txt");
char[] c4 = new char[5];
int len4;
while ((len4 = r4.read(c4))!=-1) {
w4.write(c4,0,len4); //使用多个读入时必须写成(变量 ,0,len)防错
}
r4.close();
w4.close();
//其实提升也不小
//不使用小数组进行拷贝,发现效率也比较高,因为在FileWriter中就有一个缓
//冲区数组,所以写出的效率就比较高。
//在我们使用小数组之后,还可以提升读取字符的速度,最终也可以提升拷贝效率
高效缓冲字符流
BufferedReader BufferedWriter
BufferedReader:每次调用read方法,只有第一次从磁盘中读取了8192个字符,存储到该类型对象的缓冲区数组中,将其中一个返回给调用者,再次调用read方法时,就不需要再访问磁盘,直接从缓冲区中拿一个出来即可,效率提升了很多。
BufferedWriter:每次调用write方法,不会直接将字符刷新到文件中,而是存储到字符数组中,等字符数组写满了,才一次性刷新到文件中,减少了和磁盘交互的次数,提升了效率
Reader r5 = new FileReader("cc.txt"); //6毫秒 6394字节(byte)(B)
BufferedReader br1 = new BufferedReader(r5);
BufferedWriter bw1 = new BufferedWriter(new FileWriter("d.txt"));
int i5;
while ((i5 = br1.read())!=-1) {
bw1.write(i5);
}
br1.close();
bw1.close();
高效缓冲字符流的特有方法
//高效缓冲字符流的特有方法
br1.readLine(); //一次读入一行数据,到达末尾返回null
bw1.newLine(); //为不同的操作系统提供不同的相对应的换行符
//在一般的写入中可以在write方法中加 换行符来换行,Buffer中不能直接加入换行符
BufferedReader br2 = new BufferedReader(new FileReader("cc.txt"));
String s1; //读入的为字符串用字符串接收
while ((s1 = br2.readLine())!=null) { //最后的结尾为null
System.out.println(s1);
}
br2.close();
使用特有方法的拷贝
//使用特有方法的拷贝
BufferedReader bf3 = new BufferedReader(new FileReader("src/zG_review_practice/IO字"
+ "符流.java"));
BufferedWriter bw3 = new BufferedWriter(new FileWriter("d.txt"));
String s3;
while ((s3 = bf3.readLine())!=null) {
bw3.write(s3);
}
bf3.close();
bw3.close();
读入文件 以倒序写入另一个文件
//读入文件 以倒序写入另一个文件
BufferedReader br4 = new BufferedReader(new FileReader("cc.txt"));
BufferedWriter bw4 = new BufferedWriter(new FileWriter("d.txt"));
List<String> list = new ArrayList<>();
String s4;
while ((s4 = br4.readLine())!=null) {
list.add(s4);
}
br4.close(); //关闭资源
Collections.reverse(list); //反转元素位置
for (String string : list) {
bw4.write(string);
bw4.newLine(); //换行
}
bw4.close();
小知识
GBK:国标码,定义的是英文字符和中文字符。在GBK编码表中,英文字符占一个字节,中文字符占两个字节。
UTF-8:万国码,定义了全球所有语言的所有符号,定义了这些符号和数字的对应关系,英文字符使用一个字节进行存储,中文字符使用三个字节进行存储
OutputStreamWriter:字符流到字节流的桥梁,可以指定编码形式
构造方法:OutputStreamWriter(OutputStream os, String charSetName)
创建一个转换流对象,可以把将来方法中接收到的字符,通过指定的编码表
charSetName,编码成字节信息,再通过指定的字节流os,将字节信息写出
使用:直接使用Writer中的方法即可(该类是Writer的子类)
InputStreamReader:字节流到字符流的桥梁,可以指定编码形式
构造方法:InputStreamReader(InputStream is, String charSetName)
创建一个转换流对象,可以使用is这个指定的字节流,从磁盘中读取字节信息
,通过指定的编码表charSetName,将字节信息解码成字符信息,返回给调用者
使用:直接使用Reader类中的方法即可(该类是Reader的子类)
说明:
无论是读取的时候,还是写出的时候,都需要参考读取文件和目标文件的编码形式
读取源文件时,解码的形式必须和源文件的编码形式一致
写出到目标文件时,编码形式必须和目标文件的编码形式一致
字节流转成字符流,直接取字符,避免直接将字节转字符产生乱码的问题
InputStreamReader isr= new InputStreamReader(new FileInputStream("src/com/ujiue/test/Demo09_流的转换.java"), "UTF-8");
char[] c=new char[10];
int len;
while((len=isr.read(c))!=-1) {
System.out.print(c);
}
public static void du() throws IOException { //每一次执行都对读的内容修改并重新写入
Reader r1 = new FileReader("22.txt"); //这里的读入与写入不能同时定义否则会出错
int i = r1.read();
i-=1;
Writer w1 = new FileWriter("22.txt");
w1.write(i);
r1.close();
w1.close();
}
(仅作为Java 入门学习代码,实战中文件过大时不适合一次全部读取再一次全部写入)