IO流综合练习1
一、字节流和字符流的使用场景
1、字节流
- 拷贝任意类型的文件
2、字符流
- 读取纯文本文件中的数据
- 往纯文本文件中写出数据
二、综合练习
1、拷贝文件夹
- 需求:拷贝一个文件夹,考虑子文件夹
package com.app.demo36_io_test;
import java.io.*;
import java.util.Arrays;
public class Test1 {
public static void main(String[] args) throws IOException {
/*
IO流综合练习:
1、拷贝文件夹
需求:拷贝一个文件夹,考虑子文件夹
分析:
(1)一旦看到拷贝,就要想到字节流
(2)一旦看到文件夹,就要想到一个套路
套路:
a.进入数据源
b.遍历数组
c.判断是文件,要干嘛?
d.判断是文件夹,要干嘛?
*/
// 1.创建File对象表示数据源
File src = new File("D:\\JavaSE\\aaa");
// 2.创建File对象表示目的地
File dest = new File("D:\\JavaSE\\dest");
// 3.拷贝文件夹
copyFiles(src, dest);
}
/*
作用:拷贝文件夹
参数一:数据源
参数二:目的地
*/
public static void copyFiles(File src, File dest) throws IOException {
// 考虑:假如目的地不存在
// 如果dest文件夹不存在,那就创建成功
// 如果dest文件夹已存在,那就创建失败,程序不会报错
dest.mkdirs();
// 1.进入数据源: 获取所有文件或文件夹的路径的数组
File[] files = src.listFiles();
// 2.遍历数组: 获取所有文件或文件夹的路径
for (File file : files) {
if (file.isFile()) {
// 3.判断是文件,要拷贝
// new FileInputStream(要拷贝的文件);
// new FileOutputStream(文件的目的地);
// 创建字节输入流对象,表示要拷贝的文件
FileInputStream fis = new FileInputStream(file);
// 创建字节输出流对象,表示文件的目的地:参数一表示父级目录;参数二表示子级目录
FileOutputStream fos = new FileOutputStream(new File(dest, file.getName()));
// 一次拷贝一个字节数组的数据
byte[] bytes = new byte[1024];
int len; // 定义变量,记录每次读取的字节数(防止数据多倒出)
while ((len = fis.read(bytes)) != -1) {
// 读取多少倒出多少(保证数据的完整性)
fos.write(bytes, 0, len);
}
// 关闭流:先创建的后关闭
fos.close();
fis.close();
} else {
// 4.判断是文件夹,要递归
copyFiles(file, new File(dest, file.getName()));
}
}
}
}
Process finished with exit code 0
2、加密和解密文件
-
需求:为了保证文件的安全性,就需要对原始文件进行加密存储,再使用的时候再对其进行解密处理。
-
加密原理:
对原始文件中的每一个字节数据进行更改,然后将更改以后的数据存储到新的文件中。
-
解密原理:
读取加密之后的文件,按照加密的规则反向操作,变成原始文件。
package com.app.demo36_io_test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test2 {
public static void main(String[] args) throws IOException {
/*
IO流综合练习:
2、加密和解密文件
需求:为了保证文件的安全性,就需要对原始文件进行加密存储,再使用的时候再对其进行解密处理。
加密原理:
对原始文件中的每一个字节数据进行更改,然后将更改以后的数据存储到新的文件中。
解密原理:
读取加密之后的文件,按照加密的规则反向操作,变成原始文件。
分析:
^ : 异或
两边相同:false
两边不同:true
100 ^ 10 = 110
这个结果是在底层将两边的数据都转换成二进制之后然后计算之后的结果
当你用:110 ^ 10 的时候,结果就会是 100
你会发现还原了,此时就会发现这个特性:
当使用原始数据 ^ 一个任意数字的时候,可以更改数据
当使用更改后的数据再 ^ 一个任意数字的时候,可以还原数据
*/
// 加密操作:
// encryptAndDecode(原始文件路径, 更改后的文件路径);
// 创建字节输入流对象关联原始文件
FileInputStream fis1 = new FileInputStream("D:\\JavaSE\\all.jpg");
// 创建字节输出流对象关联加密文件
FileOutputStream fos1 = new FileOutputStream("D:\\JavaSE\\encrypt.jpg");
// 加密
encryptAndDecode(fis1, fos1);
// 释放资源
fos1.close();
fis1.close();
// 解密操作:
// encryptAndDecode(加密文件路径, 解密文件路径);
// 创建字节输入流对象关联加密文件
FileInputStream fis2 = new FileInputStream("D:\\JavaSE\\encrypt.jpg");
// 创建字节输出流对象关联解密文件
FileOutputStream fos2 = new FileOutputStream("D:\\JavaSE\\decode.jpg");
// 解密
encryptAndDecode(fis2, fos2);
// 释放资源
fos2.close();
fis2.close();
}
/*
作用:给文件进行加密和解密
参数一:原始文件
参数二:加密文件
*/
public static void encryptAndDecode(FileInputStream fis, FileOutputStream fos) throws IOException {
// 定义变量,记录每次读取的数据
int b;
while ((b = fis.read()) != -1) {
// 将数据进行加密操作
fos.write(b ^ 2);
}
}
}
Process finished with exit code 0
3、修改文件中的数据
-
需求:
文本文件中有这些数据:2-1-9-4-7-8
将文件中的数据进行排序,变成:1-2-4-7-8-9
package com.app.demo36_io_test;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class Test3 {
public static void main(String[] args) throws IOException {
/*
IO流综合练习:
3、修改文件中的数据
需求:
文本文件中有这些数据:2-1-9-4-7-8
将文件中的数据进行排序,变成:1-2-4-7-8-9
分析:
一旦看到需要对纯文本文件进行数据操作的时候,就要想到字符流
细节:
1.文件的数据不要换行
2.创建文件的时候,编码一定不要带有bom头
(系统文件可以在另存为可以更改编码)
(IDEA文件可以在File——Settings——Editor——File Encodings更改编码)
*/
// 创建字符输入流对象关联原始文件
FileReader fr = new FileReader("comprehensive_exercises\\aaa\\a.txt");
// 方式1
// updateFileData1(fr);
// 方式2
updateFileData2(fr);
}
/*
方式2
作用:对文件中的数据进行升序排序
参数一:要操作的文件
*/
public static void updateFileData2(FileReader fr) throws IOException {
// 创建StringBuilder对象,用于存储在文件中读取到的数据
StringBuilder sb = new StringBuilder();
// 1.读取数据
int ch; // 定义变量,用于记录每次读取到的数据字节数
while ((ch = fr.read()) != -1) {
sb.append((char) ch);
}
// 释放资源
fr.close();
// 2.排序
Integer[] arr = Arrays.stream(sb.toString() // 将sb转换成String
.split("-")) // 按照 "-" 切割
.map(Integer::parseInt) // 将每个切割的字符串数字 转换成 整型数字
.sorted() // 按照升序进行排序
.toArray(Integer[]::new); // 将所有转换后的数字 放到 整数数组里
// 3.写出数据
// 创建字符输出流对象关联修改后的文件
FileWriter fw = new FileWriter("comprehensive_exercises\\aaa\\a.txt");
String s = Arrays.toString(arr) // 将整型数组转成字符串
.replace(", ", "-"); // 将字符串中所有的 ", " 替换成 "-"
String result = s.substring(1, s.length() - 1); // 截取字符串1索引 到 末尾的数据
fw.write(result); // 写出
// 释放资源
fw.close();
}
/*
方式1:
作用:对文件中的数据进行升序排序
参数一:要操作的文件
参数二:操作后的文件
*/
public static void updateFileData1(FileReader fr) throws IOException {
// 1.创建StringBuilder对象,用于存储在文件中读取到的数据
StringBuilder sb = new StringBuilder();
// 2.读取文件中的数据
int ch; // 定义变量,用于记录每次读取到的数据字节数
while ((ch = fr.read()) != -1) {
// 将每次读取到的数据拼接给sb
sb.append((char) ch);
}
// 释放资源
fr.close();
// 3.排序
// 将sb转换成字符串,并按照 "-" 进行切割,得到每个字符串数字存储到字符串数组中
String[] arr = sb.toString().split("-");
// 创建Integer类型的集合,用于存储数字
ArrayList<Integer> list = new ArrayList<>();
// 遍历arr字符串数组,依次得到每个数据
for (String s : arr) {
// 将每个字符串数字 转换成 整型数字,并添加到集合中
list.add(Integer.parseInt(s));
}
// 将集合中的数据进行升序排序
Collections.sort(list);
// 4.写出数据
// 创建字符输出流对象关联修改后的文件
FileWriter fw = new FileWriter("comprehensive_exercises\\aaa\\a.txt");
// 遍历list集合,依次得到每个数字
for (int i = 0; i < list.size(); i++) {
// 判断当前这个数字索引是否为集合中的最后一个数字索引
if (i == list.size() - 1) {
// 是最后一个,则直接以字符串的形式写出数据
fw.write(list.get(i) + "");
}else {
// 不是最后一个,则用 "-" 隔开的形式写出数据
fw.write(list.get(i) + "-");
}
}
// 释放资源
fw.close();
}
}
Process finished with exit code 0
- 修改成功:
4、四种拷贝方式效率对比
(1)字节流的基本流:一次读写一个字节
(2)字节流的基本流:一次读写一个字节数组
(3)字节缓冲流:一次读写一个字节
(4)字节缓冲流:一次读写一个字节数组
package com.app.demo36_io_test;
import java.io.*;
public class Test4 {
public static void main(String[] args) throws IOException {
/*
IO流综合练习:
4、四种拷贝方式效率对比
(1)字节流的基本流:一次读写一个字节
(2)字节流的基本流:一次读写一个字节数组
(3)字节缓冲流:一次读写一个字节
(4)字节缓冲流:一次读写一个字节数组
*/
// 获取开始时间
long startTime = System.currentTimeMillis();
// 运行四种拷贝的方法
// copy1(); // 超慢,拷贝文件不推荐使用这种方式
// copy2(); // 快:1.516秒 (推荐)
// copy3(); // 中等:5.503秒
copy4(); // 快:1.485秒(推荐)
// 获取结束时间
long endTime = System.currentTimeMillis();
// 计算拷贝消耗的时间
System.out.println("该拷贝方法共耗时:" + ((endTime - startTime) / 1000.0) + "秒");
}
/*
方式1:字节流的基本流:一次读写一个字节
*/
private static void copy1() throws IOException {
// 创建字节输入流对象,关联数据源
FileInputStream fis = new FileInputStream("D:\\JDK\\jdk-11.0.15_windows-x64_bin.exe");
// 创建字节输入流对象,关联目的地
FileOutputStream fos = new FileOutputStream("comprehensive_exercises\\aaa\\copy.exe");
// 拷贝数据
int b; // 记录每次读取的字节数
// 一次读写一个字节
while ((b = fis.read()) != -1) {
// 将读写到的数据写出
fos.write(b);
}
// 释放资源
fos.close();
fis.close();
}
/*
方式2:字节流的基本流:一次读写一个字节数组
*/
private static void copy2() throws IOException {
// 创建字节输入流对象,关联数据源
FileInputStream fis = new FileInputStream("D:\\JDK\\jdk-11.0.15_windows-x64_bin.exe");
// 创建字节输入流对象,关联目的地
FileOutputStream fos = new FileOutputStream("comprehensive_exercises\\aaa\\copy.exe");
// 拷贝数据
byte[] buffer = new byte[8192];
int len; // 记录每次读取的字节数
// 一次读写一个字节数组
while ((len = fis.read(buffer)) != -1) {
// 将读写到的数据写出
fos.write(buffer, 0, len);
}
// 释放资源
fos.close();
fis.close();
}
/*
方式3:字节缓冲流:一次读写一个字节
*/
private static void copy3() throws IOException {
// 创建字节缓冲输入流对象,关联数据源
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\JDK\\jdk-11.0.15_windows-x64_bin.exe"));
// 创建字节缓冲输入流对象,关联目的地
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("comprehensive_exercises\\aaa\\copy.exe"));
// 拷贝数据
int b; // 记录每次读取的字节数
// 一次读写一个字节
while ((b = bis.read()) != -1) {
// 将读写到的数据写出
bos.write(b);
}
// 释放资源
bos.close();
bis.close();
}
/*
方式4:字节缓冲流:一次读写一个字节数组
*/
private static void copy4() throws IOException {
// 创建字节缓冲输入流对象,关联数据源
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\JDK\\jdk-11.0.15_windows-x64_bin.exe"));
// 创建字节缓冲输入流对象,关联目的地
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("comprehensive_exercises\\aaa\\copy.exe"));
// 拷贝数据
byte[] buffer = new byte[8192];
int len; // 记录每次读取的字节数
// 一次读写一个字节
while ((len = bis.read(buffer)) != -1) {
// 将读写到的数据写出
bos.write(buffer, 0, len);
}
// 释放资源
bos.close();
bis.close();
}
}
5、修改文本顺序(出师表)
- 需求:把《出师表》的文章顺序进行恢复到一个新文件中
3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
-
方式1
package com.app.demo36_io_test; import java.io.*; import java.util.ArrayList; import java.util.Collections; public class Test5Case01 { public static void main(String[] args) throws IOException { /* IO流综合练习: 5、修改文本顺序 需求:把《出师表》的文章顺序进行恢复到一个新文件中 */ // 1.读取数据 // 原则(IO):随用随创建,什么时候不用什么时候关闭 // 创建字符缓冲输入流对象,将字符输入流对象关联的数据源包装起来 BufferedReader br = new BufferedReader(new FileReader("comprehensive_exercises\\aaa\\csb")); // 定义字符串变量,用于存储每行读取到的数据 String line; // 创建集合,用于存储读取到的数据 ArrayList<String> list = new ArrayList<>(); // 使用循环,不断按照行来读取数据 while ((line = br.readLine()) != null) { // 将每行读取到的数据存储到集合中 list.add(line); } // 释放资源 br.close(); // 2.排序 Collections.sort(list, (o1, o2) -> { // 获取o1的序号,并将字符串类型的序号 转成 int类型的序号 int number1 = Integer.parseInt(o1.substring(0, 1)); // 获取o2的序号,并将字符串类型的序号 转成 int类型的序号 int number2 = Integer.parseInt(o2.split("\\.")[0]); // 按照序号进行升序排序 return number1 - number2; }); // 3.写出数据 // 创建字符缓冲输出流对象,将字符输出流对象关联的目的地包装起来 BufferedWriter bw = new BufferedWriter(new FileWriter("comprehensive_exercises\\aaa\\copycsb.txt")); // 遍历list集合,依次得到每一行数据 for (String s : list) { // 依次将s写出 bw.write(s); // 每写出一个s,就换行 bw.newLine(); } // 释放资源 bw.close(); } }
-
方式2
package com.app.demo36_io_test; import java.io.*; import java.util.*; public class Test5Case02 { public static void main(String[] args) throws IOException { /* IO流综合练习: 5、修改文本顺序 需求:把《出师表》的文章顺序进行恢复到一个新文件中 */ // 1.读取数据 // 创建字符缓冲输入流对象,将字符输入流对象关联的数据源包装起来 BufferedReader br = new BufferedReader(new FileReader("comprehensive_exercises\\aaa\\csb")); // 定义字符串变量,用于存储每行读取到的数据 String line; // 创建TreeMap集合,用于存储读取到的数据(可排序) // 键:序号;值:内容 TreeMap<Integer, String> tm = new TreeMap<>(); // 使用循环,不断按照行来读取数据 while ((line = br.readLine()) != null) { // 将每行获取到的数据按照 "." 进行切割 String[] arr = line.split("\\."); // 将每行数据按照键值对形式存入集合 tm.put(Integer.parseInt(arr[0]), line); } // 释放资源 br.close(); // 2.写出数据 // 创建字符缓冲输出流对象,将字符输出流对象关联的目的地包装起来 BufferedWriter bw = new BufferedWriter(new FileWriter("comprehensive_exercises\\aaa\\copycsb2.txt")); // 得到集合中所有的键值对集合 Set<Map.Entry<Integer, String>> entries = tm.entrySet(); // 遍历所有的键值对集合,依次得到每个键值对 for (Map.Entry<Integer, String> entry : entries) { // 依次获取每个值对象,并写出 bw.write(entry.getValue()); // 换行 bw.newLine(); } // 释放资源 bw.close(); } }
6、控制软件运行次数
-
需求:实现一个验证程序运行次数的小程序,要求如下:
-
当程序运行超过3次时给出提示:本软件只能免费使用3次,欢迎您注册会员后继续使用~
-
程序运行演示如下:
第一次运行控制台输出:欢迎使用本软件,第1次使用免费~ 第二次运行控制台输出:欢迎使用本软件,第2次使用免费~ 第三次运行控制台输出:欢迎使用本软件,第3次使用免费~ 第四次及之后运行控制台输出:本软件只能免费使用3次,欢迎您注册会员后继续使用~
-
-
分析:
看到了多少次,看来又是计数器
那是使用变量的计数器还是?
肯定是不选择使用变量的计数器,因为变量在内存中,是用完就会消失了
因此,使用文件的计数形式比较好!- 表示一开始软件使用次数为0次
7、用对象流读写多个对象
- 需求:将多个自定义对象序列化到文件中,但是由于对象的个数不确定,反序列化流该如何读取呢?
package com.app.demo36_io_test;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class Test7 {
public static void main(String[] args) throws IOException {
/*
IO流综合练习:
7、用对象流读写多个对象
需求:将多个自定义对象序列化到文件中,但是由于对象的个数不确定,反序列化流该如何读取呢?
*/
// 1.序列化自定义对象到文件中
// 创建多个学生对象
Student s1 = new Student("张三", 23, "南京");
Student s2 = new Student("关羽", 26, "非洲");
Student s3 = new Student("诸葛亮", 22, "卧龙");
Student s4 = new Student("李四", 24, "广州");
// 创建list集合,存储多个学生对象
ArrayList<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
// 将多个自定义对象序列化到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("comprehensive_exercises\\aaa\\b.txt"));
oos.writeObject(list);
// 释放资源
oos.close();
}
}
package com.app.demo36_io_test;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
public class Test8 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
/*
IO流综合练习:
7、用对象流读写多个对象
需求:将多个自定义对象序列化到文件中,但是由于对象的个数不确定,反序列化流该如何读取呢?
*/
// 2.反序列化流读取文件中的多个自定义对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("comprehensive_exercises\\aaa\\b.txt"));
ArrayList<Student> list = (ArrayList<Student>) ois.readObject();
// 释放资源
ois.close();
// 遍历list集合,依次得到每个自定义对象
for (Student student : list) {
System.out.println(student);
}
}
}