21.01_字符流FileReader
-
1.字符流是什么
- 字符流是可以直接读写字符的IO流
- 字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.
-
2.FileReader
-
FileReader
类的read()
方法可以按照字符大小读取
-
FileReader fr = new FileReader("xo.txt"); //可能FileNotFoundException
int c ;
while( (c = fr.read()) != -1) { //通过项目默认的码表,一次读取一个字符
System.out.print((char)c);
}
fr.close(); //可能IOException
21.02_字符流FileWriter
-
FileWriter
类的write()
方法可以自动把字符转为字节写出
FileWriter fw = new FileWriter("xo.txt", true); // true 是否追加到文本末尾
fw.write("--我会追加文本末尾--");
fw.write(97); // 97 就是字母a
fw.close();
21.03_IO流(字符流的拷贝)
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
int c ;
while ( (c = fr.read()) != -1) {
fw.write(c);
}
fr.close();
fw.close(); // Writer类中有2K的缓冲区,如果不关流,就有可能出现缓冲区的内容没有写到文件里
21.04_什么情况下使用字符流
- 字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.
- 程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流
-
Reader
读取的时候是按照字符的大小读取的,不会出现半个中文 -
Writer
写出的时候可以直接将字符串写出,不用转换为字节数组
21.05_字符流是否可以拷贝非纯文本的文件
- 不可以拷贝非纯文本的文件
- 因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去
- 如果是?,直接写出,这样写出之后的文件就乱了,看不了了
21.06_自定义字符数组的拷贝
// 自定义小数组
FileReader fr = new FileReader("xo.txt");
FileWriter fw= new FileWriter("a.txt");
char[] data = new char[1024 * 10];
int len;
while( (len = fr.read(data)) != -1) {
fw.write(data, 0, len);
}
fr.close();
fw.close();
21.07_IO流(带缓冲的字符流)
-
BufferedReader
的read()
方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率 -
BufferedWriter
的write()
方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率
BufferedReader br = new BufferedReader(new FileReader("xo.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
int c ;
while( (c = br.read()) != -1) { //read一次,会先将缓冲区读满,从缓冲去中一个一个的返给临时变量ch
bw.write(c); //write一次,是将数据装到字符数组,装满后再一起写出去
}
br.close();
bw.close();
21.08_readLine()和newLine()方法
-
BufferedReader
的readLine()
方法可以读取一行字符(不包含换行符号) -
BufferedWriter
的newLine()
可以输出一个跨平台的换行符号\r\n
// 带缓冲区的特殊方法: readLine() newLine()
BufferedReader br = new BufferedReader(new FileReader("xo.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
String line ;
while( (line = br.readLine()) != null) { //读取一行,以 \r 或 \n 为换行标识
bw.write(line);
// bw.write("\r\n"); 这个也是换行,但是只支持windows系统
bw.newLine(); // 这个换行是跨平台的
}
br.close();
bw.close();
21.09_将文本反转
- 将一个文本文档上的文本反转,第一行和倒数第一行交换,第二行和倒数第二行交换
- 资源的读取,最好符合 晚开早关 原则
// 文本反转
BufferedReader br = new BufferedReader(new FileReader("xo.txt"));
ArrayList<String> list = new ArrayList<>();
// 将读取的数据存储到集合中
String line ;
while( (line = br.readLine()) != null) {
list.add(line);
}
br.close();
// 倒着遍历集合,将数据写到文件上
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt",true));
for (int i = (list.size() - 1); i >= 0; i--) {
bw.write(list.get(i));
bw.newLine();
}
bw.close();
21.10_LineNumberReader
-
LineNumberReader
是BufferedReader
的子类, 具有相同的功能, 并且可以统计行号- 调用
getLineNumber()
方法可以获取当前行号 - 调用
setLineNumber()
方法可以设置当前行号
- 调用
LineNumberReader lr = new LineNumberReader(new FileReader("xo.txt"));
lr.setLineNumber(100); //设置行号,那么后续的行号,将是此值的递增
String line;
while((line = lr.readLine()) != null) {
System.out.println(lr.getLineNumber() + " " +line); // getLineNumber获取行号,从101开始
}
lr.close();
21.11_装饰设计模式
23种设计模式之一,英文叫Decorator Pattern,又叫装饰者模式。装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。(Java IO流是典型的装饰设计模式)
- 装饰对象和真实对象有相同的接口
- 装饰对象包含一个真实对象的引用(reference)
interface Coder {
public void coder(); // 编码技能
}
class Student implements Coder {
public void coder() {
System.out.println("学生学会了编码技能...");
}
}
class PeiXunStudnet implements Coder { //培训过的学生 ,不是通过继承实现的扩展
private Student s ;
public PeiXunStudnet(Student s) { //装饰模式,一般通过构造方法获取被装饰的类
this.s = s ;
}
// getter 和 setter 方法
public Student getS() { return s;}
public void setS(Student s) { this.s = s;}
// 对学生进行升级
public void coder() {
s.coder(); //学生已经学会的技能 , 下面对学生进行技能扩展..
System.out.println("学生学会了 ssh");
System.out.println("学生学会了 高级数据库编程");
}
}
21.11_装饰设计模式 和 继承的区别
装饰设计模式:
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。
而继承是通过成为某个类的子类,实现功能的扩展。
所以:装饰模式比继承要灵活。避免了继承体系臃肿(降低了继承造成的耦合)。而且降低了类于类之间的关系。
装饰模式的缺点: 导致程序中出现许多小类,过度使用会使程序变的复杂。
21.12_使用指定的码表读写字符)
-
FileReader
是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader
(字节流,编码表) -
FileWriter
是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用OutputStreamWriter
(字节流,编码表)
// 指定码表读写字符,注意,后面的码表,不分大小写
InputStreamReader ir = new InputStreamReader(new FileInputStream("utf-8.txt"),"utf-8");
OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
int c ;
while( (c = ir.read()) != -1) {
ow.write(c);
}
ow.close();
ir.close();
// 使用包装类,带缓存和指定编码表的读写
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream("utf-8.txt"), "UTF-8"));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("gbk.txt"),"GBK"));
int c ;
while( (c = br.read()) != -1) {
bw.write(c);
}
br.close();
bw.close();
21.13_转换流图解
转换流.png其实这个地方应该很好理解的,读写文件有字节流和字符流。我想使用字节流读写文件,但是写文件时候,我需要另外一种编码格式,所以肯定需要一个 字节流和字符流互相转换的类,既然互相转换,所以肯定得有个 编码表 来依照参考。InputStreamReader
和OutputStreamWriter
就是互相转换字节流和字符流的类,因为二者继承于 Reader
和 Writer
,所以属于字符流体系下。
21.14_获取文本上字符出现的次数
- 获取一个文本上每个字符出现的次数,将结果写在times.txt上
BufferedReader br = new BufferedReader(new FileReader("xo.txt"));
TreeMap<Character, Integer> map = new TreeMap<>();
int c ;
while( (c = br.read()) != -1) { //注意这里的强制类型转换
map.put((char) c, map.containsKey((char)c) ? map.get((char)c)+1 : 1);
}
br.close();
BufferedWriter bw = new BufferedWriter(new FileWriter("times.txt"));
// 双列集合Map的遍历
for (Character key : map.keySet()) {
switch (key) { //注意下面的转义的使用
case '\t':
bw.write("\\t" + "-->" + map.get(key) + "次");
break;
case '\n':
bw.write("\\n" + "-->" + map.get(key) + "次");
break;
case '\r':
bw.write("\\r" + "-->" + map.get(key) + "次");
break;
case ' ':
bw.write("空格" + "-->" + map.get(key) + "次");
break;
default:
bw.write(key + "-->" + map.get(key) + "次");
break;
}
bw.newLine();
}
bw.close();
21.15_IO流(试用版软件)
当我们下载一个试用版软件,没有购买正版的时候,每执行一次就会提醒我们还有多少次使用机会用学过的IO流知识,模拟试用版软件,试用10次机会,执行一次就提示一次您还有几次机会,如果次数到了提示请购买正版
核心代码:
BufferedReader br = new BufferedReader(new FileReader("config.txt"));
String line = br.readLine();
br.close();
int num = Integer.parseInt("10"); //将数字字符串转为int
// 注意,下面这句,不能写到readLine()之前,因为构造Writer会清空源文件内容
BufferedWriter bw = new BufferedWriter(new FileWriter("config.txt"));
bw.write("8");
bw.close(); // close()千万千万不能忘记!!!!
21.16_递归,方法自己调用自己
递归通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
注意 :
- 递归可能造成栈内存溢出 ,因为方法不弹栈,一直在栈里循环调用,超过栈内存限制就会内存崩溃了。
- 构造方法不能递归调用,造成死循环,堆内存溢出。
- 递归方法不一定非有返回值。
阶乘递归
public int fun(int num) { //三目不好理解,可以转为if
return num == 1 ? 1 : num * fun(num - 1);
}
整数递减求和
public int sum(int x) { //三目不好理解,可以转为if
return x > 0 ? x + sum(x-1) : x ;
}
21.17_File类(递归练习)
- 需求:从键盘输入接收一个文件夹路径,打印出该文件夹下所有的.java文件名
/**
* 递归获取 文件夹及其子目录下的java文件
* @param file 文件夹路径
*/
public static void printJavaFile(File file) {
File list[] = file.listFiles();
for (File subFile : list) {
if (subFile.isFile() && subFile.getName().endsWith(".java")) {
System.out.println(subFile);
}else if (subFile.isDirectory()){ //是文件夹,就进入递归
printJavaFile(subFile);
}
}
}
21.18_IO流(总结)
- 1.会用
BufferedReader
读取GBK码表和UTF-8码表的字符 - 2.会用
BufferedWriter
写出字符到GBK码表和UTF-8码表的文件中 - 3.会使用
BufferedReader
从键盘读取一行(readLine()
)
END。
我是小侯爷。
在魔都艰苦奋斗,白天是上班族,晚上是知识服务工作者。
如果读完觉得有收获的话,记得关注和点赞哦。
非要打赏的话,我也是不会拒绝的。
网友评论