黑马程序员----二十一-IO流二

本文深入讲解Java中的字符流概念及应用,包括字符流的基本操作、拷贝文本文件的方法、使用缓冲区提高效率、如何反转文本内容等。同时探讨了字符流在不同场景下的适用性及其限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

今天学习IO流.

一.字符流
1.字符流是可以直接读写字符的IO流
2.字符流读取字符,就要先读取到字节数据,然后转为字符,如果要写出字符,需要把字符转为字节再写出.
3.Reader抽象类
 子类InputStreamReader
 子类FileReader

二.FileWriter

三.字符流的拷贝.
1.语句:
FileReader fr=new FileReader("a1.txt");
FileWriter fw=new FileWriter("a1copy.txt")
int ch;
while( (ch=fr.read()) != -1){
 fw.writer(ch);
}
fr.close();
fw.close();

2.Writer底层是有缓冲区的,有一个字符数组writeBuffer,大小是1024个字符,也就是2048个字节,2KB.
如果不关流,内容就在缓冲区里,如果关流,就会刷新缓冲区.

四.什么情况使用字符流
1.字符流可以拷贝文本文件,但是不推荐使用,因为读取时会把字节转为字符,写出时还要把字符转回字节.
2.程序需要读取一段文本,或者需要写出一段文本的时候可以使用字符流.
读取的时候是按照字符的大小读取的,不会出现半个中文,写出的时候可以直接将字符串写出,不用转换为字节数组.

五.字符流是否可以拷贝非纯文本的文件.
1.不可以拷贝非纯文本的文件
2.因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去.
如果是?问号,直接写出,这样写出之后的文件就乱了,看不了了.

六.自定义字符数组的拷贝
FileReader fr=new FileReader("a1.txt");
FileWriter fw=new FileWriter("a1copy.txt")
char[] arr=new char[1024*8];
int len;
while( (ch=fr.read(arr)) != -1){
 fw.write(arr,0,len);
}
fr.close();
fw.close();

