IO
概述
IO: 存储和读取数据的解决方案
作用: 用于读写文件中的数据(可以读写文件, 或网络中的数据)
IO流的分类
按流的方向: 输入流, 输出流
按操作文件类型:
字节流: 可以操作所有类型的文件
字符流: 只能操作纯文本文件
纯文本文件: windows自带的记事本打开能读懂(txt md xml lrc是) word和excel都不是
IO流体系
FileOutputStream
FileOutputStream基本使用
操作本地文件的字节输出流, 可以把程序中的数据写道本地文件中
java
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写数据的三种方式
java
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); // c
byte[] arr = {97,98,99,100,101};
// fos.write(arr); // abcde
// 第二个参数位起始索引, 第三个参数为写入个数
fos.write(arr, 1, 2); // bc
fos.close();
}
}
两个小问题
1.换行
windows: \r\n
linux: \n
mac: \r
2.不覆盖, 续写
想要续写, 打开续写开关即可
java
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();
}
}
程序输出:
java
guanjunyanshidashazi
666guanjunyanshidashazi
666
FileInputStream
基本使用
操作本地文件的字节输入流, 可以把本地文件中的数据读取到程序中来
书写步骤:
- 创建字节输入流对象
- 读数据
- 释放资源
java
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); // a
int r2 = fis.read();
System.out.println((char)r2); // b
int r3 = fis.read();
System.out.println((char)r3); // c
int r4 = fis.read();
System.out.println((char)r4); // d
int r5 = fis.read();
System.out.println(r5); // -1
// 释放资源
fis.close();
}
}
书写细节
创建字节输入流对象
细节:如果文件不存在,就直接报错。
读取数据
细节1: 一次读一个字节,读出来的是数据在ASCII上对应的数字
细节2:读到文件末尾了,read方法返回-1。
释放资源
细节:每次使用完流必须要释放资源。
循环读取
注意: 每调用一次read()方法, 读取数据的指针就会后移, 因此不要多次调用read()函数
java
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();
}
}
练习
文件拷贝
java
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)
java
package IOTest2;
import java.io.FileInputStream;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
// 一次读取多个数据
// 文件中的数据是abcde
FileInputStream fis = new FileInputStream("IO\\a.txt");
// 一次读两个数据
byte[] bytes = new byte[2];
int len1 = fis.read(bytes);
System.out.println(len1); // 2
String str1 = new String(bytes);
System.out.println(str1); // ab
int len2 = fis.read(bytes);
System.out.println(len2); // 2
String str2 = new String(bytes);
System.out.println(str2); // cd
int len3 = fis.read(bytes);
System.out.println(len3); // 1
String str3 = new String(bytes);
System.out.println(str3); // ed
int len4 = fis.read(bytes);
System.out.println(len4); // -1
String 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的另一种构造方法:
java
package IOTest2;
import java.io.FileInputStream;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
// 一次读取多个数据
// 文件中的数据是abcde
FileInputStream fis = new FileInputStream("IO\\a.txt");
// 一次读两个数据
byte[] bytes = new byte[2];
int len1 = fis.read(bytes);
System.out.println(len1); // 2
String str1 = new String(bytes, 0, len1);
System.out.println(str1); // ab
int len2 = fis.read(bytes);
System.out.println(len2); // 2
String str2 = new String(bytes, 0, len2);
System.out.println(str2); // cd
int len3 = fis.read(bytes);
System.out.println(len3); // 1
String str3 = new String(bytes, 0, len3);
System.out.println(str3); // e
int len4 = fis.read(bytes);
System.out.println(len4); // -1
// String str4 = new String(bytes, 0, len4);
// System.out.println(str4); // 这两行会报错
}
}
表示从byte数组的0位置转len个字符为字符串
文件拷贝改进
java
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); // 2
fos.close();
fis.close();
}
}
速度显著提升
异常处理
finally
在try...catch语句的下面可以加finally, 其中的代码一定会执行, 除非JVM退出
JDK9之后的捕获异常的代码:
java
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.编码解码时使用同一个码表,同一个编码方式
字节流读取中文会乱码, 但是拷贝是不会乱码的
编码和解码
编码的方法
解码的方法
java
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-8
byte[] 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方法的细节
java
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方法的细节
java
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.释放资源
细节: 每次使用完流之后都要释放资源
java
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
拷贝一个文件夹, 考虑子文件夹
java
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(异或应用)
为了保证文件的安全性,就需要对原始文件进行加密存储,再使用的时候再对其进行解密处理。
加密原理:
对原始文件中的每一个字节数据进行更改,然后将更改以后的数据存储到新的文件中
解密原理:
读取加密之后的文件,按照加密的规则反向操作,变成原始文件。
java
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->c
FileInputStream 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
java
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的缓冲区提高性能
常用方法
对象还是基本流, 只不过借助缓冲流速度会更快
练习
利用字节缓冲流拷贝文件
一次操作一个字节:
cpp
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();
}
}
一次操作多个字节:
java
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的缓冲区提高性能
构造方法
特有方法
方法底层会先判断是什么操作系统, 在换行
字符缓冲输入流代码:
java
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();
}
}
字符缓冲输出流:
java
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 拷贝文件
四种方式拷贝文件,并统计各自用时
- 字节流的基本流:一次读写一个字节
- 字节流的基本流:一次读写一个字节数组
- 字节缓冲流:一次读写一个字节
- 字节缓冲流:一次读写一个字节数组
java
package Practice3;
import java.io.*;
public class Demo1 {
public static void main(String[] args) throws IOException {
/*
- 字节流的基本流:一次读写一个字节
- 字节流的基本流:一次读写一个字节数组
- 字节缓冲流:一次读写一个字节
- 字节缓冲流:一次读写一个字节数组
* */
// byteStreamCopy(); // 1039
// bytesStreamCopy(); // 7
// byteBufferedCopy(); // 21
bytesBufferedCopy(); // 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
排序文件中的出师表
每句话前面有序号
java
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>() {
@Override
public 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次,欢迎您注册会员后继续使用~
java
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"));
// 读取文件中的使用次数 初始为0
String 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
java
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-8
FileReader 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
java
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这个接口没有抽象方法, 被称为标记型接口
java
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();
}
}
反序列化流
可以把序列化到本地的文件中的对象, 读取到程序中来
java
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就会根据这个类的对象的各种属性值计算出一个序列号, 可以理解为版本号, 在我们修改类的时候, 版本号就会发生变化, 导致数据无法读取
解决方案: 在类中固定版本号
java
private static final long serialVersionUID = 1L;
2.当我们不想把某个属性值序列化的时候 可以加关键字transient, 此时的标准JavaBean类
java
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:特有的写出方法可以实现自动刷新,自动换行打印一次数据 =写出 + 行 + 刷新构造方法
构造方法
成员方法
java
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();
}
}
字符打印流
构造方法
成员方法
java
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();
}
}
打印流和打印语句的联系
java
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按照层级拷贝到本地另一个文件夹中
java
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对象放到压缩包中
压缩单个文件
java
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();
}
}
压缩文件夹
Java
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.txt
zos.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
- 在类中导包使用
常见方法
java
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包
Java
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.txt
File 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
java
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包, 更改了部分数据
java
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(只显示名字)
java
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
java
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 - girl
ArrayList<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); // 7033
System.out.println(countGirl); // 2697
}
}
版本3(独立完成)
需求:
一个文件里面存储了班级同学的姓名,每一个姓名占一行要求通过程序实现随机点名器。
第三次必定是张三同学
运行效果:
- 第一次运行程序:随机同学姓名1
- 第二次运行程序:随机同学姓名2
- 第三次运行程序:张三
java
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
- 让用户键盘录入用户名和密码
- 比较用户录入的和正确的用户名密码是否一致
- 如果一致则打印登陆成功
- 如果不一致则打印登陆失败
java
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=123
br.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类基本用法
java
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);
}
}
}
写出数据
java
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();
}
}
读数据
java
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}
}
}