# IO流
Io流是数据存储和读取的解决方案,用于读写文件中的数据,或网络中的数据
注意
- File类:表示系统文件中的文件或者文件夹的路径
- File类:只能对文件本身进行操作,不能读写文件里面存储的数据
# Io流的分类
- 流的方向:IO流->输入、输出流
- 操作文件类型:IO流->字节流(能操作所有类型的文件)、字符流(纯文本文件)
- 输入流负责读取数据,输出流负责写入数据
# 基本IO流:字节流、字符流
# 高级流
# 缓冲流
- 字节流:字节缓冲输入流、字节缓冲输出流
- 字符流:字符缓冲输入流、字符缓冲输出流
基本流与高级流相比:高级流全部带有缓冲区(字符流自带缓冲区),并且这四种流封装了一些方法
# 字节输出流FileOutputStream
package Io;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//Io流
//演示:使用字节流进行操作文件
//写一段文字到本地文件夹中(暂时不写中文)
//实现步骤:
// 1.创建对象
//2.写出数据
//3.释放资源
//1.创建对象
//写出 输出流 OutputStream
//本地文件 File
FileOutputStream fos = new FileOutputStream("attrs\\file\\a.txt");
//2.写入数据 要求Int 类型
fos.write("hello Java".getBytes());
//3.释放资源
fos.close();
}
}
注意
字节输出流的细节
- 创建字节输出流的对象
- 参数是字符串表示的路径或者是File对象都是可以的
- 如果文件不存在,则会创建一个新的文件,但是要保证父级路径是存在的
- 如果文件已经存在,构造则会清空并覆盖文件
- 写数据
write方法的参数是整数,但实际上写到本地文件中的是整数在ASCII上对应的字符- 97->a
- 释放资源
- 每次使用完流之后都要释放资源,否则会占用着文件
# FileOutputStream写数据的三种方式
| 方法名称 | 说明 |
|---|---|
void write(int b) | 写入一个字节到文件中(一次写一个字节) |
void write(byte[] b) | 写入一个字节数组到文件中(一次写一个字节数组) |
void write(byte[] b, int off, int len) | 写入一个字节数组到文件中(一次写一个字节数组的一部分) |
package Io;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//1. void write(byte[] b)
FileOutputStream fos1 = new FileOutputStream("attrs\\file\\a.txt");
fos1.write("hello Java123".getBytes());
fos1.close();
//字节数组
System.out.println(Arrays.toString("hello Java123".getBytes()));//[104, 101, 108, 108, 111, 32, 74, 97, 118, 97, 49, 50, 51]
//2. void write(byte[] b, int off, int len)
FileOutputStream fos2 = new FileOutputStream("attrs\\file\\a.txt");
//参数一:off起始位置 len长度(写多少个)
fos2.write("hello Java123".getBytes(), 0, 7);
fos2.close();
}
}
# 换行和续写
package Io;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//2. void write(byte[] b, int off, int len)
FileOutputStream fos2 = new FileOutputStream("attrs\\file\\a.txt");
//参数一:off起始位置 len长度(写多少个)
fos2.write("hello Java123".getBytes(), 0, 7);
//换行和续写
//换行写 (就是在中间写一个换行符就可以)
//win系统 \r\n
//linux系统 \n
//mac \r
fos2.write("\r\n".getBytes());
fos2.write("\n".getBytes());
fos2.write("\r".getBytes());
//在win系统中java对换行进行了优化 虽然完整的是\r\n,但是我们写其中一个\r或\n java在底层会进行优化
//建议写\r\n
//续写
//在创建对象时 如果文件已经存在,则会清空文件内容
//FileOutputStream 第二个参数append为是否开启续写 默认值为false
//开启后创建对象则会开启续写
FileOutputStream fos3 = new FileOutputStream("attrs\\file\\a.txt", true);
fos2.write("my home".getBytes());
fos2.close();
}
}
# 字节输入流FileInputStream
字节输入流FileInputStream用于读取文件
package Io;
import java.io.FileInputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//字节输入流`FileInputStream`
//1.创建字节输入流的对象
//如果文件不存在则直接报错
FileInputStream fis = new FileInputStream("attrs\\file\\a.txt");
//2.读取数据
//一个一个的读取
//如果读不到则返回-1
int read = fis.read();
//打印数据
System.out.println((char) read);
}
}
# 字节流循环读取
package Io;
import java.io.FileInputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//字节流循环读取
//创建对象
FileInputStream fis1 = new FileInputStream("attrs\\file\\a.txt");
//循环读取数据
int b;
while ((b = fis1.read()) != -1) {
System.out.println((char) b);
}
//读取完毕释放资源
fis1.close();
}
}
# 练习 文件拷贝
package Io;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
//练习 文件拷贝
//读取文件对象
FileInputStream fis2 = new FileInputStream("attrs\\file\\a.txt");
//拷贝文件对象
FileOutputStream fos4 = new FileOutputStream("attrs\\file\\b.txt");
int c;
while ((c = fis2.read()) != -1) {
//边读边写
fos4.write(c);
}
fos4.close();
//先开的流 最后再关闭
fis2.close();
}
}
# FileOutputStream一次读取多个字节
| 方法名称 | 说明 |
|---|---|
int read() | 一次读取一个字节 |
int read(byte[] buffer) | 一次读取多个字节数组 |
package Io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//一次读取多个数据
FileInputStream fis3 = new FileInputStream("attrs\\file\\a.txt");
//创建一个字节数组
byte[] bytes = new byte[2];
//read返回读取了几个数据
int read1 = fis3.read(bytes);
System.out.println(new String(bytes, 0, read1));
}
}
# 字节流异常处理
使用try-with-resources实现特定情况自动释放资源
public class Main {
public static void main(String[] args) throws IOException {
//io 字节流异常处理
//只有实现了AutoCloseable接口的流才可以使用try-with-resources
//功能:特定情况下自动释放资源
try (FileInputStream fis7 = new FileInputStream("attrs\\file\\e.txt");
FileOutputStream fos8 = new FileOutputStream("attrs\\file\\c.txt")) {
//拷贝文件对象
int e;
byte[] bytes2 = new byte[1024 * 1024];
while ((e = fis7.read(bytes2)) != -1) {
//边读边写
fos8.write(bytes1, 0, d);
}
} catch (IOException e) {
System.err.println(e.getMessage());
}
}
}
# 字符集详解
计算机存储规则
- GB2312:1980年发布 中文字符编码规则
- BIG5:1984年发布 台湾地区繁体字符编码规则
- GBK:2000年发布 兼容GB2312和BIG5 包含日文、韩文、中文和繁体
- 简体中文版windows系统默认的字符集就是GBK。系统显示:ANSI(兼容大部分国家字符集)
# 汉字的存储规则(GBK)
- 一个汉字使用两个字节存储
# uniCode字符集(万国码)
utf-8:是一种uniCode的编码方式
utf-8:用1~4的字节表示文字
- 英文:1个字节
- 汉字:3个字节
# 为什么会有乱码
- 字符集不匹配
- 读取数据是未读完整个汉字
- 编码和解码的方式不匹配
# 如何不产生乱码
- 不使用字节流读取文本文件
- 编码和解码时使用同一个码表,同一个编码方式
# 编码和解码的方式
编码方式
String类中的方法 | 说明 |
|---|---|
public byte[] getBytes() | 使用默认的方式进行编码 |
public byte[] getBytes(String charsetName) | 使用指定的编码的方式进行编码 |
解码方式
String类中的方法 | 说明 |
|---|---|
String(byte[] bytes) | 使用默认的方式进行编码 |
String(byte[] bytes,String charsetName) | 使用指定的方式进行解码 |
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class Main {
public static void main(String[] args) {
//使用指定的编码和解码方式读取和写入中文
//写入/读取中文
//创建输出/输入流对象
try (FileOutputStream fileOutputStream = new FileOutputStream("attrs\\file\\a.txt");
FileInputStream fileInputStream = new FileInputStream("attrs\\file\\a.txt");
) {
//写入中文
fileOutputStream.write("你好,我是java".getBytes(StandardCharsets.UTF_8));
//读取中文
byte[] bytes3 = new byte[1024 * 1024];
int read2 = fileInputStream.read(bytes3);
System.out.print(new String(bytes3, 0, read2, StandardCharsets.UTF_8));
} catch (IOException e) {
System.err.println("中文读取或写入失败");
}
}
}
# 字符输入流FileReader
字符流的使用方式与字节流基本一致
# 空参read方法详解
public class Main {
public static void main(String[] args) throws IOException {
try (
//创建字符流输入对象
FileReader fileReader = new FileReader("attrs\\file\\a.txt")
) {
int read3;
//读取数据read()
//字符流的底层是字节流,默认也是一个字节一个字节读取
//如果遇到中文就会一次读取多个,CBK一次一次读两个字节,UTF-8一次读取多个
//在读取之后,方法的底层还是会进行解码并转成十进制
//最终把这个十进制作为返回值
//这个十进制的数据也表示在字符集上的数子
while ((read3 = fileReader.read()) != -1) {
//字符集输入流返回十进制数
System.out.println(read3);//25105
//如果想看到中文,就需要把这些十进制数据进行强转
System.out.print((char) read3);
}
} catch (IOException e) {
System.err.println("字符流读取失败");
}
}
}
# 带参read方法详解
public class Main {
public static void main(String[] args) {
//字符流带参输入
FileReader fileReader1 = new FileReader("attrs\\file\\a.txt");
char[] chars = new char[1024 * 1024];
//read(chars):读取数据,解码、强转三步合并了,把强制转换后的字符放到数组中
//空参read+强制类型转换
int read2 = fileReader1.read(chars);
System.out.println(Arrays.toString(chars));
System.out.println(new String(chars, 0, read2));
fileReader1.close()
}
}
# 字符流输出流FileWriter
# FileWriter的构造方法
| 构造方法 | 说明 |
|---|---|
public FileWriter(File file) | 创建字符输出流关联本地文件 |
public FileWriter(String file) | 创建字符输出流关联本地文件 |
public FileWriter(File file,boolean append) | 创建字符输出流关联本地文件,续写 |
public FileWriter(String file,boolean append) | 创建字符输出流关联本地文件,续写 |
public class Main {
public static void main(String[] args) throws IOException {
//字符流输出FileWriter
FileWriter fileWriter = new FileWriter("attrs\\file\\a.txt");
fileWriter.write(Content.article1);
fileWriter.close();
//续写
FileWriter fileWriter1 = new FileWriter("attrs\\file\\a.txt", true);
fileWriter1.write("\r\njava续写");
fileWriter1.close();
}
}
# FileWriter的write成员方法
| 成员方法 | 说明 |
|---|---|
public void write(int c) | 写出一个字符 |
public void write(String str) | 写出字符串 |
public void write(char[] chars) | 写出字符数组 |
public void write(String str,int start,int end) | 写出字符串的指定部分 |
public void write(char[] chars,int start,int end) | 写出字符数组的指定部分 |
public class Main {
public static void main(String[] args) throws IOException {
//字符输出流的成员方法
FileWriter fileWriter2 = new FileWriter("attrs\\file\\a.txt", true);
//当调用write方法时:根据字符集的编码方式进行编码,把编码之后的数据写到文件中去
//写出一个字符
fileWriter2.write(97);
//写出一个字符串
fileWriter2.write("\r\n");
//写出字符串的一部分
fileWriter2.write("你好java", 0, 3);
//写出一个字符数组
fileWriter2.write(new char[]{'我', '是', 'c'});
//写出字符数组的一部分
fileWriter2.write(new char[]{'我', 'b', 'c'}, 0, 2);
fileWriter2.close();
}
}
# 字符流原理详解
- 创建字符输入流对象 底层:关联文件,并创建缓冲区(长度为8192的字节数组)
- 读取数据 底层:
- 判断缓冲区中是否有数据可以读取
- 如果缓冲区中有数据:就从缓冲区中读取
- 如果缓冲区中没有数据,就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区,如果文件中也没有数据了,则返回-1
read方法
- 空参的
read方法:一次读取一个字节,遇到中文一次读多个字节,把字节编码并转成十进制返回 - 有参的
read方法:把读取字节、解码、强转三步合并,强转之后的字符放到数组中
# 字符流与字节流的使用场景
字节流:
- 拷贝任意类型的文件
字符流:
- 读取纯文本文件中的数据
- 往纯文本文件中写出数据
# 字符与字节流综合练习
# 文件拷贝
public class Main {
public static void main(String[] args) throws IOException {
//1.拷贝一个文件夹,考虑子文件夹
File file = new File("attrs\\file");
//拷贝的文件夹
File file1 = new File("attrs\\file1");
//文件拷贝
copyFile(file, file1);
}
//拷贝文件夹 包含子文件夹
/**
* 作用:拷贝文件夹
*
* @param src File 原数据
* @param copySrc File 目标数据
* @throws IOException 可能出现的错误
*/
public static void copyFile(File src, File copySrc) throws IOException {
//创建父级路径
copySrc.mkdir();
File[] files = src.listFiles();
if (files == null) return;
for (File file : files) {
if (file.isFile()) {
//如果是文件,直接拷贝
FileInputStream fileInputStream = new FileInputStream(file);
//创建文件
FileOutputStream fileOutputStream = new FileOutputStream(new File(copySrc, file.getName()));
int readLength;
byte[] bytes = new byte[1024 * 1024];
while ((readLength = fileInputStream.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, readLength);
}
fileOutputStream.close();
fileInputStream.close();
} else copyFile(file, new File(copySrc, file.getName()));
}
}
}
# 加密解密文件
public class Main {
public static void main(String[] args) throws IOException {
//2.加密和解密文件
//数据源
File file2 = new File("attrs\\file\\a.txt");
//加密文件位置
File file3 = new File("attrs\\file\\a-加密文件.txt");
encryptDecryptionFile(file2, 1, file3);
//解密文件位置
File file4 = new File("attrs\\file\\a-解密文件.txt");
//解密文件
encryptDecryptionFile(file3, 1, file4);
}
/**
* 作用:加密解密文件
*
* @param src File 源文件
* @param key int 加密解密密钥
* @param copySrc File 目标文件
*/
public static void encryptDecryptionFile(File src, int key, File copySrc) throws IOException {
if (!src.isFile()) throw new IOException("src不是文件");
//读取文件对象
FileInputStream fileInputStream = new FileInputStream(src);
//写入文件对象
FileOutputStream fileOutputStream = new FileOutputStream(copySrc);
int b;
while ((b = fileInputStream.read()) != -1) {
//^ 异或 相同返回false 不同返回true
//当运算符两边为数字时,会将两边的数字转为2进制进行与运算得到的结果再转为十进制进行输出
//当进行两次与运算时,会得到原始的数字
fileOutputStream.write(b ^ key);
}
fileOutputStream.close();
fileInputStream.close();
}
}
# 文件排序
public class Main {
public static void main(String[] args) throws IOException {
/*
3.文本文件中有以下数据
2-1-5-4-3
将文件中的数据进行排序,并输出到新文件中
1-2-3-4-5
*/
//源文件
File file5 = new File("attrs\\file\\d.txt");
//目标文件
File file6 = new File("attrs\\file\\d-排序后.txt");
sortFile(file5, file6);
}
/**
* 作用:排序文件
*
* @param src File 源文件
* @param copySrc File 目标文件
*/
public static void sortFile(File src, File copySrc) throws IOException {
if (!src.isFile()) throw new IOException("src不是文件");
//创建读取文件对象
FileReader fileReader = new FileReader(src);
int b;
StringBuilder sb = new StringBuilder();
while ((b = fileReader.read()) != -1) sb.append((char) b);
fileReader.close();
Integer[] split = Arrays.stream(sb.toString().split("-")).map(Integer::parseInt).sorted().toArray(Integer[]::new);
//处理字符串
String str = Arrays.toString(split);
String finallyStr = str.substring(1, str.length() - 1).replace(", ", "-");
//创建输出流对象
FileWriter fileWriter = new FileWriter(copySrc);
fileWriter.write(finallyStr);
fileWriter.close();
}
}
# 字节缓冲流
- 原理:底层自带了长度为8192的缓冲区提高性能
- 缓冲流是高级流,他对基本流做了一个包装
- 在创建高级流时需要创建基本流,高级流不能单独工作
| 方法名称 | 说明 |
|---|---|
public BufferedInputStream(InputStream is) | 把基本流包装成高级流,提高读写数据的性能 |
public BufferedOutputStream(OutputStream os) | 把基本流包装成高级流,提高读写数据的性能 |
public class Main {
public static void main(String[] args) throws IOException {
//字节缓冲流
//利用字节缓冲流来拷贝文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("attrs\\file\\a.txt"));
//读取数据 readAllBytes()获取全部的字符数据并保存到数组中
byte[] bytes2 = bis.readAllBytes();
//拷贝文件对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("attrs\\file\\a-字节缓冲流拷贝.txt"));
bos.write(bytes2);
bos.close();
bis.close();
}
}
# 字符缓冲流
- 字符缓冲流本身自带8192的缓冲区
- 字符流本身自带缓冲区,因此性能几乎没有提升,但字符缓冲流封装了一些方法,是的操作更加简单
| 方法名称 | 说明 |
|---|---|
public BufferedReader(Reader r) | 把基本流包装成高级流,提高读写数据的性能 |
public BufferedWriter(Writer w) | 把基本流包装成高级流,提高读写数据的性能 |
# 字符缓冲输入流特有的方法BufferedReader
| 方法名称 | 说明 |
|---|---|
public String readLine() | 读取一行数据,如果没有数据可读了,会返回null |
# 字符缓冲输出流特有的方法BufferedWriter
| 方法名称 | 说明 |
|---|---|
public void newLine() | 写入一个换行符,兼容全平台 |
# 实现
public class Main {
public static void main(String[] args) throws IOException {
//字符缓冲流
//利用字符缓冲流来拷贝文件
BufferedReader bufferedReader = new BufferedReader(new FileReader("attrs\\file\\a.txt"));
//拷贝文件对象
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("attrs\\file\\a-字符缓冲流拷贝.txt"));
//读取数据 一行一行的读取,遇到回车换行就会结束(不会把回车换行读到内存中,会清除原来的换行)
//
String s;
//如果没有数据可读了 则会返回null
while ((s = bufferedReader.readLine()) != null) {
bufferedWriter.write(s);
//换行
bufferedWriter.newLine();
}
bufferedWriter.close();
bufferedReader.close();
}
}
# 文件缓冲流练习
public class Main {
public static void main(String[] args) throws IOException {
//记录软件运行次数
//将软件运行次数写入本地文件中存储
String fileUrl = "attrs\\file\\软件运行次数.txt";
File file7 = new File(fileUrl);
//如果文件不存在,则创建文件 防止BufferedReader报错
file7.createNewFile();
BufferedReader bufferedReader1 = new BufferedReader(new FileReader(file7));
String read3 = bufferedReader1.readLine();
//如果文件为空 则将read3手动赋值为0 避免重复写入文件
if (read3 == null) read3 = "0";
// if (read3 == null) {
// BufferedWriter bufferedWriter1 = new BufferedWriter(new FileWriter(file7));
// bufferedWriter1.write("1");
// bufferedWriter1.close();
// read3 = bufferedReader1.readLine();
// //注意只有关闭了流重新读取文件才会更新
// //读取的是缓冲区的数据
// bufferedReader1.close();
// }
//使用try-with-resource 在报错时关闭流
try (BufferedWriter bufferedWriter1 = new BufferedWriter(new FileWriter(file7))) {
bufferedWriter1.write(String.valueOf(Integer.parseInt(read3) + 1));
if (Integer.parseInt(read3) > 3) throw new Error("本软件只能免费3次,欢迎您注册会员后使用");
}
}
}
# 字符流-转换流
- 转换流是字符流和字节流之间的桥梁
- 将字节流转换为字符流,拥有字节流特性的同时,又可以使用字符流中的方法
public class Main {
public static void main(String[] args) throws IOException {
//字符转换流 在jdk11时这种方式被淘汰了
//1.利用转换流按照指定的字符编码读取数据
// InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("attrs\\file\\字符转换流.txt"), "GBK");
// char[] chars4 = new char[1024 * 1024];
// int read4;
// while ((read4 = inputStreamReader.read(chars4)) != -1) {
// System.out.print(new String(chars4, 0, read4));
// }
// inputStreamReader.close();
FileReader fileReader = new FileReader("attrs\\file\\字符转换流.txt", Charset.forName("GBK"));
char[] chars4 = new char[1024 * 1024];
int read4;
//创建输出流对象
FileWriter outputStreamWriter = new FileWriter("attrs\\file\\字符转换流1.txt", Charset.forName("GBK"));
//在jdk11时已经淘汰
// OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("attrs\\file\\字符转换流1.txt"), Charset.forName("GBK"));
while ((read4 = fileReader.read(chars4)) != -1) {
outputStreamWriter.write(chars4, 0, read4);
}
outputStreamWriter.close();
fileReader.close();
//2.文件字符类型转换
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("attrs\\file\\字符转换流.txt"), Charset.forName("GBK"));
OutputStreamWriter outputStreamWriter1 = new OutputStreamWriter(new FileOutputStream("attrs\\file\\字符转换流-utf-转换.txt"), StandardCharsets.UTF_8);
char[] chars5 = new char[1024 * 1024];
int read5;
while ((read5 = inputStreamReader.read(chars5)) != -1) {
outputStreamWriter1.write(chars5, 0, read5);
}
outputStreamWriter1.close();
inputStreamReader.close();
//替代方案直接创建FireReader和FireWriter
FileReader fireReader = new FileReader("attrs\\file\\字符转换流.txt", Charset.forName("GBK"));
FileWriter fireWriter = new FileWriter("attrs\\file\\字符转换流-utf-转换.txt", StandardCharsets.UTF_8);
char[] chars6 = new char[1024 * 1024];
int read6;
while ((read6 = fireReader.read(chars6)) != -1) {
fireWriter.write(chars6, 0, read6);
}
fireWriter.close();
fireReader.close();
//3.利用字节流读取数据,每次读一整行,而且不能出现乱码
//一次读一整行时字符缓冲流特有的
String fileUrl = "attrs\\file\\字符转换流1.txt";
//创建字节流对象
FileInputStream fileInputStream = new FileInputStream(fileUrl);
//将字节流包装成字符流(利用字符转换流)
InputStreamReader inputStreamReader2 = new InputStreamReader(fileInputStream, "GBK");
//将字符流包装成字符缓冲流
BufferedReader bufferedReader1 = new BufferedReader(inputStreamReader2);
//读取一整行
System.out.println(bufferedReader1.readLine());
//释放资源
bufferedReader1.close();
}
}
# 字节流-序列化流(对象操作输出流)
- 可以吧java中的对象写到文件当中
# 构造方法
| 构造方法 | 说明 |
|---|---|
public ObjectOutputStream(OutputStream out) | 把基本流包装成高级流 |
# 成员方法
| 成员方法 | 说明 |
|---|---|
public void writeObject(Object obj) | 把对象写入到文件中 |
注意
使用序列化流将对象直接保存到文件时会报NotSerializableException异常
- 解决方案:需要让
javaBean类实现Serializable接口 Serializable接口中没有抽象方法,属于标记型接口- 一旦实现了这个接口,那么表示当前类可以被序列化
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化流
//创建序列化流(序列化流是由字节流包装而来)
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("attrs\\file\\序列化流.txt"));
People people = new People("张三", 18, '男');
objectOutputStream.writeObject(people);
objectOutputStream.close();
}
}
package Io;
import java.io.Serializable;
public class People implements Serializable {
/*
*Serializable接口中没有抽象方法,属于标记型接口
* 表示:一旦实现了这个接口,那么表示当前类可以被序列化
* */
private String name;
private int blod;
private char sex;
public People(String name, int blod, char sex) {
this.name = name;
this.blod = blod;
this.sex = sex;
}
public People() {
}
/**
* 获取
*
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
*
* @return blod
*/
public int getBlod() {
return blod;
}
/**
* 设置
*
* @param blod
*/
public void setBlod(int blod) {
this.blod = blod;
}
/**
* 获取
*
* @return sex
*/
public char getSex() {
return sex;
}
/**
* 设置
*
* @param sex
*/
public void setSex(char sex) {
this.sex = sex;
}
public String toString() {
return "People{name = " + name + ", blod = " + blod + ", sex = " + sex + "}";
}
}
# 字节流-反序列化流(对象操作输入流)
可以把序列化到本地文件中的对象,读取到程序中来
# 构造方法
| 构造方法 | 说明 |
|---|---|
public ObjectInputStream(InputStream in) | 把字节流包装成高级流 |
# 成员方法
| 成员方法 | 说明 |
|---|---|
public Object readObject() | 从文件中读取对象,返回读取的对象,读取的对象必须是序列化流所序列化的对象 |
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//反序列化流
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("attrs\\file\\序列化流.txt"));
People people1 = (People) objectInputStream.readObject();
System.out.println(people1.toString());
}
}
package Io;
import java.io.Serial;
import java.io.Serializable;
public class People implements Serializable {
//固定版本号
@Serial
private static final long serialVersionUID = 6598053267419231310L;
/*
*Serializable接口中没有抽象方法,属于标记型接口
* 表示:一旦实现了这个接口,那么表示当前类可以被序列化
* */
private String name;
//瞬态关键字 transient
//表示不会把该成员变量序列化到文件中
private transient int blod;
private char sex;
private int age;
public People() {
}
public People(String name, int blod, char sex, int age) {
this.name = name;
this.blod = blod;
this.sex = sex;
this.age = age;
}
/**
* 获取
*
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
*
* @return blod
*/
public int getBlod() {
return blod;
}
/**
* 设置
*
* @param blod
*/
public void setBlod(int blod) {
this.blod = blod;
}
/**
* 获取
*
* @return sex
*/
public char getSex() {
return sex;
}
/**
* 设置
*
* @param sex
*/
public void setSex(char sex) {
this.sex = sex;
}
/**
* 获取
*
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
*
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "People{name = " + name + ", blod = " + blod + ", sex = " + sex + ", age = " + age + "}";
}
}
注意
- 序列化流写到文件中的数据是不能改的,一旦修改了 就无法再读出来
- 序列化对象后,修改了
javaBean类,再反序列化就会报错 - 解决方案:给
javaBean类添加一个固定版本号,当反序列化时,会比较版本号,如果版本号不一致,就会报错 - 如果说成员变量某一个的值不想被序列化,可以使用
transient关键字修饰(瞬态关键字) - 反序列化如果读到文件末尾则会报异常
# 练习
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//练习:
//1.将多个自定义对象序列化到文件中,但是对象的个数不确定
People people2 = new People("张三", 18, '男', 18);
People people3 = new People("李四", 18, '女', 18);
People people4 = new People("王五", 18, '男', 18);
ObjectOutputStream objectOutputStream1 = new ObjectOutputStream(new FileOutputStream("attrs\\file\\序列化流-存储多个对象.txt"));
ArrayList<People> peoples = new ArrayList<>();
Collections.addAll(peoples, people2, people3, people4);
objectOutputStream1.writeObject(peoples);
objectOutputStream1.close();
//读取
ObjectInputStream objectInputStream1 = new ObjectInputStream(new FileInputStream("attrs\\file\\序列化流-存储多个对象.txt"));
ArrayList<People> object = (ArrayList<People>) objectInputStream1.readObject();
objectInputStream1.close();
for (People people5 : object) {
System.out.println(people5.toString());
}
}
}
# 打印流
- 打印流不能读只能写,打印流只有输出流,是字节流和字符流的高级流
# 分类
打印流一般是指:PrintStream(字节打印流)和PrintWriter(字符打印流)
# 特点
- 打印流只能操作文件的目的地,不能操作数据源
- 特有的写出方法可以实现原样写出
- 特有的写出方法,可以实现自动刷新,自动换行。打印一次数据=写出+换行+刷新
# 构造方法
| 构造方法 | 说明 |
|---|---|
public PrintStream(OutputStream/File/String out) | 关联字节输出流、文件、文件路径 |
piblic PrintStream(String fileName,Charset charset) | 指定字符编码 |
public PrintStream(OutputStream out,boolean autoFlush) | 自动刷新 |
public PrintStream(OutputStream out,boolean autoFlush,String encoding) | 指定字符编码且自动刷新 |
注意
字节流底层无缓冲区,因此开不开自动刷新都一样
# 成员方法
| 成员方法 | 说明 |
|---|---|
public void write(int b) | 规则跟之前的一样,将指定字节写出 |
public void println(Xxx xx) | 特有方法:打印任意数据,自动刷新,自动换行 |
public void print(Xxx xx) | 特有方法:打印任意数据,不换行 |
public void printf(String format,Object...args) | 特有方法:带有占位符的打印语句,不换行 |
public class Main {
public static void main(String[] args) throws IOException {
//字节打印流
PrintStream printStream = new PrintStream(new FileOutputStream("attrs\\file\\字节打印流.txt"), true, "GBK");
//字节方式写出
printStream.write(97);//a
//换行 原样写出
printStream.println(97);//97
//不换行 原样写出
printStream.println(true);//true
//使用占位符写出
printStream.printf("%s爱上了%s", "阿珍", "阿强");//阿珍爱上了阿强
printStream.close();
}
}
# 字符打印流
- 字符打印流底层具有缓冲区,想要自动刷新需要开启
# 构造方法 与字节流基本一致
| 构造方法 | 说明 |
|---|---|
public PrintWriter(OutputStream/File/String out) | 关联字符输出流、文件、文件路径 |
piblic PrintWriter(String fileName,Charset charset) | 指定字符编码 |
public PrintWriter(Write w,boolean autoFlush) | 自动刷新 |
public PrintWriter(OutputStream out,boolean autoFlush,String encoding) | 指定字符编码且自动刷新 |
# 成员方法 与字节流基本一致
| 成员方法 | 说明 |
|---|---|
public void write(int b) | 规则跟之前的一样,将指定字节写出 |
public void println(Xxx xx) | 特有方法:打印任意数据,自动刷新,自动换行 |
public void print(Xxx xx) | 特有方法:打印任意数据,不换行 |
public void printf(String format,Object...args) | 特有方法:带有占位符的打印语句,不换行 |
注意
如果不开启自动刷新,那么只有在流关闭的时候才能写入数据,此时数据保存在缓冲区中
public class Main {
public static void main(String[] args) throws IOException {
//字符打印流
PrintWriter printWriter = new PrintWriter(new FileWriter("attrs\\file\\字符打印流.txt"), true);
printWriter.write(97);//a
printWriter.println(97);//97
printWriter.println(true);//true
printWriter.printf("%s爱上了%s", "阿珍", "阿强");////阿珍爱上了阿强
printWriter.close();
}
}
# 字节流-解压缩流与压缩流
- 解压缩流与压缩流都属于字节流
- 解压缩流主要目的是读取压缩包中的文件,读取属于输入流
- 压缩流主要目的是将文件中的数据写入到压缩包中,属于输出流
# 解压缩流
- 解压本质:把每一个
zipEntry对象中的数据按照层级拷贝到另一个文件夹中 - java只能识别
zip格式的压缩包,所以解压时需要指定压缩包类型为zip
public class Main {
public static void main(String[] args) throws IOException {
//解压缩流
//定义需要解压的文件
File src = new File("attrs\\file\\ZipStreamDemo\\ZipStreamDemo.zip");
//定义需要解压的目的地
File src1 = new File("attrs\\file\\ZipStreamDemo\\ZipStreamDemo");
unZip(src, src1);
}
/**
* 解压缩流
* 定义一个方法用来解压
*
* @param src File 原数据
* @param src1 File 目标数据
*/
public static void unZip(File src, File src1) throws IllegalArgumentException, IOException, FileNotFoundException {
ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(src), Charset.forName("GBK"));
//获取压缩包中的每一个ZipEntry对象
ZipEntry nextEntry;
while ((nextEntry = zipInputStream.getNextEntry()) != null) {
System.out.println(nextEntry.toString());
//判断当前目录是文件还是文件夹
//如果是文件夹就创建文件夹
if (nextEntry.isDirectory()) {
File file = new File(src1, nextEntry.toString());
boolean mkdir = file.mkdirs();
System.out.println(mkdir + " " + file.toString());
}
//如果是文件就拷贝文件
else {
//读取目标中的文件 并把它存放到目的地文件夹中(按照底层目录进行存放)
FileOutputStream fileOutputStream = new FileOutputStream(new File(src1, nextEntry.getName()));
int b;
while ((b = zipInputStream.read()) != -1) {
fileOutputStream.write(b);
}
fileOutputStream.close();
}
}
}
}
# 压缩流
public class Main {
public static void main(String[] args) throws IOException {
//压缩流
//将一个文件变成压缩包
//定义需要压缩的文件
File src2 = new File("attrs\\file\\ZipStreamDemo\\字符流");
//定义压缩目的地
File src3 = new File("attrs\\file\\ZipStreamDemo");
onZip(src2, src3);
}
/**
* 压缩流
* 作用:将文件夹中的所有文件压缩成一个压缩包
*
* @param src File 原文件
* @param src1 File 压缩的目的地
*/
public static void onZip(File src, File src1) throws IllegalArgumentException, IOException, FileNotFoundException {
//获取文件夹中的所有文件
ArrayList<File> allFile = getAllFile(src);
System.out.println("所有数据" + allFile.toString());
//创建压缩流对象 创建压缩包
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(src1, src.getName() + ".zip")));
//循环压缩文件
for (File file : allFile) {
zos.putNextEntry(new ZipEntry(file.getPath().substring(src.getPath().length() + 1)));
//读取文件内容 放入压缩包中
FileInputStream fileInputStream = new FileInputStream(file);
int b;
while ((b = fileInputStream.read()) != -1) {
zos.write(b);
}
fileInputStream.close();
}
zos.closeEntry();
zos.close();
}
/**
* 压缩流
* 作用:压缩单个文件,不支持文件夹压缩
*
* @param src File 原文件
* @param src1 File 压缩的目的地
*/
public static void zipFile(File src, File src1) throws IllegalArgumentException, IOException, FileNotFoundException {
//创建压缩流对象
//作用:使用压缩目的地创建一个空的压缩包
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(src1, src.getName().split("\\.")[0] + ".zip")));
//创建zipEntry对象 表示压缩包里面的每一个对象
//ZipEntry中的形参也表示压缩包里面的路径
ZipEntry zipEntry = new ZipEntry(src.getPath());
//将zipEntry对象添加到压缩流对象中 表示将文件放入压缩包中
zos.putNextEntry(zipEntry);
//把文件中的数据写入到压缩流中
FileInputStream fileInputStream = new FileInputStream(src);
int b;
while ((b = fileInputStream.read()) != -1) {
zos.write(b);
}
zos.closeEntry();
zos.close();
}
}
# Commons-io包
Commons-io包是java中的一个开源包,提供了许多与io操作相关的工具类,如文件复制、文件移动、文件重命名、文件删除、文件重命名 第三方架包的使用步骤- 在项目中创建
lib文件夹 - 将架包复制粘贴到该文件夹下
- 右键点击架包,选择
add as library->点击ok
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
public class Main {
public static void main(String[] args) throws IOException {
// commons-io
//fileUtils类
//复制文件 copyFile
File src4 = new File("attrs\\file\\ZipStreamDemo\\a-解密文件.txt");
File src5 = new File("attrs\\file\\ZipStreamDemo\\a-解密文件-复制.txt");
FileUtils.copyFile(src4, src5);
//复制文件夹
File src6 = new File("attrs\\file\\ZipStreamDemo\\字符流");
File src7 = new File("attrs\\file\\ZipStreamDemo\\字符流-复制");
FileUtils.copyDirectory(src6, src7);
//清空文件夹
File src9 = new File("attrs\\file\\ZipStreamDemo\\字符流-复制");
FileUtils.cleanDirectory(src9);
//删除文件夹
File src8 = new File("attrs\\file\\ZipStreamDemo\\字符流-复制");
FileUtils.deleteDirectory(src8);
//读取文件中的数据变成字符串
File src10 = new File("attrs\\file\\ZipStreamDemo\\a-解密文件.txt");
String content = FileUtils.readFileToString(src10, StandardCharsets.UTF_8);
System.out.println(content);
//写出数据
File src11 = new File("attrs\\file\\ZipStreamDemo\\Common-io-写出数据.txt");
FileUtils.writeStringToFile(src11, content, StandardCharsets.UTF_8);
//IOUtils类
//复制文件
File src12 = new File("attrs\\file\\ZipStreamDemo\\a-解密文件.txt");
File src13 = new File("attrs\\file\\ZipStreamDemo\\a-解密文件-复制(副本).txt");
IOUtils.copy(new FileInputStream(src12), new FileOutputStream(src13));
//复制大文件
File src14 = new File("attrs\\file\\ZipStreamDemo\\a-解密文件.txt");
File src15 = new File("attrs\\file\\ZipStreamDemo\\大文件-复制.txt");
IOUtils.copyLarge(new FileInputStream(src14), new FileOutputStream(src15));
//读取数据(一行读取)
File src16 = new File("attrs\\file\\ZipStreamDemo\\a-解密文件.txt");
List<String> strings = IOUtils.readLines(new FileReader(src16));
System.out.println(strings.toString());
//写出数据
//lineEnding:每行结尾的字符
File src17 = new File("attrs\\file\\ZipStreamDemo\\Common-io-写出数据.txt");
IOUtils.writeLines(strings, "\n", new FileWriter(src17));
}
}
# Hutool工具包
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
# IO流相关类
| 类名 | 作用 |
|---|---|
IoUtil | 流操作工具类 |
FileUtil | 文件操作工具类 |
FileUtil | 文件类型判断工具类 |
watchMonitor | 目录、文件监听 |
ClassPathResource | 针对ClassPath中资源的访问封装 |
FileReader | 封装文件读取 |
FileWriter | 封装文件写入 |
import cn.hutool.core.io.FileUtil;
public class Main {
public static void main(String[] args) throws IOException {
//hutool工具包
//根据参数获取一个file对象
File file7 = FileUtil.file("attrs\\file\\ZipStreamDemo\\a-解密文件.txt");
System.out.println(file7.getPath());
//根据参数创建文件
//创建父级
File file8 = new File("attrs\\file\\aaa\\Hutool-创建文件.txt");
File touch = FileUtil.touch(file8);
System.out.println(touch.getPath());
//读取数据
//将文件中的数据读取到集合中
List<String> list = FileUtil.readLines(new File("attrs\\file\\ZipStreamDemo\\a-解密文件.txt"), StandardCharsets.UTF_8);
System.out.println(list.toString());
//写入数据
FileUtil.writeLines(list, file8, StandardCharsets.UTF_8);
}
}
# IO流综合练习
# 使用网络爬虫获取随机姓名
# 使用正则表达式方式获取指定字符串
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
public class Main {
//姓氏数组
static ArrayList<String> familyNameList = new ArrayList<>();
//男生名字数组
static ArrayList<String> boyNameList = new ArrayList<>();
//女生名字数组
static ArrayList<String> girlNameList = new ArrayList<>();
//获取数据 加载类时立即执行
static {
try {
//通过正则表达式获取内容
getNameDataListByRegx();
} catch (URISyntaxException | IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws IOException {
//1.爬取数据
ArrayList<String> name = getName(100, 10);
System.out.println(name);
//使用字符流将数据写入本地文件中
BufferedWriter fileWriterName = new BufferedWriter(new FileWriter("attrs\\file\\ZipStreamDemo\\网络爬虫-姓名爬取.txt"));
for (String nameItem : name) {
//写入数据
fileWriterName.write(nameItem);
//换行
fileWriterName.newLine();
}
//关流
fileWriterName.close();
}
/**
* 网络爬虫-获取字符串数据
*
* @param url String 网络地址
*/
public static String getUrlDataString(String url) throws URISyntaxException, IOException {
Connection connect = Jsoup.connect(url);
Document document = connect.timeout(6000).get();
return document.toString();
}
/**
* 网络爬虫 利用正则获取数据
*
* @param url String 网络地址
* @param regex String 正则
* @param index Int 获取第几组数据
* @return return ArrayList<String> 返回数据
*/
private static ArrayList<String> getNameDataList(String url, String regex, int index) throws URISyntaxException, IOException {
//获取网站内容字符串
String urlString = getUrlDataString(url);
//使用正则对象查找内容
Pattern pattern = Pattern.compile(regex);
//按照pattern的规则到url中获取数据
Matcher matcher = pattern.matcher(urlString);
//定义存储字符串集合
ArrayList<String> list = new ArrayList<>();
//循环寻找字符串(一行一行的读取)
while (matcher.find()) {
//匹配的字符
String[] groupList = matcher.group(index).replaceAll("。", "").split(",");
list.addAll(Arrays.asList(groupList));
}
return list;
}
/**
* 利用正则获取姓名列表数据
*/
public static void getNameDataListByRegx() throws URISyntaxException, IOException {
String familyNameNet = "https://www.diyifanwen.com/tool/baijiaxing/";
//男生名字
String boyNameNet = "http://www.haoming8.cn/baobao/65736.html";
//女生名字
String girlNameNet = "http://www.haoming8.cn/baobao/92428.html";
//姓名数据处理
familyNameList = getNameDataList(familyNameNet, "([\\u4E00-\\u9FA5](,|。)){10}", 0);
//男生名字数据处理
boyNameList = getNameDataList(boyNameNet, "([\\u4E00-\\u9FA5]{2})(、|'')", 1);
//女生名字数据处理
girlNameList = getNameDataList(girlNameNet, "([\\u4E00-\\u9FA5]{2})(、|。)", 1);
}
/**
* 获取随机姓名
*
* @param boyCount 男生数量
* @param girlCount 女生数量
*/
public static ArrayList<String> getName(int boyCount, int girlCount) throws URISyntaxException, IOException {
if (boyCount == 0 || girlCount == 0) throw new IOException("boyCount或者girlCount不能为0");
//男生名字集合
HashSet<String> boyNameSet = new HashSet<>();
while (boyNameSet.size() < boyCount) {
//打乱姓氏集合
Collections.shuffle(familyNameList);
//打乱男生名字集合
Collections.shuffle(boyNameList);
//获取随机年龄17~27
Random r = new Random();
int age = r.nextInt(10) + 18;
//添加数据
boyNameSet.add(familyNameList.getFirst() + boyNameList.getFirst() + "-男-" + age);
}
//女生名字集合
HashSet<String> girlNameSet = new HashSet<>();
while (girlNameSet.size() < girlCount) {
//打乱姓氏集合
Collections.shuffle(familyNameList);
//打乱女生名字集合
Collections.shuffle(girlNameList);
//获取随机年龄17~27
Random r = new Random();
int age = r.nextInt(10) + 18;
girlNameSet.add(familyNameList.getFirst() + girlNameList.getFirst() + "-女-" + age);
}
ArrayList<String> nameList = new ArrayList<>();
//将男生和女生的数据合并 添加到nameList集合中
nameList.addAll(boyNameSet);
nameList.addAll(girlNameSet);
//打乱集合
Collections.shuffle(nameList);
return nameList;
}
}
# 利用Jsoup架包获取文档元素内容方式
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
public class Main {
//姓氏数组
static ArrayList<String> familyNameList = new ArrayList<>();
//男生名字数组
static ArrayList<String> boyNameList = new ArrayList<>();
//女生名字数组
static ArrayList<String> girlNameList = new ArrayList<>();
//获取数据 加载类时立即执行
static {
try {
//通过选择元素获取内容
getNameDataList();
} catch (URISyntaxException | IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
//1.爬取数据
ArrayList<String> name = getName(100, 10);
System.out.println(name);
//使用字符流将数据写入本地文件中
BufferedWriter fileWriterName = new BufferedWriter(new FileWriter("attrs\\file\\ZipStreamDemo\\网络爬虫-姓名爬取.txt"));
for (String nameItem : name) {
//写入数据
fileWriterName.write(nameItem);
//换行
fileWriterName.newLine();
}
//关流
fileWriterName.close();
}
/**
* 网络爬虫 爬取数据-get请求
*
* @param url String 爬取的网址
*/
public static Document getUrlData(String url) throws URISyntaxException, IOException {
Connection connect = Jsoup.connect(url);
Document document = connect.timeout(6000).get();
return document;
}
//获取姓名列表数据
private static void getNameDataList() throws URISyntaxException, IOException {
//百家姓
String familyNameNet = "https://www.diyifanwen.com/tool/baijiaxing/";
//男生名字
String boyNameNet = "http://www.haoming8.cn/baobao/65736.html";
//女生名字
String girlNameNet = "http://www.haoming8.cn/baobao/92428.html";
//姓名处理
Document familyNameData = getUrlData(familyNameNet);
Element familyNamePm = familyNameData.getElementById("pm");
Element familyNameChild = familyNamePm.child(1);
String familyNameString = familyNameChild.toString();
String[] familyNameStringList = familyNameString.replaceAll("<[^>]*>", "").replaceAll("。", "").replace("\n", "").split(",");
//名字数组
Collections.addAll(familyNameList, familyNameStringList);
System.out.println(familyNameList);
//男生名字处理
Document boyNameData = getUrlData(boyNameNet);
Element boyNameContentText = boyNameData.getElementById("contentText");
//获取所有p元素
Elements boyNameContentTextP = boyNameContentText.getElementsByTag("p");
//字符串替换
String[] bodyNameContentTextSplit = boyNameContentTextP.toString().replaceAll("</p>", "、").replaceAll("<[^>]*>", "").replaceAll("\n", "").split("、");
//男生名字数组
Collections.addAll(boyNameList, bodyNameContentTextSplit);
System.out.println(boyNameList);
//女生名字处理
Document girlNameData = getUrlData(girlNameNet);
Element girlNameContentText = girlNameData.getElementById("contentText");
Elements girlNameContentTextP = girlNameContentText.getElementsByTag("p");
Element girlNameContentTextPElement = girlNameContentTextP.get(2);
String[] girlNameContentTextList = girlNameContentTextPElement.text().replaceAll("。", "").split("、");
Collections.addAll(girlNameList, girlNameContentTextList);
System.out.println(girlNameList);
}
}
# 使用糊涂包爬取数据方式
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.HttpUtil;
public class Main {
//姓氏数组
static ArrayList<String> familyNameList = new ArrayList<>();
//男生名字数组
static ArrayList<String> boyNameList = new ArrayList<>();
//女生名字数组
static ArrayList<String> girlNameList = new ArrayList<>();
//获取数据 加载类时立即执行
static {
try {
//通过糊涂包获取内容
getNameDataListByHutoolRegx();
} catch (URISyntaxException | IOException e) {
throw new RuntimeException(e);
}
}
/**
* 使用hutool包爬取数据
*
* @param url String 网络地址
*/
public static ArrayList<String> getNameDataListByHutool(String url, String regex, int index) throws URISyntaxException, IOException {
//获取网站内容字符串
String urlString = HttpUtil.get(url).replaceAll("。", "");
List<String> stringList = ReUtil.findAll(regex, urlString, index);
return (ArrayList<String>) stringList;
}
/**
* 利用hutool包爬取并处理数据
*/
public static void getNameDataListByHutoolRegx() throws URISyntaxException, IOException {
String familyNameNet = "https://www.diyifanwen.com/tool/baijiaxing/";
//男生名字
String boyNameNet = "http://www.haoming8.cn/baobao/65736.html";
//女生名字
String girlNameNet = "http://www.haoming8.cn/baobao/92428.html";
//姓名数据处理
for (String itemName : getNameDataListByHutool(familyNameNet, "([\\u4E00-\\u9FA5](,|。)){10}", 0)) {
String[] split = itemName.split(",");
familyNameList.addAll(List.of(split));
}
//男生名字数据处理
boyNameList = getNameDataListByHutool(boyNameNet, "([\\u4E00-\\u9FA5]{2})(、|'')", 1);
//女生名字数据处理
girlNameList = getNameDataListByHutool(girlNameNet, "([\\u4E00-\\u9FA5]{2})(、|。)", 1);
}
}
# io流-随机点名器
# 随机点名器1(只显示名字)
需求:有一个文件存储了班级同学的信息,每信息占一行,格式为:姓名-性别-年龄
要求:通过程序实现随机点名
运行效果:
第一次运行程序:随机同学姓名1(只显示名字)
第二次运行程序:随机同学姓名2(只显示名字)
第三次运行程序:随机同学姓名3(只显示名字)
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//随机点名器1(只显示名字)
String name1 = getName1();
System.out.println("随机点名器1:" + name1);
}
/**
* 随机点名器1(只显示名字)
*
* @return String 学生名字
*/
public static String getName1() throws IOException {
//获取数据
BufferedReader fileReader = new BufferedReader(new FileReader("attrs\\file\\ZipStreamDemo\\网络爬虫-姓名爬取.txt"));
String b;
ArrayList<String> nameList = new ArrayList<>();
while ((b = fileReader.readLine()) != null) {
nameList.add(b);
}
Collections.shuffle(nameList);
return nameList.getFirst().split("-")[0];
}
}
# 随机点名器2(加入性别概率)
需求:一个文件里面存储了班级同学的信息,每一个学生信息占一行格式为:张三-男-23。
要求:通过程序实现随机点名器
运行效果:
70%的概率随机到男生
30%的概率随机到女生
总共随机100万次,统计结果。
注意观察:看生成男生和女生的比例是不是接近于7:3
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
String name2 = getName2();
}
/**
* 获取姓名文件中的数据
*
* @return ArrayList<String> 姓名集合
*/
public static ArrayList<String> getNameListFromFile() throws IOException {
BufferedReader fileReader = new BufferedReader(new FileReader("attrs\\file\\ZipStreamDemo\\网络爬虫-姓名爬取.txt"));
String b;
ArrayList<String> nameList = new ArrayList<>();
while ((b = fileReader.readLine()) != null) {
nameList.add(b);
}
Collections.shuffle(nameList);
return nameList;
}
/**
* 随机点名器2 70%概率随机男生 30%概率随机女生
*/
public static String getName2() throws IOException {
//获取数据
ArrayList<String> nameListFromFile = getNameListFromFile();
//打乱数据
Collections.shuffle(nameListFromFile);
//生成1-10的随机数
Random r = new Random();
int i = r.nextInt(9) + 1;
List<String> list;
if (i <= 7) {
list = nameListFromFile.stream().filter(s -> Objects.equals(s.split("-")[1], "男")).toList();
} else {
list = nameListFromFile.stream().filter(s -> Objects.equals(s.split("-")[1], "女")).toList();
}
return list.getFirst();
}
}
# 随机点名器3(第三次运行程序必定是某人)
需求:一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器。
要求:第三次必定是张三同学
运行效果:
第一次运行程序:随机同学姓名1
第二次运行程序:随机同学姓名2
第三次运行程序:张三
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//随机点名器3 软件运行三次必定是张三
for (int i = 0; i < 10; i++) {
String name3 = getName3();
System.out.println("随机点名器3:" + name3);
}
}
/**
* 随机点名器3(运行三次必定是某人)
*/
public static String getName3() throws IOException {
//获取数据
ArrayList<String> nameListFromFile = getNameListFromFile();
//打乱数据
Collections.shuffle(nameListFromFile);
//记录运行次数
File file = new File("attrs\\file\\ZipStreamDemo\\随机点名器-记录软件运行次数.txt");
//如果文件不存在则创建文件
file.createNewFile();
BufferedReader fileReader = new BufferedReader(new FileReader(file));
String s = fileReader.readLine();
fileReader.close();
if (s == null) s = "0";
//写入软件运行次数
BufferedWriter fileWriter = new BufferedWriter(new FileWriter(file));
fileWriter.write(String.valueOf(Integer.parseInt(s) + 1));
fileWriter.close();
//当软件运行次数大于等于3次时,随机点名器3必定是某人
int i = Integer.parseInt(s);
if (i >= 3) return "张三";
return nameListFromFile.getFirst().split("-")[0];
}
}
# 随机点名器4 (学生独立完成)
需求:一个文件里面存储了班级同学的姓名,每一个姓名占一行。
要求:通过程序实现随机点名器。
运行效果:
如果班级中所有的学生都点完了,需要自动的重新开启第二轮点名
细节1:假设班级有10个学生,每一轮中每一位学生只能被点到一次,程序运行10次,第一轮结束
细节2:第11次运行的时候,我们自己不需要手动操作本地文件,要求程序自动开始第二轮点名
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
//随机点名器4 被点到的学生不会再被点到
//如果班级中所有的学生都点完了,需要自动的重新开启第二轮点名
for (int i = 0; i < 100; i++) {
String name4 = getName4();
System.out.println("随机点名器4:" + name4);
}
}
/**
* 随机点名器4(被点到的学生不会再被点到)
* 如果班级中所有的学生都点完了,需要自动的重新开启第二轮点名
*/
public static String getName4() throws IOException {
//获取数据
ArrayList<String> nameListFromFile = getNameListFromFile();
//打乱数据
Collections.shuffle(nameListFromFile);
File file = new File("attrs\\file\\ZipStreamDemo\\随机点名器-记录已点到的学生.txt");
file.createNewFile();
//点名的集合
ArrayList<String> list = new ArrayList<>(nameListFromFile);
//获取已点到的学生
BufferedReader fileReader = new BufferedReader(new FileReader(file));
String s;
while ((s = fileReader.readLine()) != null) {
list.remove(s);
}
fileReader.close();
//如果班级的学生全部被点到,则清空文件
if (list.isEmpty()) {
//清空文件
file.delete();
//重新开始点名
System.out.println("班级学生全部被点到,重新开始第二轮点名");
return getName4();
}
//记录被点到的学生
String nameItem = list.getFirst();
BufferedWriter fileWriter = new BufferedWriter(new FileWriter(file, true));
if (nameItem != null) {
fileWriter.write(nameItem);
fileWriter.newLine();
fileWriter.close();
return nameItem.split("-")[0];
}
return "123";
}
}
# 随机点名器5 (带权重的随机算法)
TxT文件中事先准备好一些学生信息,每个学生的信息独占一行。
要求1:每次被点到的学生,再次被点到的概率在原先的基础上降低一半。
举例:80个学生,点名5次,每次都点到小A,概率变化情况如下:
第一次每人概率:1.25%。
第二次小A概率:0.625%。 其他学生概率:1.2579%
第三次小A概率:0.3125%。 其他学生概率:1.261867%
第四次小A概率:0.15625%。其他学生概率:1.2638449%
第五次小A概率:0.078125%。其他学生概率:1.26483386%
提示:本题的核心就是带权重的随机
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
getName5();
}
/**
* 获取姓名文件中的数据
*
* @return ArrayList<String> 姓名集合
*/
public static ArrayList<String> getNameListFromFile() throws IOException {
BufferedReader fileReader = new BufferedReader(new FileReader("attrs\\file\\ZipStreamDemo\\网络爬虫-姓名爬取.txt"));
String b;
ArrayList<String> nameList = new ArrayList<>();
while ((b = fileReader.readLine()) != null) {
nameList.add(b);
}
Collections.shuffle(nameList);
return nameList;
}
/**
* 随机点名器5(带权重的随机算法)
*/
public static String getName5() throws IOException {
//将文件中所有的学生信息读取到内存中
//并把 学生信息封装成学生对象放到集合中
ArrayList<String> nameListFromFile = getNameListFromFile();
ArrayList<Student> studentList = new ArrayList<>();
for (String student : nameListFromFile) {
String[] split = student.split("-");
studentList.add(new Student(split[0], split[1], Integer.parseInt(split[2]), Double.parseDouble(split[3])));
}
System.out.println(studentList);
//获取所有人权重的总和
double countWeight = 0;
for (Student student : studentList) {
countWeight += student.getWeight();
}
System.out.println("权重总和" + countWeight);
ArrayList<Double> weightList = new ArrayList<>();
weightList.add(studentList.getFirst().getWeight() / countWeight);
for (int i = 1; i < studentList.size(); i++) {
//计算每个人的权重占比
double v = studentList.get(i).getWeight() / countWeight;
//计算权重占比范围
double v1 = weightList.getLast() + v;
//将结果存入集合中
weightList.add(v1);
}
System.out.println("权重占比" + weightList);
//随机一个小数开始点名
double num = Math.random();
System.out.println("随机数" + num);
//判断num在weightList中的范围
//因为数据是有序的,所以使用二分查找
//方法返回 -插入点-1
//获取num这个小数在数组插入点的位置
int i = -Arrays.binarySearch(weightList.toArray(), num) - 1;
System.out.println("num在weightList中的位置" + i);
//根据索引获取学生
Student student = studentList.get(i);
System.out.println("被点到的学生" + student.getName());
//被点到的学生修改该学生的权重
student.setWeight(student.getWeight() / 2);
System.out.println("修改后的学生" + student);
//将集合再次写到文件中
Student.writeFile(studentList);
return student.getName();
}
}
package Io;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
public class Student {
//姓名
private String name;
// 性别
private String sex;
// 年龄
private int age;
// 权重
private double weight;
public Student(String name, String sex, int age, double weight) {
this.name = name;
this.sex = sex;
this.age = age;
this.weight = weight;
}
//写入文件方法
public static void writeFile(List<Student> list) throws IOException {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("attrs\\file\\ZipStreamDemo\\网络爬虫-姓名爬取.txt"));
for (Student student : list) {
bufferedWriter.write(student.getFileDate());
bufferedWriter.newLine();
}
bufferedWriter.close();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Double.compare(weight, student.weight) == 0 && Objects.equals(name, student.name) && Objects.equals(sex, student.sex);
}
@Override
public int hashCode() {
return Objects.hash(name, sex, age, weight);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", weight=" + weight +
'}';
}
public String getFileDate() {
return name + "-" + sex + "-" + age + "-" + weight;
}
}
← File的概述和构造方法 多线程 →