七.带缓冲的字符流
BufferedReader br=new BufferedReader(new FileReader("a1.txt"));
BufferedWriter bw=new BufferedWriter(new FileWriter("a1copy.txt");
int ch;
while( (ch=br.read()) != -1){
 bw.write();
}
br.close();
bw.close();

八.readLine()和newLine()方法
1.BufferedReader的readLine()方法可以读取一行字符(不包含换行符号)
2.BufferedWriter的newLine()方法可以输出一个跨平台的换行符号"\r\n".
BR br;
BW bw;
String line;
while( (line=br.readLine()) != null){
 bw.write(line);
 //bw.write("\r\n");   /windows系统
 bw.newLine();
}
br.close();
bw.close();

九.将文本反转
分析:
1.创建输入输出流对象.
2.创建集合对象
3.将读到的数据存储在集合中
4.倒着遍历集合将数据写到文件上.
5.关流

BR br;
BW bw;
ArrayList<String> list =new ArrayList<>();
String line;
while( (line=br.readLine()) !=null){
 list.add(line);
}
for(int i=list.size()-1;i>=0;i--){
 bw.write(list.get(i));
 bw.newLine();
}
br.close();
bw.close();

注意:流对象尽量晚开早关
所以改写:
BR br;
ArrayList;
String line;
while{}
br.close();
BW bw;
for{}
bw.close();

十.LineNumberReader
1.两个方法
setLineNumber()和getLineNumber()

2.getLineNumber()
LineNumberReader lnr=new LineNumberReader(new FileReader("a1.txt");
String line;
while( (line=lnr.readLine())  != null){
 sysout(lnr.getLN+"+++"+line);
}

3.setLineNumber()
setLineNumber(100);
设置成100,那么第一行就是101.

十一.装饰设计模式
装饰设计模式就是对被装饰的对象的功能进行加强.

十二.使用指定的码表读写字符.
1.FileReader是使用默认码表读取文件,如果需要使用指定码表读取,那么可以使用InputStreamReader(字节流,编码表);

2.FileWriter是使用默认码表写出文件,如果需要使用指定码表写出,那么可以使用OutputStreamReader(字节流,编码表);

3.默认的编码表其实就是GBK.
UTF-8码表中一个中文是3个字节.

4.代码:
不能用FileReader,要用InputStreamReader
InputStreamReader isr=new InputStreamReader(new FileInputStream("utf-8.txt"),"utf-8");
OutputStreamWriter osw=new InputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
int c;
while(  (c=isr.read())  !=  -1){
 osw.write(c);
}
isr.close();
osw.close();

十三.转换流图解
utf-8.txt----------FileInputStream字节流----------InputStreamReader字节通向字符的桥梁,通过指定的编码表将字节转换为字符----------BufferedReader

BufferedWriter----------OutputStreamWriter字符通向字节的桥梁,通过指定的编码表将字符转换为字节----------FileOutputStream字节流----------gbk.txt

十四.获取文本上字符出现的次数.
分析:
1.创建带缓冲的输入流对象.
2.创建双列集合对象TreeMap
3.将读到的字符存储在双列集合中,存储的时候要做判断,如果不包含这个键,就将键和1存储,如果包含这个键,就将键和值加1存储
4.关闭输入流
5.创建输出流对象
6.遍历集合将集合中的内容写到times.txt中
7.关闭输出流.

BufferedReader br=new BufferedReader(new FileReader("a1.txt");
TreeMap<Character,Integer> tm=new TreeMap<>();
int ch;
while(  (ch=br.read())  !=  -1){
 char c=(char) ch;
 /*if(!tm.containsKey(c)){
  tm.put(c,1);
 }else{
  tm.put(c,tm.get(c)+1);
 }*/
 tm.put(c,!tm.containsKey(c)?1:tm.get(c)+1);
}
br.close();

BufferedWriter bw=new BufferedWriter(new FileWriter("times.txt"));
for(Character key:tm.keySet()){
 bw.write(key+"="+tm.get(key));
 bw.newLine();
}
bw.close();

修改:
for(Character key:tm.keySet()){
 switch(key){
 case'\t':
 bw.write("\\t"+"="+tm.get(key));
 break;

 case'\n':
 bw.write("\\n"+"="+tm.get(key));
 break;
 
 case'\r':
 bw.write("\\r"+"="+tm.get(key));
 break;

 default:
 bw.write(key +"="+tm.get(key));
 break;
 }
}

十五.试用版软件
当我们下载一个试用版软件,没有购买正版的时候,每执行一次就会提醒我们还有多少使用机会,用学过的IO流知识,模拟试用版软件,试用10此机会,执行一次就提示您还有几次机会,如果次数到了就提示请购买正版.

分析:
1.创建带缓冲的输入流对象,因为要使用readLine方法,可以保证数据的原样性
2.将读到的字符串转换为int数.
3.对int数进行判断,如果大于0,就将其--写回去,如果不大于0,就提示请购买正版
4.在if判断中要将--的结果打印,并将结果通过输出流写到文件上.

BufferedReader br=new BufferedReader(new FileReader("config.txt");
String line=br.readLine();
int times=Integer.parseInt(line);
if(times>0){
 sysout("剩"+times--+"次机会");
 FileWriter fw=new FileWriter("config.txt");
 fw.write(times+"");
 fw.close();
}else{
 sysout("用完");
}
br.close();

十六.File类(递归)
1.递归就是: 方法自己调用自己
例如:
method(){
 method();
}

2.5的阶乘  5!=5*4*3*2*1.
分析:
1.5*fun(4).
2.  4*fun(3).
3.      3*fun(2).
4.          2*fun(1).

代码:
public int fun(int num){
 if(num==1){
  return 1;
 }else{
  return num*fun(num-1);
 }
}

弊端:如果调用次数过多,就会导致栈内存溢出.
还有可能栈内存没有溢出,但是到6000-7000次左右的时候算出来的结果已经超出了int的范围,所以结果是0.

好处:不用知道循环的次数.

3.构造方法是否可以递归调用呢?
不能,因为无穷调用.

4.递归调用是否必须有返回值?
不一定

十七.练习
需求:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.java文件名.
分析:
1.如果录入的是不存在,给提示
2.如果录入的是文件路径,给提示
3.如果是文件夹路径,直接返回

打印出所有.java文件名
1.获取到该文件夹路径下所有的文件和文件夹,存储在File数组中
2.遍历数组,对每一个文件或文件夹做判断
3.如果是文件,并且后缀是.java的就打印
4.如果是文件夹,就递归调用

public static File getDir(){
 Scanner sc=new Scanner(S.in);
 sysout("请输入一个文件夹路径");
 while(true){
 String line=sc.nextLine();
 File dir=new File(line);
 if(!dir.exists()){
 sysout("文件夹路径不存在");
 }else if(dir.isFile()){
 sysout("是文件路径");
 }else{
 return dir;
 }
 }
}

public void printJavaFile(File dir){
 File[] subFiles=dir.listFiles();
 for(File subFile:subFiles){
 if(subFile.isFile()&&subFile.getName().endsWith(".java")){
 sysout(subFile);
 }else if(subFile.isDirectory()){
 printJavaFile(subFile);
 }
 }
}
main:
File dir=getDir();
printJavaFile(dir);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值