IO
概述
IO: 存储和读取数据的解决方案
作用: 用于读写文件中的数据(可以读写文件, 或网络中的数据)
IO流的分类
按流的方向: 输入流, 输出流
按操作文件类型:
字节流: 可以操作所有类型的文件
字符流: 只能操作纯文本文件
纯文本文件: windows自带的记事本打开能读懂(txt md xml lrc是) word和excel都不是
IO流体系
FileOutputStream
FileOutputStream基本使用
操作本地文件的字节输出流, 可以把程序中的数据写道本地文件中
package IOTest1;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;public class IODemo1 {public static void main(String[] args) throws IOException {// 创建输出流对象FileOutputStream fos = new FileOutputStream("IO\\a.txt");// 写数据fos.write(97);// 释放资源fos.close();}
}
细节
1.创建字节输出流对象
- 细节1:参数是字符串表示的路径或者File对象都是可以的
- 细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的。
- 细节3:如果文件已经存在,则会清空文件
2.写数据
- 细节: write方法的参数是整数,但 是实际上写到本地文件中的是整数在ASCII上对应的字符
3.释放资源
- 细节:每次使用完流之后都要释放资源
FileOutputStream写数据的三种方式
package IOTest1;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;public class Demo2 {public static void main(String[] args) throws IOException {FileOutputStream fos = new FileOutputStream("IO\\a.txt");// fos.write(99); // cbyte[] arr = {97,98,99,100,101};// fos.write(arr); // abcde// 第二个参数位起始索引, 第三个参数为写入个数fos.write(arr, 1, 2); // bcfos.close();}
}
两个小问题
1.换行
windows: \r\n
linux: \n
mac: \r
2.不覆盖, 续写
想要续写, 打开续写开关即可
package IOTest1;import java.io.FileOutputStream;
import java.io.IOException;public class Demo3 {public static void main(String[] args) throws IOException {FileOutputStream fos = new FileOutputStream("IO\\a.txt", true);String str = "guanjunyanshidashazi";byte[] bytes1 = str.getBytes();fos.write(bytes1);// 换行String str3 = "\r\n";byte[] bytes3 = str3.getBytes();fos.write(bytes3);String str2 = "666";byte[] bytes2 = str2.getBytes();fos.write(bytes2);fos.close();}
}
程序输出:
guanjunyanshidashazi
666guanjunyanshidashazi
666
FileInputStream
基本使用
操作本地文件的字节输入流, 可以把本地文件中的数据读取到程序中来
书写步骤:
- 创建字节输入流对象
- 读数据
- 释放资源
package IOTest1;import java.io.FileInputStream;
import java.io.IOException;public class InputDemo1 {public static void main(String[] args) throws IOException {// 创建对象FileInputStream fis = new FileInputStream("IO\\a.txt");// 读取数据int r1 = fis.read();System.out.println((char)r1); // aint r2 = fis.read();System.out.println((char)r2); // bint r3 = fis.read();System.out.println((char)r3); // cint r4 = fis.read();System.out.println((char)r4); // dint r5 = fis.read();System.out.println(r5); // -1// 释放资源fis.close();}
}
书写细节
创建字节输入流对象
细节:如果文件不存在,就直接报错。
读取数据
细节1: 一次读一个字节,读出来的是数据在ASCII上对应的数字
细节2:读到文件末尾了,read方法返回-1。
释放资源
细节:每次使用完流必须要释放资源。
循环读取
注意: 每调用一次read()方法, 读取数据的指针就会后移, 因此不要多次调用read()函数
package IOTest1;import java.io.FileInputStream;
import java.io.IOException;public class Demo4 {public static void main(String[] args) throws IOException {FileInputStream fis = new FileInputStream("IO\\a.txt");int b = 0;while((b = fis.read()) != -1){System.out.print((char)b); // abcdefg}fis.close();}
}
练习
文件拷贝
package IOTest1;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class Demo5 {public static void main(String[] args) throws IOException {// 创建对象FileInputStream fis = new FileInputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶.md");FileOutputStream fos = new FileOutputStream("IO\\copy.md");int b;// 核心思想: 边读边写long l1 = System.currentTimeMillis();while((b = fis.read()) != -1){fos.write(b);}long l2 = System.currentTimeMillis();// 打印copy时间System.out.println(l2 - l1);// 释放资源: 最后释放最先打开的流对象fos.close();fis.close();}
}
文件拷贝的问题
弊端: 一次第一个字节, 导致速度很慢
FileInputStream一次读多个字节
注意: 一次都一个字节数组的数据, 每次读取会尽可能把数组装满
数组长度一般为1024的整数倍(1024 * 1024 * 5)
package IOTest2;import java.io.FileInputStream;
import java.io.IOException;public class Demo1 {public static void main(String[] args) throws IOException {// 一次读取多个数据// 文件中的数据是abcdeFileInputStream fis = new FileInputStream("IO\\a.txt");// 一次读两个数据byte[] bytes = new byte[2];int len1 = fis.read(bytes);System.out.println(len1); // 2String str1 = new String(bytes);System.out.println(str1); // abint len2 = fis.read(bytes);System.out.println(len2); // 2String str2 = new String(bytes);System.out.println(str2); // cdint len3 = fis.read(bytes);System.out.println(len3); // 1String str3 = new String(bytes);System.out.println(str3); // edint len4 = fis.read(bytes);System.out.println(len4); // -1String str4 = new String(bytes);System.out.println(str4); // ed}
}
解释: 文件中的数据位abcde
每次都两个, 前两次len都为2, 数据分别为ab 和 cd(cd把byte数据的ab覆盖了)
第三次的时候, 只剩一个数据了, 所以len = 1, 然后e把c覆盖了, d没有被覆盖, 所以输出ed
第四次的时候, 没有数据所以返回-1, 数组中的数据ed不变
解决方法
使用string的另一种构造方法:
package IOTest2;import java.io.FileInputStream;
import java.io.IOException;public class Demo1 {public static void main(String[] args) throws IOException {// 一次读取多个数据// 文件中的数据是abcdeFileInputStream fis = new FileInputStream("IO\\a.txt");// 一次读两个数据byte[] bytes = new byte[2];int len1 = fis.read(bytes);System.out.println(len1); // 2String str1 = new String(bytes, 0, len1);System.out.println(str1); // abint len2 = fis.read(bytes);System.out.println(len2); // 2String str2 = new String(bytes, 0, len2);System.out.println(str2); // cdint len3 = fis.read(bytes);System.out.println(len3); // 1String str3 = new String(bytes, 0, len3);System.out.println(str3); // eint len4 = fis.read(bytes);System.out.println(len4); // -1// String str4 = new String(bytes, 0, len4);// System.out.println(str4); // 这两行会报错}
}
表示从byte数组的0位置转len个字符为字符串
文件拷贝改进
package IOTest2;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class Demo2 {public static void main(String[] args) throws IOException {// 创建对象FileInputStream fis = new FileInputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶.md");FileOutputStream fos = new FileOutputStream("IO\\copy.md");int len;byte[] bytes = new byte[1024 * 1024 * 5];long l1 = System.currentTimeMillis();while((len = fis.read(bytes)) != -1){fos.write(bytes, 0, len);}long l2 = System.currentTimeMillis();System.out.println(l2-l1); // 2fos.close();fis.close();}
}
速度显著提升
异常处理
finally
在try…catch语句的下面可以加finally, 其中的代码一定会执行, 除非JVM退出
JDK9之后的捕获异常的代码:
package IOTest;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class Demo1 {public static void main(String[] args) throws IOException {// 创建对象FileInputStream fis = new FileInputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶.md");FileOutputStream fos = new FileOutputStream("IO\\copy.md");try (fis; fos){int len;byte[] bytes = new byte[1024 * 1024 * 5];while((len = fis.read(bytes)) != -1){fos.write(bytes, 0, len);}} catch (IOException e) {e.printStackTrace();}}
}
了解即可, 一般都是直接抛出异常
字符集详解
计算机存储规则
任意数据以二进制形式来存储
字节: 计算机中最小的存储单元 8bit
存储英文, 一个字节就足够
ASCII字符集
编码规则: 前面补0, 补齐8位
解码规则: 直接转成十进制
英文的存储规则:
汉字存储规则
- GB2312字符集:1980年发布,1981年5月1日实施的简体中文汉字编码国家标准。
收录7445个图形字符,其中包括6763个简体汉字 - BIG5字符集:台湾地区繁体中文标准字符集,共收录13053个中文字,1984年实施。
- GBK字符集:2000年3月17日发布,收录21003个汉字。
包含国家标准GB13000-1中的全部中日韩汉字,和BIG5编码中的所有汉字
windows系统默认使用的就是GBK。
系统显示: ANSI - Unicode字符集:国际标准字符集,它将世界各种语言的每个字符定义一个唯一的编码,以满足跨语言、跨平台的文本信息转换。
GBK
英文
用一个字节存储, 完全兼容ASCII
不足8位, 前面补0
汉字
规则1: 汉字使用两个字节存储(前面的是高位字节, 后面的是低位字节)
规则2: 高位字节二进制一定以1开头, 转成十进制之后是一个负数(为了和英文区分开)
总结
1.在计算机中,任意数据都是以二进制的形式来存储
2.计算机中最小的存储单元是一个字节
3.ASCII字符集中,一个英文占一个字节
4.简体中文版Windows,默认使用GBK字符集
5.GBK字符集完全兼容ASCII字符集
一个英文占一个字节,二进制第一位是0
一个中文占两个字节,二进制高位字节的第一位是1
Unicode(万国码)
英文存储方式
UTF-16编码规则: 用2-4个字节保存
UTF-32编码规则: 固定使用四个字节保存
UTF-8编码规则: 用1-4个字节保存
只需知道英文字母 - 1个字节(前面补0)
简体中文 - 3个字节
填补方式: 红色的即为填补的
编码图解:
总结
UTF-8是字符集吗?
不是, 它是**unicode字符集**的一种编码方式
一个英文占一个字节,二进制第一位是0,转成十进制是正数
一个中文占三个字节,二进制第一位是1,第一个字节转成十进制是负数
乱码
原因1: 读取数据时, 未读完整个汉字(字节流: 一次读取一个字节)
原因2: 编码和解码的方式不统一, 比如UTF-8编码, GBK解码
如何不产生乱码?
1.不要用字节流读取文本文件
2.编码解码时使用同一个码表,同一个编码方式
字节流读取中文会乱码, 但是拷贝是不会乱码的
编码和解码
编码的方法
解码的方法
package IOTest;import java.io.UnsupportedEncodingException;
import java.util.Arrays;public class Demo2 {public static void main(String[] args) throws UnsupportedEncodingException {// 编码String str = "ai你呦";// 默认方式编码 UTF-8byte[] bytes1 = str.getBytes();System.out.println(Arrays.toString(bytes1)); // [97, 105, -28, -67, -96, -27, -111, -90]// 1+1+3+3 = 8 个字节// 指定GBK编码byte[] bytes2 = str.getBytes("GBK");System.out.println(Arrays.toString(bytes2)); // [97, 105, -60, -29, -33, -49]// 1+1+2+2 = 6 个字节// 解码// bytes1是UTF-8编码, bytes2是GBK编码// UTF-8 解码 bytes1 正常String str2 = new String(bytes1);System.out.println(str2); // ai你呦// 我用UTF-8解码bytes2 乱码String str3 = new String(bytes2);System.out.println(str3); // ai����// 用GBK解码bytes1 乱码String str4 = new String(bytes1, "GBK");System.out.println(str4); // ai浣犲懄// 用GBK解码bytes2 正常String str5 = new String(bytes2, "GBK");System.out.println(str5); // ai你呦}
}
字符输入流
概述
底层就是字节流(加上了字符集)
特点:
输入流:一次读一个字节, 遇到中文时, 一次读多个字节
输出流: 底层会把数据按照指定的编码方式进行编码, 变成字节在写到文件中
使用场景
对于纯文本文件进行操作
继承结构
蓝色框的都是抽象类 不能直接创建对象 需要使用子类
FileReader
创建字符输入流对象
细节: 如果文件不存在, 就直接报错
读取数据
- 细节1:按字节进行读取,遇到中文,一次读多个字节,读取后解码,返回一个整数
- 细节2:读到文件末尾了,read方法返回-1。
释放资源
空参read方法的细节
package IOTest;import java.io.FileReader;
import java.io.IOException;public class Demo3 {public static void main(String[] args) throws IOException {// 创建对象FileReader fr = new FileReader("IO\\a.txt");// 读取数据int ch;while((ch = fr.read()) != -1){System.out.print((char)ch);}// 释放资源fr.close();}
}
细节:
字符流的底层也是字节流,默认也是一个字节一个字节的读取的。
如果遇到中文就会一次读取多个,GBK一次读两个字节,UTF-8一次读三个字节
read () 细节:
1.read:默认也是一个字节一个宁节的读取的,如果遇到中文就会一次读取多个
2.在读取之后,方法的底层还会进行解码并转成十进制。最终把这个十进制作为返回值
这个十进制的数据也表示在字符集上的数字
英文: 文件里面二进制数据 0110 0001
read方法进行读取,解码并转成十进制97
中文:文件里面的二进制数据 11100110 1110001 10001001
read方法进行读取,解码并转成十进制27721
我想看到中文汉字,就是把这些十进制数据,再进行强转就可以
带参read方法的细节
package IOTest;import java.io.FileReader;
import java.io.IOException;public class Demo4 {public static void main(String[] args) throws IOException {FileReader fr = new FileReader("IO\\a.txt");char[] chars = new char[3]; // 一次读三个int len;while ((len = fr.read(chars)) != -1){System.out.print(new String(chars, 0, len));}fr.close();}
}
带参read的细节:
read(chars): 读取数据, 解码, 强转三部合并了, 把强转之后的字符放到数组当中
相当于是空参read + 强制类型转换
字符输出流
构造方法
成员方法
书写细节
- 创建对象
- 细节1:参数是字符串表示的路径或者File对象都是可以的
- 细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
- 细节3:如果文件已经存在,则会清空文件,如果不想清空可以打开续写开关
2.写数据
细节: 如果write方法的参数是整数,但是实际上写到本地文件中的是整数在字符集上对应的字符
3.释放资源
细节: 每次使用完流之后都要释放资源
package IOTest;import java.io.FileWriter;
import java.io.IOException;public class Demo5 {public static void main(String[] args) throws IOException {// 字符输出流FileWriter fw = new FileWriter("IO\\a.txt", true);// 打开了续写开关// 一次写一个字符// fw.write(25105);// 一次写一个字符串// fw.write("范德萨");// 写出一个字符数组char[] chars = {'a', 'b', '但'};fw.write(chars);fw.close();}
}
字符流原理解析
输入流
每次调用read函数的时候, 都会判断缓冲区中是否有数据, 如果缓冲区中没有数据, 就回到数据源中获取数据
(字节流是没有缓冲区的)
原理剖析
创建字符输入流对象
底层: 关联文件,并创建缓冲区(长度为8192的字节数组)
读取数据底层:
- 判断缓冲区中是否有数据可以读取
- 缓冲区没有数据:就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区如果文件中也没有数据了,返回-1
- 缓冲区有数据:就从缓冲区中读取。
- 空参的read方法:一次读取一个字节,遇到中文一次读多个字节,把字节解码并转成十进制返回
- 有参的read方法: 把读取字节,解码,强转三步合并了,强转之后的字符放到数组中
输出流
图中的三种情况即为目的地读取缓冲区数据的情况
flush和close方法
两个函数的区别: flush后还能继续写出数据, close不能
字节流和字符流使用场景
字节流:
拷贝任意类型的文件
字符流:
读取纯文本文件中的数据
往纯文本文件中写出数据
练习
练习1
拷贝一个文件夹, 考虑子文件夹
package Practice;import java.io.*;public class Demo2 {public static void main(String[] args) throws IOException {// 拷贝文件夹, 包含子文件夹// 创建对象表示数据源 和 目的地File file1 = new File("D:\\java\\笔记\\进阶笔记");File file2 = new File("D:\\java\\笔记\\进阶笔记2");copyDir(file1, file2);}private static void copyDir(File file1, File file2) throws IOException {file2.mkdirs();File[] files = file1.listFiles();for (File file : files) {// 判断if(file.isFile()){// 拷贝文件 字节流FileInputStream fis = new FileInputStream(file);FileOutputStream fos = new FileOutputStream(new File(file2, file.getName()));int len;byte[] bytes = new byte[1024 * 1024 * 5];while((len = fis.read(bytes)) != -1){fos.write(bytes, 0, len);}fos.close();fis.close();}else{// 文件夹copyDir(file, new File(file2, file.getName()));}}}
}
练习2(异或应用)
为了保证文件的安全性,就需要对原始文件进行加密存储,再使用的时候再对其进行解密处理。
加密原理:
对原始文件中的每一个字节数据进行更改,然后将更改以后的数据存储到新的文件中
解密原理:
读取加密之后的文件,按照加密的规则反向操作,变成原始文件。
package Practice;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class Demo3 {public static void main(String[] args) throws IOException {// 加密和解密// 加密是a->b 解密是b->cFileInputStream fis = new FileInputStream("IO\\b.txt");FileOutputStream fos = new FileOutputStream("IO\\c.txt");// 加密处理int b;while((b = fis.read()) != -1){fos.write(b ^ 10);}// 释放资源fos.close();fis.close();}
}
练习3
文本文件中有以下的数据:
2-1-9-4-7-8
将文件中的数据进行排序,变成以下的数据:
1-2-4-7-8-9
package Practice;import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.StringJoiner;public class Demo1 {public static void main(String[] args) throws IOException {
// File file = new File("IO\\a.txt");
// file.createNewFile();// 写入数据// 2-1-9-4-7-8/*FileWriter fw = new FileWriter("IO\\a.txt");String str = "2-1-9-4-7-8";fw.write(str);fw.close();*/// 读数据 字符串形式FileReader fr = new FileReader("IO\\a.txt");StringBuilder sb = new StringBuilder();int ch;while((ch = fr.read()) != -1){sb.append((char)ch);}String str2 = sb.toString();System.out.println(str2); // 2-1-9-4-7-8// 数据拆分, 放到数组中String[] split = str2.split("-");ArrayList<Integer> nums = new ArrayList<>();for(int i = 0; i < split.length; ++i){nums.add(Integer.parseInt(split[i]));}// 排序Collections.sort(nums);// 拼接成字符串StringJoiner sj = new StringJoiner("-","","");for (Integer num : nums) {sj.add(num.toString());}String str3 = sj.toString();System.out.println(str3);// 把str3写入到a.txt中FileWriter fw = new FileWriter("IO\\a.txt");fw.write(str3);fw.close();}
}
高级流
缓冲流
体系结构
字节缓冲流
分为字节缓冲输入流和字节缓冲输出流
原理: 底层自带了长度为8192的缓冲区提高性能
常用方法
对象还是基本流, 只不过借助缓冲流速度会更快
练习
利用字节缓冲流拷贝文件
一次操作一个字节:
package Practice2;import java.io.*;public class Demo1 {public static void main(String[] args) throws IOException {// 利用字节缓冲流拷贝文件BufferedInputStream bis = new BufferedInputStream(new FileInputStream("IO\\a.txt"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("IO\\b.txt"));// 循环读取并写到目的地int b;while((b = bis.read()) != -1){bos.write(b);}// 释放资源bos.close(); // FileInputStream 在 BufferInputStream.close()的底层已经关闭了bis.close();}
}
一次操作多个字节:
package Practice2;import java.io.*;public class Demo2 {public static void main(String[] args) throws IOException {BufferedInputStream bis = new BufferedInputStream(new FileInputStream("IO\\a.txt"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("IO\\b.txt"));byte[] bytes = new byte[1024];int len;while((len = bis.read(bytes)) != -1){bos.write(bytes, 0, len);}// 释放资源bos.close();bis.close();}
}
字符缓冲流
底层自带了8192的缓冲区提高性能
构造方法
特有方法
方法底层会先判断是什么操作系统, 在换行
字符缓冲输入流代码:
package BufferedTest;import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;public class Demo1 {public static void main(String[] args) throws IOException {// 创建对象BufferedReader br = new BufferedReader(new FileReader("IO\\a.txt"));// 读取数据// 细节: readLine这个函数一次读一行, 遇到回车结束, 但是不会把回车也读进来String line;while((line = br.readLine()) != null){System.out.println(line);}// 释放资源br.close();}
}
字符缓冲输出流:
package BufferedTest;import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;public class Demo2 {public static void main(String[] args) throws IOException {// 字符缓冲输出流BufferedWriter bw = new BufferedWriter(new FileWriter("IO\\b.txt", true)); // 打开续写开关bw.write("123");bw.newLine();bw.write("345");bw.newLine();bw.close();}
}
总结
1.缓冲流有几种?
- 字节缓冲输入流: BufferedInputStream
- 字节缓冲输出流: BufferedOutputStream
- 字符缓冲输入流: BufferedReader
- 字符缓冲输出流: BufferedWriter
2.缓冲流为什么能提高性能
- 缓冲流自带长度为8192的缓冲区
- 可以显著提高字节流的读写性能
- 对于字符流提升不明显,对于字符缓冲流而言关键点是两个特有的方法
3.字符缓冲流两个特有的方法是什么?
- 字符缓冲输入流BufferedReader: readLine ()
- 字符缓冲输出流BufferedWriter: newLine ()
练习
练习1 拷贝文件
四种方式拷贝文件,并统计各自用时
- 字节流的基本流:一次读写一个字节
- 字节流的基本流:一次读写一个字节数组
- 字节缓冲流:一次读写一个字节
- 字节缓冲流:一次读写一个字节数组
package Practice3;import java.io.*;public class Demo1 {public static void main(String[] args) throws IOException {/*- 字节流的基本流:一次读写一个字节- 字节流的基本流:一次读写一个字节数组- 字节缓冲流:一次读写一个字节- 字节缓冲流:一次读写一个字节数组* */// byteStreamCopy(); // 1039// bytesStreamCopy(); // 7// byteBufferedCopy(); // 21bytesBufferedCopy(); // 6}private static void bytesBufferedCopy() throws IOException{long l1 = System.currentTimeMillis();// 创建对象BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶.md"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶4.md"));int len;byte[] bytes = new byte[1024 * 1024 * 5];while((len = bis.read(bytes)) != -1){bos.write(bytes, 0, len);}// 释放资源bos.close();bis.close();long l2 = System.currentTimeMillis();System.out.println(l2 - l1);}private static void byteBufferedCopy() throws IOException{long l1 = System.currentTimeMillis();// 创建对象BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶.md"));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶4.md"));int len;while((len = bis.read()) != -1){bos.write(len);}// 释放资源bos.close();bis.close();long l2 = System.currentTimeMillis();System.out.println(l2 - l1);}private static void bytesStreamCopy() throws IOException{long l1 = System.currentTimeMillis();// 创建对象FileInputStream fis = new FileInputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶.md");FileOutputStream fos = new FileOutputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶3.md");int len;byte[] bytes = new byte[1024 * 1024 * 5];while((len = fis.read(bytes)) != -1){fos.write(bytes, 0, len);}// 释放资源fos.close();fis.close();long l2 = System.currentTimeMillis();System.out.println(l2 - l1);}// 字节流基本流static void byteStreamCopy() throws IOException{long l1 = System.currentTimeMillis();// 创建对象FileInputStream fis = new FileInputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶.md");FileOutputStream fos = new FileOutputStream("D:\\java\\笔记\\进阶笔记\\JAVA进阶2.md");int b;while((b = fis.read()) != -1){fos.write(b);}// 释放资源fos.close();fis.close();long l2 = System.currentTimeMillis();System.out.println(l2 - l1);}
}
练习2
排序文件中的出师表
每句话前面有序号
package Practice3;import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;public class Demo2 {public static void main(String[] args) throws IOException {// 文件乱序在a.txt中// 先读取// 用字符缓冲流BufferedReader br = new BufferedReader(new FileReader("IO\\a.txt"));// 存储数据的容器ArrayList<String> list = new ArrayList<>();String len;while((len = br.readLine()) != null){list.add(len);}System.out.println(list);// 排序Collections.sort(list, new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {String[] arr1 = o1.split("\\.");String[] arr2 = o2.split("\\.");String s1 = arr1[0];String s2 = arr2[0];int i1 = Integer.parseInt(s1);int i2 = Integer.parseInt(s2);return i1 - i2;}});System.out.println(list);// 写入到a.txt中BufferedWriter bw = new BufferedWriter(new FileWriter("IO\\a.txt"));for (int i = 0; i < list.size(); ++i) {bw.write(list.get(i));bw.newLine();}bw.close();br.close();}
}
练习3
实现一个验证程序运行次数的小程序,要求如下:
1.当程序运行超过3次时给出提示:本软件只能免费使用3次,欢迎您注册会员后继续使用~
2.程序运行演示如下:
- 第一次运行控制台输出: 欢迎使用本软件,第1次使用免费~
- 第二次运行控制台输出: 欢迎使用本软件,第2次使用免费~
- 第三次运行控制台输出: 欢迎使用本软件,第3次使用免费~
- 第四次及之后运行控制台输出:本软件只能免费使用3次,欢迎您注册会员后继续使用~
package Practice3;import java.io.*;public class Demo3 {public static void main(String[] args) throws IOException {// 创建对象BufferedReader br = new BufferedReader(new FileReader("IO\\a.txt"));// 读取文件中的使用次数 初始为0String s = br.readLine();br.close();int count = Integer.parseInt(s);// 判断是否超过3次if(++count <= 3){System.out.println("欢迎使用本软件, 这是您第" + count + "次免费试用");}else{System.out.println("试用次数已到, 请冲会员");}// 把count写入到文件中// 随用随创建BufferedWriter bw = new BufferedWriter(new FileWriter("IO\\a.txt")); // 这一行不能写在上面, 否则会直接清空bw.write(count + "");bw.close();}
}
转换流
是字符流和字节流之间的桥梁
作用1: 指定字符集读写(淘汰了)
作用2: 字节流想要使用字符流中的方法
练习1
需求1:手动创建一个GBK的文件,把文件中的中文读取到内存中,不能出现乱码
需求2: 把一段中文按照GBK的方式写到本地文件
需求3:将本地文件中的GBK文件,转成UTF-8
package Practice3;import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;public class Demo4 {public static void main(String[] args) throws IOException {// 需求1// 读取GBK文件FileReader fr = new FileReader("IO\\a.txt", Charset.forName("GBK"));int ch;while ((ch = fr.read()) != -1){System.out.println((char) ch);}fr.close();// 需求2: 按照指定的码表写出数据FileWriter fw = new FileWriter("IO\\b.txt", Charset.forName("GBK"));fw.write("你好你好");fw.close();// 需求3: 将本地的GBK文件转成UTF-8FileReader fr2 = new FileReader("IO\\b.txt", Charset.forName("GBK"));FileWriter fw2 = new FileWriter("IO\\c.txt", Charset.forName("UTF-8"));int ch1;while ((ch1 = fr2.read()) != -1){fw2.write(ch1);}fw2.close();fr2.close();}
}
练习2
package Practice3;import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;public class Demo5 {public static void main(String[] args) throws IOException {// 利用字节流获取文件中的数据// 每次读一行, 而且不能出现乱码BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("IO\\a.txt")));String line;while((line = br.readLine()) != null){System.out.println(line);}br.close();}
}
总结
1.转换流的名字是什么?
- 字符转换输入流:InputStreamReader
- 字符转换输出流: OutputStreamWriter
2.转换流的作用是什么?
- 指定字符集读写数据(JDK11之后已淘汰)
- 字节流想要使用字符流中的方法了
序列化流
序列化流
可以把Java中的对象写到本地文件中(看不懂)
可以利用反序列化流从文件中读取出来
细节:
使用对象输出流将对象保存到文件时会出现NotserializableException异常
解决方案: 需要让Javabean类实现Serializable接口
一旦实现了这个接口, 就表示这个类可以被序列化
Serializable这个接口没有抽象方法, 被称为标记型接口
package ObjectStream;import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;public class Demo1 {public static void main(String[] args) throws IOException {Student s = new Student("zhangsan", 23);// 创建对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("IO\\a.txt"));oos.writeObject(s);oos.close();}
}
反序列化流
可以把序列化到本地的文件中的对象, 读取到程序中来
package ObjectStream;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;public class Demo2 {public static void main(String[] args) throws IOException, ClassNotFoundException {// 利用反序列化流把a.txt中的信息打印ObjectInputStream ois = new ObjectInputStream(new FileInputStream("IO\\a.txt"));Object o = ois.readObject();System.out.println(o); // Student{name = zhangsan, age = 23}ois.close();}
}
细节
当我们把类的对象序列化的写入到文件中后, 如果这时我们对类做了修改(加了一个属性), 这时在反序列化读取数据的时候就会报错
原因是如果一个类实现了Serializable接口, Java就会根据这个类的对象的各种属性值计算出一个序列号, 可以理解为版本号, 在我们修改类的时候, 版本号就会发生变化, 导致数据无法读取
解决方案: 在类中固定版本号
private static final long serialVersionUID = 1L;
2.当我们不想把某个属性值序列化的时候 可以加关键字transient, 此时的标准JavaBean类
package ObjectStream;import java.io.Serializable;public class Student implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;private transient String address;// 瞬态关键字// 不会把当前属性序列化到本地文件中public Student() {}public Student(String name, int age, String address) {this.name = name;this.age = age;this.address = address;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}/*** 获取* @return address*/public String getAddress() {return address;}/*** 设置* @param address*/public void setAddress(String address) {this.address = address;}public String toString() {return "Student{ name = " + name + ", age = " + age + ", address = " + address + "}";}
}
总结
- 使用序列化流将对象写到文件时,需要让Javabean类实现Serializable接口。否则,会出现NotSerializableException异常
- 序列化流写到文件中的数据是不能修改的,一旦修改就无法再次读回来了
- 序列化对象后,修改了Javabean类,再次反序列化,会不会有问题?
会出问题,会抛出InvalidclassException异常
解决方案:给Javabean类添加serialVersionUID (序列号、版本号) - 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
解决方案:给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
练习
将多个自定义对象序列化到文件中, 但是对象的个数不确定, 该如何操作?
可以把对象都方法ArrayList中, 然后只需序列化一次
反序列化也只需要一次
打印流
字节打印流
只有输出流
PrintStream和PrintWriter两个类
- 特点1: 打印流只操作文件目的地,不操作数据源
- 特点2:特有的写出方法可以实现,数据原样写出例如:打印:97
文件中:97
文件中: true打印: true - 特点3:特有的写出方法可以实现自动刷新,自动换行打印一次数据 =写出 + 行 + 刷新构造方法
构造方法
成员方法
package printStream;import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.Charset;public class Demo1 {public static void main(String[] args) throws IOException {// 创建字节打印流对象PrintStream ps = new PrintStream(new File("IO\\a.txt"));ps.println(97);ps.print(true);ps.println();ps.printf("%s爱上了%s", "阿珍", "阿强");ps.close();}
}
字符打印流
构造方法
成员方法
package printStream;import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;public class Demo2 {public static void main(String[] args) throws IOException {// 创建字符打印流对象PrintWriter pw = new PrintWriter(new FileWriter("IO\\a.txt"), true);// 写入数据pw.println("fcsa");pw.print(78);pw.close();}
}
打印流和打印语句的联系
package printStream;import java.io.PrintStream;public class Demo3 {public static void main(String[] args) {// 获取打印流的对象, 此打印流在虚拟机启动的时候有虚拟机创建// 默认值向控制台// 特殊的打印流, 系统中的标准输出流, 不能关闭// 在系统中是唯一的PrintStream ps = System.out;// 调用打印流中的方法println// 写出数据, 自动换行, 自动刷新ps.println("123");ps.close();// 关闭之后就不能继续打印了ps.println("345");}
}
上述代码只会打印123
总结
打印流有几种?各有什么特点?
- 有字节打印流和字符打印流两种
- 打印流不操作数据源,只能操作目的地
- 字节打印流:默认自动刷新,特有的println自动换行
- 字符打印流:自动刷新需要开启,特有的println自动换行
解压流
解压缩流
读取压缩包的文件
解压本质: 把每一个ZipEntry按照层级拷贝到本地另一个文件夹中
package com.itheima.myzipstream;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;/*
* 解压缩流
*
* */
public class ZipStreamDemo1 {public static void main(String[] args) throws IOException {//1.创建一个File表示要解压的压缩包File src = new File("D:\\aaa.zip");//2.创建一个File表示解压的目的地File dest = new File("D:\\");//调用方法unzip(src,dest);}//定义一个方法用来解压public static void unzip(File src,File dest) throws IOException {//解压的本质:把压缩包里面的每一个文件或者文件夹读取出来,按照层级拷贝到目的地当中//创建一个解压缩流用来读取压缩包中的数据ZipInputStream zip = new ZipInputStream(new FileInputStream(src));//要先获取到压缩包里面的每一个zipentry对象//表示当前在压缩包中获取到的文件或者文件夹ZipEntry entry;while((entry = zip.getNextEntry()) != null){System.out.println(entry);if(entry.isDirectory()){//文件夹:需要在目的地dest处创建一个同样的文件夹File file = new File(dest,entry.toString());file.mkdirs();}else{//文件:需要读取到压缩包中的文件,并把他存放到目的地dest文件夹中(按照层级目录进行存放)FileOutputStream fos = new FileOutputStream(new File(dest,entry.toString()));int b;while((b = zip.read()) != -1){//写到目的地fos.write(b);}fos.close();//表示在压缩包中的一个文件处理完毕了。zip.closeEntry();}}zip.close();}
}
压缩流
本质: 把每一个(文件/文件夹)看成ZipEntry对象放到压缩包中
压缩单个文件
package com.itheima.myzipstream;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;public class ZipStreamDemo2 {public static void main(String[] args) throws IOException {/** 压缩流* 需求:* 把D:\\a.txt打包成一个压缩包* *///1.创建File对象表示要压缩的文件File src = new File("D:\\a.txt");//2.创建File对象表示压缩包的位置File dest = new File("D:\\");//3.调用方法用来压缩toZip(src,dest);}/** 作用:压缩* 参数一:表示要压缩的文件* 参数二:表示压缩包的位置* */public static void toZip(File src,File dest) throws IOException {//1.创建压缩流关联压缩包ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(dest,"a.zip")));//2.创建ZipEntry对象,表示压缩包里面的每一个文件和文件夹//参数:压缩包里面的路径ZipEntry entry = new ZipEntry("aaa\\bbb\\a.txt");//3.把ZipEntry对象放到压缩包当中zos.putNextEntry(entry);//4.把src文件中的数据写到压缩包当中FileInputStream fis = new FileInputStream(src);int b;while((b = fis.read()) != -1){zos.write(b);}zos.closeEntry();zos.close();}
}
压缩文件夹
package com.itheima.myzipstream;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;public class ZipStreamDemo3 {public static void main(String[] args) throws IOException {/** 压缩流* 需求:* 把D:\\aaa文件夹压缩成一个压缩包* *///1.创建File对象表示要压缩的文件夹File src = new File("D:\\aaa");//2.创建File对象表示压缩包放在哪里(压缩包的父级路径)File destParent = src.getParentFile();//D:\\//3.创建File对象表示压缩包的路径File dest = new File(destParent,src.getName() + ".zip");//4.创建压缩流关联压缩包ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));//5.获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中toZip(src,zos,src.getName());//aaa//6.释放资源zos.close();}/** 作用:获取src里面的每一个文件,变成ZipEntry对象,放入到压缩包当中* 参数一:数据源* 参数二:压缩流* 参数三:压缩包内部的路径* */public static void toZip(File src,ZipOutputStream zos,String name) throws IOException {//1.进入src文件夹File[] files = src.listFiles();//2.遍历数组for (File file : files) {if(file.isFile()){//3.判断-文件,变成ZipEntry对象,放入到压缩包当中ZipEntry entry = new ZipEntry(name + "\\" + file.getName());//aaa\\no1\\a.txtzos.putNextEntry(entry);//读取文件中的数据,写到压缩包FileInputStream fis = new FileInputStream(file);int b;while((b = fis.read()) != -1){zos.write(b);}fis.close();zos.closeEntry();}else{//4.判断-文件夹,递归toZip(file,zos,name + "\\" + file.getName());}}}
}
常用工具包
Commons-io
Commons-io是apache开源基金组织提供的一组有关IO操作的开源工具包
作用: 提高IO开源效率
使用步骤
- 在项目中创建一个文件夹lib
- 将jar包复制粘贴到lib文件夹
- 右键点击jar包,选择Add as Library -> 点击OK
- 在类中导包使用
常见方法
package Commons_io;import org.apache.commons.io.FileUtils;import java.io.File;
import java.io.IOException;public class Demo1 {public static void main(String[] args) throws IOException {// 拷贝文件File src = new File("IO\\a.txt");File dest = new File("IO\\b.txt");FileUtils.copyFile(src, dest);// 拷贝文件夹// FileUtils.copyDirectory(); // 这个方法是把文件夹直接拷贝到dest路径下// FileUtils.copyDirectoryToDirectory(); // 这个方法是把文件夹拷贝到路径文件夹里面}
}
Hutool工具包
先导入jar包
package com.itheima.myhutool;import cn.hutool.core.io.FileUtil;import java.util.List;public class Test1 {public static void main(String[] args) {/*FileUtil类:file:根据参数创建一个file对象touch:根据参数创建文件writeLines:把集合中的数据写出到文件中,覆盖模式。appendLines:把集合中的数据写出到文件中,续写模式。readLines:指定字符编码,把文件中的数据,读到集合中。readUtf8Lines:按照UTF-8的形式,把文件中的数据,读到集合中copy:拷贝文件或者文件夹*//* File file1 = FileUtil.file("D:\\", "aaa", "bbb", "a.txt");System.out.println(file1);//D:\aaa\bbb\a.txtFile touch = FileUtil.touch(file1);System.out.println(touch);ArrayList<String> list = new ArrayList<>();list.add("aaa");list.add("aaa");list.add("aaa");File file2 = FileUtil.writeLines(list, "D:\\a.txt", "UTF-8");System.out.println(file2);*//* ArrayList<String> list = new ArrayList<>();list.add("aaa");list.add("aaa");list.add("aaa");File file3 = FileUtil.appendLines(list, "D:\\a.txt", "UTF-8");System.out.println(file3);*/List<String> list = FileUtil.readLines("D:\\a.txt", "UTF-8");System.out.println(list);}
}
综合练习
制造假数据
需求: 制造假数据也是开发中的一个能力, 在各个网上爬取数据, 是其中一个方法
获取姓氏
https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0
男生名字
http://www.haoming8.cn/baobao/10881.html
女生名字
http://www.haoming8.cn/baobao/7641.html
package Practice1;import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class Demo1 {public static void main(String[] args) throws IOException {// 利用网络爬虫获取姓氏, 男生名, 女生名// 1.定义变量记录网址String familyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0";String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";String girlNameNet = "http://www.haoming8.cn/baobao/7641.html";// 2.爬取数据, 把网址上所有的数据拼接成一个字符串String familyNameStr = webCrawler(familyNameNet);String boyNameStr = webCrawler(boyNameNet);String girlNameStr = webCrawler(girlNameNet);// System.out.println(familyNameStr);// 3.用正则表达式提取数据ArrayList<String> familyTempList = getData(familyNameStr, "(.{4})(,| 。)", 1);ArrayList<String> boyNameTempList = getData(boyNameStr, "([\\u4E00-\\u9FA5]{2})(、| 。)", 1);ArrayList<String> girlNameTempList = getData(girlNameStr, "(.. ){4}..", 0); // 雅晶 月莹 秀竹 凡梦 虹影// 4.数据处理// 姓氏处理方案: 每个元素的每个字单独拿出来ArrayList<String> familyList = new ArrayList<>();for (String str : familyTempList) {for (int i = 0; i < str.length(); i++) {familyList.add(str.charAt(i) + "");}}// System.out.println(familyList);// 男生名处理方案: 去重ArrayList<String> boyNameList = new ArrayList<>();for (String str : boyNameTempList) {if(!boyNameList.contains(str)) boyNameList.add(str);}// System.out.println(boyNameList);// 女生名处理方案: split 空格ArrayList<String> girlNameList = new ArrayList<>();for (String str : girlNameTempList) {String[] split = str.split(" ");for (String s : split) {girlNameList.add(s);}}// System.out.println(girlNameList);// 5.生成数据// 姓名(唯一)-性别-年龄ArrayList<String> list = getInfos(familyList, boyNameList, girlNameList, 70, 50);// System.out.println(list.size()); // 120// 6.写出数据BufferedWriter bw = new BufferedWriter(new FileWriter("IOPractice\\name.txt"));for (String info : list) {bw.write(info);bw.newLine();}bw.close();}private static ArrayList<String> getInfos(ArrayList<String> familyList, ArrayList<String> boyNameList,ArrayList<String> girlNameList, int boyNum, int girlNum){// 男生名HashSet<String> boyhs = new HashSet<>();while(boyhs.size() < boyNum){Collections.shuffle(familyList);Collections.shuffle(boyNameList);boyhs.add(familyList.get(0) + boyNameList.get(0));}// 女生名HashSet<String> gilrhs = new HashSet<>();while(gilrhs.size() < girlNum){Collections.shuffle(familyList);Collections.shuffle(girlNameList);gilrhs.add(familyList.get(0) + girlNameList.get(0));}ArrayList<String> list = new ArrayList<>();Random r = new Random();// 把男生加进集合for (String boy : boyhs) {int age = r.nextInt(10) + 18; // [18, 27]list.add(boy + "-男-" +age);}// 女生加进集合for (String girl : gilrhs) {int age = r.nextInt(8) + 18; // [18, 25]list.add(girl + "-女-" + age);}Collections.shuffle(list);return list;}private static ArrayList<String> getData(String familyNameStr, String regex, int index) {// 创建集合存放数据ArrayList<String> list = new ArrayList<>();// 按照正则表达式的规则, 去获取数据Pattern pattern = Pattern.compile(regex);Matcher matcher = pattern.matcher(familyNameStr);while(matcher.find()){String group = matcher.group(index);list.add(group);}return list;}private static String webCrawler(String familyNameNet) throws IOException {// 1.定义stringBuilder拼接数据StringBuilder sb = new StringBuilder();// 2.创建一个URL对象URL url = new URL(familyNameNet);// 3.连接上这个网址URLConnection conn = url.openConnection();// 4.读取数据 转换流InputStreamReader isr = new InputStreamReader(conn.getInputStream());int ch;while ((ch = isr.read()) != -1){sb.append((char)ch);}// 5.释放资源isr.close();// 6.返回数据return sb.toString();}
}
利用Hutool包, 更改了部分数据
package Practice1;import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.HttpUtil;import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class Demo1 {public static void main(String[] args) throws IOException {// 利用网络爬虫获取姓氏, 男生名, 女生名// 1.定义变量记录网址String familyNameNet = "https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d&from=kg0";String boyNameNet = "http://www.haoming8.cn/baobao/10881.html";String girlNameNet = "http://www.haoming8.cn/baobao/7641.html";// 2.爬取数据, 把网址上所有的数据拼接成一个字符串
// String familyNameStr = webCrawler(familyNameNet);
// String boyNameStr = webCrawler(boyNameNet);
// String girlNameStr = webCrawler(girlNameNet);// 利用Hutool爬取String familyNameStr = HttpUtil.get(familyNameNet);String boyNameStr = HttpUtil.get(boyNameNet);String girlNameStr = HttpUtil.get(girlNameNet);// System.out.println(familyNameStr);// 3.用正则表达式提取数据
// ArrayList<String> familyTempList = getData(familyNameStr, "(.{4})(,| 。)", 1);
// ArrayList<String> boyNameTempList = getData(boyNameStr, "([\\u4E00-\\u9FA5]{2})(、| 。)", 1);
// ArrayList<String> girlNameTempList = getData(girlNameStr, "(.. ){4}..", 0); // 雅晶 月莹 秀竹 凡梦 虹影// 用Hutool提取数据List<String> familyTempList = ReUtil.findAll("(.{4})(,| 。)", familyNameStr, 1);List<String> boyNameTempList = ReUtil.findAll("([\\u4E00-\\u9FA5]{2})(、| 。)", boyNameStr, 1);List<String> girlNameTempList = ReUtil.findAll("(.. ){4}..", girlNameStr, 0);// 4.数据处理// 姓氏处理方案: 每个元素的每个字单独拿出来ArrayList<String> familyList = new ArrayList<>();for (String str : familyTempList) {for (int i = 0; i < str.length(); i++) {familyList.add(str.charAt(i) + "");}}// System.out.println(familyList);// 男生名处理方案: 去重ArrayList<String> boyNameList = new ArrayList<>();for (String str : boyNameTempList) {if(!boyNameList.contains(str)) boyNameList.add(str);}// System.out.println(boyNameList);// 女生名处理方案: split 空格ArrayList<String> girlNameList = new ArrayList<>();for (String str : girlNameTempList) {String[] split = str.split(" ");for (String s : split) {girlNameList.add(s);}}// System.out.println(girlNameList);// 5.生成数据// 姓名(唯一)-性别-年龄ArrayList<String> list = getInfos(familyList, boyNameList, girlNameList, 70, 50);// System.out.println(list.size()); // 120// 6.写出数据
// BufferedWriter bw = new BufferedWriter(new FileWriter("IOPractice\\name.txt"));
// for (String info : list) {
// bw.write(info);
// bw.newLine();
// }
// bw.close();// 利用Hutool写出数据// 细节: 糊涂报的相对路径, 不是相对于当前项目而言的, 而是相对于class文件而言的FileUtil.writeLines(list, "names.txt", "UTF-8");}
随机点名器
版本1(独立完成)
需求:
有一个文件里面存储了班级同学的信息,每一个信息占一行
格式为:张三-男-23
要求通过程序实现随机点名器。
运行效果:
- 第一次运行程序:随机同学姓名1 (只显示名字)
- 第二次运行程序:随机同学姓名2(只显示名字)
- 第三次运行程序:随机同学姓名3(只显示名字)
package duanmingqi;import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;public class Test01 {public static void main(String[] args) throws IOException {// 1.读取文件中学生的姓名ArrayList<String> list = new ArrayList<>();BufferedReader br = new BufferedReader(new FileReader("IOPractice\\name.txt"));String line;while ((line = br.readLine()) != null){list.add(line);}br.close();// 2.随机抽取Random r = new Random();int index = r.nextInt(list.size());String str = list.get(index);String[] info = str.split("-");System.out.println(info[0]);}
}
版本2(独立完成)
需求:
一个文件里面存储了班级同学的信息,每一个学生信息占一行
格式为:张三-男-23
要求通过程序实现随机点名器。
运行效果:
- 70%的概率随机到男生
- 30%的概率随机到女生
- 总共随机100万次,统计结果。
- 注意观察:看生成男生和女生的比例是不是接近于7:3
package duanmingqi;import javax.sql.rowset.serial.SerialStruct;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;public class Test02 {public static void main(String[] args) throws IOException {/** 需求:一个文件里面存储了班级同学的信息,每一个学生信息占一行格式为:张三-男-23要求通过程序实现随机点名器。运行效果:- 70%的概率随机到男生- 30%的概率随机到女生- 总共随机100万次,统计结果。- 注意观察:看生成男生和女生的比例是不是接近于7:3* */// 读取数据ArrayList<String> list = new ArrayList<>();BufferedReader br = new BufferedReader(new FileReader("IOPractice\\name.txt"));String line;while((line = br.readLine()) != null){list.add(line);}br.close();System.out.println(list);// 分两个集合 一男一女ArrayList<String> boyList = new ArrayList<>();ArrayList<String> girlList = new ArrayList<>();// 遍历集合, 判断并进行导入for (String str : list) {String[] info = str.split("-");if("男".equals(info[1])){boyList.add(str);}else{girlList.add(str);}}
// System.out.println(boyList);
// System.out.println(girlList);// 创建集合确定权重// 0 - boy// 1 - girlArrayList<Integer> weight = new ArrayList<>();for (int i = 0; i < 7; i++) {weight.add(0);}for (int i = 0; i < 3; i++) {weight.add(1);}// System.out.println(sex);int countBoy = 0;int countGirl = 0;for (int i = 0; i < 10000; ++i) {// 从里面随机选一个数, 0就随机男生, 1随机女生Collections.shuffle(weight);int sex = weight.get(0);if(sex == 0){// 随机男生
// Collections.shuffle(boyList);
// System.out.println(boyList.get(0));++countBoy;}else{// 随机女生
// Collections.shuffle(girlList);
// System.out.println(girlList.get(0));++countGirl;}}System.out.println(countBoy); // 7033System.out.println(countGirl); // 2697}
}
版本3(独立完成)
需求:
一个文件里面存储了班级同学的姓名,每一个姓名占一行要求通过程序实现随机点名器。
第三次必定是张三同学
运行效果:
- 第一次运行程序:随机同学姓名1
- 第二次运行程序:随机同学姓名2
- 第三次运行程序:张三
package duanmingqi;import java.io.*;
import java.util.ArrayList;
import java.util.Collections;public class Test03 {public static void main(String[] args) throws IOException {ArrayList<String> list = new ArrayList<>();// 读取数据BufferedReader br = new BufferedReader(new FileReader("IOPractice\\name.txt"));String line;while((line = br.readLine()) != null){list.add(line);}br.close();// 创建一个文件, 记录第几次点名
// BufferedWriter bw = new BufferedWriter(new FileWriter("IOPractice\\count.txt"));
// bw.write("0");
// bw.close();// 开始点名// 先读文件看是第几次点名BufferedReader br2 = new BufferedReader(new FileReader("IOPractice\\count.txt"));String countStr = br2.readLine();int count = Integer.parseInt(countStr);BufferedWriter bw = new BufferedWriter(new FileWriter("IOPractice\\count.txt"));if(count == 2){// 第三次点名, 直接打印张三System.out.println("张三");// 更改文件中次数++count;bw.write(count + "");}else{// 正常点名Collections.shuffle(list);String info = list.get(0);String name = info.split("-")[0];System.out.println(name);// 更改文件中次数++count;bw.write(count + "");}bw.close();}
}
版本4(独立完成)
需求:
一个文件里面存储了班级同学的姓名,每一个姓名占一行
要求通过程序实现随机点名器
运行效果:
- 被点到的学生不会再被点到
- 如果班级中所有的学生都点完了,需要自动的重新开启第二轮点名
- 细节1:假设班级有10个学生,每一轮中每一位学生只能被点到一次,程序运行10次,第一轮结束
- 细节2:第11次运行的时候,我们自己不需要手动操作本地文件,要求程序自动开始第二轮点名
登录注册
案例1
需求:写一个登陆小案例。
步骤:
- 将正确的用户名和密码手动保存在本地的userinfo.txt文件中
- 保存格式为:username=zhangsan&password=123
- 让用户键盘录入用户名和密码
- 比较用户录入的和正确的用户名密码是否一致
- 如果一致则打印登陆成功
- 如果不一致则打印登陆失败
package com.itheima.myiotest7;import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;public class Test {public static void main(String[] args) throws IOException {/*需求:写一个登陆小案例。步骤:将正确的用户名和密码手动保存在本地的userinfo.txt文件中。保存格式为:username=zhangsan&password=123让用户键盘录入用户名和密码比较用户录入的和正确的用户名密码是否一致如果一致则打印登陆成功如果不一致则打印登陆失败*///1.读取正确的用户名和密码BufferedReader br = new BufferedReader(new FileReader("myiotest\\src\\com\\itheima\\myiotest7\\userinfo.txt"));String line = br.readLine();//username=zhangsan&password=123br.close();String[] userInfo = line.split("&");String[] arr1 = userInfo[0].split("=");String[] arr2 = userInfo[1].split("=");String rightUsername = arr1[1];String rightPassword = arr2[1];//2.用户键盘录入用户名和密码Scanner sc = new Scanner(System.in);System.out.println("请输入用户名");String username = sc.nextLine();System.out.println("请输入密码");String password = sc.nextLine();//3.比较if(rightUsername.equals(username) && rightPassword.equals(password)){System.out.println("登陆成功");}else{System.out.println("登陆失败");}}
}
配置文件
概述
优点:
- 好处1: 可以把软件的设置永久化存储
- 好处2: 如果我们要修改参数,不需要改动代码,直接修改配置文件就可以了
因为修改代码的话需要重新打包, 重新发布, 很麻烦
常见的配置文件
XML ini properties YAML
properties
properties是一个双列集合集合,拥有Map集合所有的特点。
重点:有一些特有的方法,可以把集合中的数据,按照键值对的形式写到配置文件当中也可以把配置文件中的数据,读取到集合中来
Properties类基本用法
package Test1;import java.util.Map;
import java.util.Properties;
import java.util.Set;public class Demo1 {public static void main(String[] args) {// 基本用法Properties prop = new Properties();// 插入数据prop.put("aaa", "111");prop.put("bbb", "222");prop.put("ccc", "333");prop.put("ddd", "444");// 遍历Set<Map.Entry<Object, Object>> entries = prop.entrySet();for (Map.Entry<Object, Object> entry : entries) {Object key = entry.getKey();Object value = entry.getValue();System.out.println(key + "=" + value);}}
}
写出数据
package Test1;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;public class Demo1 {public static void main(String[] args) throws IOException {// 基本用法Properties prop = new Properties();// 插入数据prop.put("aaa", "111");prop.put("bbb", "222");prop.put("ccc", "333");prop.put("ddd", "444");FileOutputStream fos = new FileOutputStream("properties\\a.properties");prop.store(fos, "test");fos.close();}
}
读数据
public class Demo1 {public static void main(String[] args) throws IOException {// 读数据Properties prop = new Properties();FileInputStream fis = new FileInputStream("properties\\a.properties");prop.load(fis);fis.close();System.out.println(prop); // {aaa=111, ccc=333, bbb=222, ddd=444}}
}