用于读写文件的“流”
流的分类:
- 按文件类型:
- 字节流:可以操作所有类型的文件
- 字符流:只能操作纯文本文件
- 按流向:
- 输出流
- 输入流
流的结构:
java.io
│
├── 字节流
│ ├── InputStream 字节输入流
│ │ ├── FileInputStream
│ │ ├── FilterInputStream
│ │ │ ├── BufferedInputStream 缓冲字节输入流
│ │ │ └── DataInputStream
│ │ └── ObjectInputStream 反序列化流
│ └── OutputStream 字节输出流
│ ├── FileOutputStream
│ ├── FilterOutputStream
│ │ ├── BufferedOutputStream 缓冲字节输出流
│ │ └── DataOutputStream
│ └── ObjectOutputStream 序列化流
│
└── 字符流
├── Reader 字符输入流
│ ├── FileReader
│ ├── FilterReader
│ │ ├── BufferedReader 缓冲字符输入流
│ │ └── LineNumberReader
│ └── InputStreamReader
└── Writer 字符输出流
├── FileWriter
├── FilterWriter
│ ├── BufferedWriter 缓冲字符输出流
│ └── PrintWriter
└── OutputStreamWriter
1.字节流:
1.1 文件字节输出流:FileOutputStream
创建字节输出流对象:
- 参数可以是字符串路径、File对象
- 文件不存在则会创建新文件,但要保证父级路径正确
- 文件已存在,默认覆盖
- 需要续写,则需要在创建时在第二个参数处填 true。
写入数据:
- 写入参数对应ASCII码
方法名称 | 说明 |
---|---|
void write(int b) | 一次写一个字节数据 |
void write(byte[] b) | 一次写一个字节数组数据 |
void write(byte[] b, int off, int len) | 一次写一个字节数组的部分数据 |
其他类型可以使用方法:对象.getBytes();
换行:
写入换行符:
- windows: \r\n (只写一个JAVA会自己补全)
- Linux: \n
- Mac: \r
释放资源/关流:
- f.close();
1.2 文件字节输入流: FileInputStream
创建字节输入流对象:
- 参数可以是字符串路径、File对象
- 文件不存在则直接报错
读取数据:
- 读出返回值为字符对应ASCII码
- 读到文件末尾,read()返回-1
方法名称 | 说明 |
---|---|
public int read() | 一次读一个字节数据 |
public int read(byte[] buffer) | 一次读一个字节数组数据(会尽可能填满) |
循环读取示例:注意此处临时变量b的必要性
FileInputStream fis =new FileInputStream("src/aaa.txt");
int b;
while ((b=fis.read())!=-1){
System.out.print((char)b);
}
释放资源/关流:
- f.close();
2 字符流
字符流=字节流+字符集
2.1 文件字符输入流:
创建对象:
- FileReader(File file)
- FileReader(String pathname)
细节同上
读取:
- public int read() : 读取单个字符,读到末尾返回-1
- public int read (char[] buffer) : 读取多个数据,读到末尾返回-1
细节:
- 按字节读取,遇到中文,一次读多个字节,读取后解码,返回对应整数
- 释放资源/关流:f.close();
- 返回的是十进制,可用(char)强制类型转换
2.2 文件字符输出流:
构造对象:
FileWriter()同FileOutputStream,有重载的两个参数,第二个参数append为true时续写。
写出:
成员方法 说明 void write(int c) 写出一个字符(整数对应字符) void write(String str) 写出一个字符串 void write(String str, int off, int len) 写出一个字符串的一部分 void write(char[] cbuf) 写出一个字符数组 void write(char[] cbuf, int off, int len) 写出字符数组的一部分
3 综合案例:
3.1 拷贝整个文件夹:
import java.io.*;
public class Main{
public static void main(String[] args) throws IOException {
//拷贝文件源
File src=new File("D:\\My Java Code\\filetest\\aaa");
//拷贝文件目标
File target = new File("D:\\My Java Code\\filetest\\target");
copyFile(src,target);
}
private static void copyFile(File src,File target) throws IOException {
target.mkdirs();//确保目标目录存在。
File[] files=src.listFiles();
for (File file:files){
//如果是文件,拷贝;否则递进入文件夹
if(file.isFile()){
FileInputStream fis=new FileInputStream(file);
FileOutputStream fos=new FileOutputStream(new File(target,file.getName()));
byte[] bytes=new byte[1024];
int len=0;
while((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}else {
copyFile(file,new File(target,file.getName()));
}
}
}
}
4 缓冲流:BufferedStream
缓冲流(Buffered Streams)和普通IO流(Non-buffered Streams)在Java中都是用于输入和输出操作的,通过减少实际的读写操作次数,缓冲流可以显著提高性能。适用于对性能要求较高或数据量较大的场景,如文件复制、大数据处理等。
缓冲流在创建时需要一个基本流对象,并且在使用时提供了与基本流相似的方法。
缓冲区默认大小:8192
字符缓冲流:
- public BufferedReader(Reader r)
- 特有方法:public String readLine() :读取一行数据,读不到返回null。(不会读入换行)
- public BufferedWriter(Writer w)
- 特有方法:public void newLine() :跨平台的换行
- 特有方法:public void flush(): 刷新缓冲区,常与newLine一起使用。
另外,要开启续写,要在对应基本流里写append。
5 转换流
转换流主要有两种类型:InputStreamReader 和 OutputStreamWriter。
高级流,所以构造方法参数为基本流。
转换流可以将一个字节流包装成字符流,或者将一个字符流包装成字节流。这种转换通常用于处理文本数据,如读取文本文件或将数据从网络传输到应用程序。
6 序列流
Java 的序列流(ObjectInputStream 和 ObjectOutputStream)是一种可以将 Java 对象序列化和反序列化的流。
6.1 序列化流 :Object OutputSteam:
条件:
- 只有实现了 Serializable 接口的对象才能被序列化。(一个标记型接口)
- 该类的所有字段都必须是可序列化的。如果一个字段不需要序列化,则需要使用transient 关键字 进行修饰。
构造方法:
也是一个高级流,所以构造方法参数为基本流。
ObjectOutputStream(OutputStream out)
写出方法:
oos.writeObject (Object obj)
6.2 反序列化流:ObjectInputStream
构造方法:
ObjectInputStream(InputStream in)
读入方法:
oos.readObject(Object obj)返回一个Object 对象,需要再次进行强转。
序列版本号:serialVersionUID:
序列化一个对象后,更改javabean类,就会改变它的版本号。再次反序列化时,版本号不一致就会报错。
解决:手动添加serialVersionUID。
private static final long serialVersionUOD=65465156L;
7 打印流 PrintStream 、PrintWrite:
- 打印流只输出目的地,不操作数据源
- 特有的写出方法可以让数据原样写出
- 特有的写出方法可以自动刷新、自动换行
8 IO工具包
8.1:commons.io
Apache Commons IO是一个Apache的jar包,使用前需要导入到lib中。
主要功能:
- 工具类:提供静态方法来执行常见任务,如
IOUtils
、FileUtils
、FilenameUtils
和FileSystemUtils
。 - 输入和输出:提供
InputStream
和OutputStream
的实现,以及Reader
和Writer
的实现。 - 过滤器:提供多种文件过滤器实现,定义了
IOFileFilter
接口,同时继承了FileFilter
和FilenameFilter
接口。 - 比较器:提供用于文件比较的多种
java.util.Comparator
实现。 - 文件监控:提供组件用于监控文件系统事件(目录和文件的创建、更新和删除事件)
8.2:Hutool工具类库
更加全面、边查边用。
9 综合案例:网络爬虫
import java.io.*;
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 Main{
public static void main(String[] args) throws IOException {
//记录网址
String familyNameNet="https://hanyu.baidu.com/shici/detail?pid=0b2f26d4c0ddb3ee693fdb1137ee1b0d";
//爬取数据
String familyNameTempStr=webCrawler(familyNameNet);
//清洗数据
ArrayList<String> familyNameTempList =getData(familyNameTempStr,"(.{3}[^tem\"\\>])(,|。)");
//提取单个姓氏
ArrayList<String>familyNameList=new ArrayList<>();
for (String temp:familyNameTempList){
for (int i = 0; i < temp.length(); i++) {
char c=temp.charAt(i);
familyNameList.add(c+"");
}
}
//得到信息 姓名-性别-年龄
ArrayList<String>infos=getInfos(familyNameList,30);
//写入文档
FileWriter fileWriter=new FileWriter("D:\\My Java Code\\filetest\\name.txt");
BufferedWriter bufferedWriter=new BufferedWriter(fileWriter);
for (String info:infos){
bufferedWriter.write(info);
bufferedWriter.newLine();
}
bufferedWriter.close();
fileWriter.close();
}
private static ArrayList<String> getInfos(ArrayList<String> familyNameList, int num) {
ArrayList<String>infos=new ArrayList<>();
Random random=new Random();
//随机得到不重复的姓名
HashSet<String> hs=new HashSet<>();
while (true){
if (hs.size()==num)
break;
Collections.shuffle(familyNameList);
hs.add(familyNameList.get(0)+"建国");
}
//随机性别
String[] gender={"男","女"};
//组合
for (String name:hs) {
int genderIndex=random.nextInt(2);
//随机年龄
Integer age =random.nextInt(10)+18;
String ageStr=age.toString();
infos.add(name +"-"+gender[genderIndex]+"-"+ageStr);
}
return infos;
}
private static ArrayList<String> getData(String str, String regex) {
ArrayList<String> list=new ArrayList<>();
Pattern pattern= Pattern.compile(regex);
Matcher matcher = pattern.matcher(str);
while (matcher.find()){
String group=matcher.group(1);
list.add(group);
//System.out.println(group);
}
return list;
}
private static String webCrawler(String net) throws IOException {
//定义sb拼接爬取到的数据
StringBuilder sb =new StringBuilder();
//创建URL对象
URL url=new URL(net);
//链接这个网址
URLConnection connection = url.openConnection();
//读取数据
InputStreamReader isr=new InputStreamReader(connection.getInputStream());
int ch;
while ((ch=isr.read())!=-1){
sb.append((char) ch);
}
isr.close();
return sb.toString();
}
}
得到:info.txt
池建国-女-25
文建国-男-25
彭建国-男-26
巫建国-女-19
扶建国-女-25
宣建国-女-25
鲍建国-男-27
满建国-女-20
戴建国-男-18
卓建国-女-20
樊建国-男-25
俞建国-女-20
裘建国-女-19
桂建国-女-24
汲建国-女-25
凤建国-女-20
融建国-男-18
年建国-女-22
娄建国-女-25
易建国-男-20
诸建国-女-22
木建国-女-23
鞠建国-女-21
云建国-女-25
禹建国-女-20
丘建国-女-18
束建国-男-18
郜建国-女-24
丌建国-男-26
茅建国-女-18