Java基础之IO流
2023-05-03
参考JAVAschool基础教程的IO流学习:http://www.51gjie.com/java/686.html
以及chatgpt
概念
IO流(stream)
流的源端和目的端可简单地看成是字节的生产者和消费者,对输入流,可不必关心它的源端是什么,只要简单地从流中读数据,而对输出流,也可不知道它的目的端,只是简单地往流中写数据。
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
分类
- 数据类型:字符、字节
- 流向:输入、输出
- 方法类型:结点流、过滤器流(buffer)、数据流(也是过滤器流)…
有一些类名容易搞混,比如:InputStreamReader和FileInputStream,两者都含Input
和Stream
,但是前者是继承Reader类的字符流,后者是继承InputStream类的字节流。因此,要识别类是属于字符or字节流的话,看【后缀】即可:
- Reader后缀:输入字符流
- Writer后缀:输出字符流
- InputStream后缀:输入字节流
- OutputStream后缀:输出字节流
按用法分类:
(1) 按数据来源(去向)使用
是文件: FileInputStream, FileOutputStream, FileReader, FileWriter
是byte[]:ByteArrayInputStream, ByteArrayOutputStream
是Char[]: CharArrayReader, CharArrayWriter
是String: StringBufferInputStream, StringReader, StringWriter
网络数据流:InputStream, OutputStream, Reader, Writer
(2) 按是否格式化输出使用
要格式化输出:PrintStream, PrintWriter
(3) 按是否要缓冲使用
要缓冲:BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter。
(4) 按数据格式使用
二进制格式(只要不能确定是纯文本的): InputStream, OutputStream及其所有带Stream结束的子类
纯文本格式(含纯英文与汉字或其他编码方式);Reader, Writer及其所有带Reader, Writer的子类
(5) 按输入输出使用
输入:Reader, InputStream类型的子类;输出:Writer, OutputStream类型的子类
(6) 特殊需要
从Stream到Reader,Writer的转换类:InputStreamReader, OutputStreamWriter
对象输入输出:ObjectInputStream, ObjectOutputStream
进程间通信:PipeInputStream, PipeOutputStream, PipeReader, PipeWriter
合并输入:SequenceInputStream
更特殊的需要:PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader
(7) 决定使用哪个类以及它的构造进程的一般准则如下(不考虑特殊需要):
考虑最原始的数据格式是什么:是否为文本?是输入还是输出?是否需要转换流:InputStreamReader, OutputStreamWriter?数据来源(去向)是什么:文件?内存?网络?是否要缓冲:bufferedReader (特别注明:一定要注意的是readLine()是否有定义,有什么比read, write更特殊的输入或输出方法)是否要格式化输出:print。
http://www.51gjie.com/java/686.html
字符流和字节流的转换
转换流:是两者之间的桥梁
- 字节到字符的桥梁:InputStreamReader
- 字符到字节的桥梁:OutputStreamWriter
File类
java.io.File
类实现对文件进行基本操作。常用方法如下:
- 创建
- createNewFile()
- mkdir()
- mkdirs()
- 获取信息
- getName()
- getParent()
- getParentFile()
- getPath()
- lastModified()
- exists()
- isFile()
- isDirectory()
- length()
- listFiles()
- listFiles(FileFilter filter)
- list()
- list(FilenameFilter filter)
- 修改
- delete()
- renameTo(File dest)
其中,listFiles返回的是File[]
类型,list方法返回的是String[]
类型
InputStream类
是字节输入流的所有类的超类。常见子类:
- FileInputStream
- StringBufferInputStream
- BufferedInputStream
以下仅介绍FileInputStream
的常见用法。需要注意的是,因为这些字类都实现了InputStream
定义的基本方法,故基本的使用方法是差不多的。但是各自有自己的特色方法,我们可以按需使用。
FileInputStream
用法:读取文件内容,用byte[]
存储。主要方法如下:
FileInputStream(String fileName) // 构造函数
int read() // 从输入流中读取一个字节的数据(输出时可用(char)转换为字符)。如果已经到达文件的末尾,则返回-1
int read(byte[] b) // 从输入流中读取若干个字节的数据,并将其存储在缓冲区`b`中。返回实际读取的字节数。
int read(byte[] b, int offset, int len) // 从输入流中读取最多`len`个字节的数据,并将其存储在缓冲区`b`中,从偏移量`off`开始存储。返回实际读取的字节数。
void close() // 关闭并释放流
使用实例:
public static void fileRead() {
try {
File file = new File("IOStreamLearning/file.txt");
// 如果不确定文件路径是否写对,可以在失败时输出文件路径进行调整
if (!file.exists()) {
throw new FileNotFoundException("File not found: " + file.getAbsolutePath());
}
// 1.使用read()方法
FileInputStream fis1 = new FileInputStream(file);
// 也可以直接将文件路径写在参数里
// FileInputStream fis1 = new FileInputStream("IOStreamLearning/file.txt");
int data;
// 读取结果转换为int赋值给data
while ( (data = fis1.read()) != -1) {
// System.out.print((char)data);
}
fis1.close();
// 2.使用read(byte[] b)方法
FileInputStream fis2 = new FileInputStream(file);
byte[] b2 = new byte[2];
int byteArray = fis2.read(b2);
// 依次读取b2所定义的字节数量,直到读取结束
while (-1 != byteArray) {
// System.out.print(new String(b2)); // byte[]->String用的是new String(byte[])哦!
byteArray = fis2.read(b2);
}
fis2.close();
// 3.使用read(byte[], int offset, int len)
FileInputStream fis3 = new FileInputStream(file);
byte[] b3 = new byte[1024];
while (-1 != fis3.read(b3, 0, 1024)) {
System.out.println(new String(b3));
}
fis3.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
OutputStream类
是字节输出流的所有类的超类。常见子类:
- BufferedOutputStream
- FileOutputStream
以下仅介绍FileOutputStream
的常见用法。
FileOutputStream
用于将数据写入文件,常用方法:
void write(int b) // 写入一个字节到输出流
void write(byte[] b) // 写入一个字节数组
void write(byte[]b, int offset, int len)
void flush() // 刷新输出流,将缓冲区的内容写入目标文件。虽然大部分情况下,程序正常结束时数据都会自动写入文件中,但是保险起见需要在适当的时候及时刷新写入数据
void close()
关于为何第一个方法的参数是int
类型:
虽然这个方法接受一个int作为参数,但它实际上会写入一个无符号字节。Java没有无符号字节数据类型,所以这里要使用int来代替。
应用实例:
public static void writeFile() {
try {
// 1.使用write(int b)方法
// FileOutputStream第二个参数为可选,表示是否追加写。默认为false
FileOutputStream fos1 = new FileOutputStream("IOStreamLearning/file1.txt", true);
byte b = 66;
fos1.write(b);
fos1.flush();
fos1.close();
// 2.使用write(byte[])方法
FileOutputStream fos2 = new FileOutputStream("IOStreamLearning/file2.txt");
String content = "hello";
fos2.write(content.getBytes()); // 通过getBytes方法将要写入的String转换为byte[]
fos2.flush();
fos2.close();
// 3.使用write(byte[], int offset, int len)方法
FileOutputStream fos3 = new FileOutputStream("IOStreamLearning/file3.txt");
String content1 = "bye";
byte[] b3 = content1.getBytes("UTF-8"); // 将String对象转换为UTF-8编码的byte数组
// 如果要写入全部,len参数取数组的长度。注意len参数不能指定为超出实际数组的长度
fos3.write(b3, 0, b3.length);
fos3.flush();
fos3.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Reader类
是字符输入流的所有类的超类。常见子类:
- BufferedReader
- InputStreamReader
- FileReader
三者的区别:
BufferedReader
是一个缓冲字符输入流,它可以减少了读取文件的次数,提高读取文件的效率。InputStreamReader
是用来读取字节流并将其转换为字符流的类,可以将任何字节流转换为字符流。更重要的是:InputStreamReader
可以接收第二个参数,指定字节流使用的编码格式。FileReader继承自InputStreamReader
类,读取文本文件时可以直接使用该类。但是,FileReader
是使用系统默认的字符编码读取文件(无法指定的编码),如果文件的编码不是系统默认的编码,那么就不能正确地读取文件内容(乱码)。
需要注意的是:这些类都是用于读取文本文件的,如果要读取二进制文件(例如图片、音频、视频等),则应该使用InputStream
类及其子类,而不是使用这些类。
下面介绍仅BufferedReader
的用法。
BufferedReader
在开始之前,先搞清楚一件事:前面说到,InputStreamReader可以通过第二个参数指定读取的编码,但是BufferedReader并不能这样做。那么如果实现缓存+指定编码的效果呢?答案是:将InputStreamReader对象传递给BufferedReader构造函数(下面会给出例子)。
BufferedReader的常用方法:
int read() // 与InputStream类似
int read(byte[] b) // 与InputStream类似
String readLine() // 读取一行文本数据,如到达文件结尾返回null
close() // 与InputStream类似
具体实例:
public static void readFile() {
String filePath = "IOStreamLearning/file.txt";
try {
// 1.使用FileReader构建BufferedReader对象
BufferedReader br1 = new BufferedReader(new FileReader(filePath));
String line;
while(null != (line = br1.readLine())) {
// System.out.println(line);
}
// 2.使用InputStreamReader构建BufferedReader对象
// FileReader无法指定读取编码,但是可以借助FileInputStream+InputStreamReader来指定编码
BufferedReader br2 = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8"));
while(null != (line = br2.readLine())) {
System.out.println(line);
}
} catch(IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
}
BufferedReader的构造函数不能直接使用文件路径作为参数,而是必须使用字符输入流传递进去(例如借助桥梁InputStreamReader
或它的子类FileReader
)。即,需要使用这些方法作为字节流和字符流的连接桥梁。
分析方法2:BufferedReader br2 = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8"));
。FileInputStream提供文件的字节流,InputStreamReader将字节流转换为字符流,BufferedReader接收字符流生成对象。此外,读取的编码集在InputStreamReader的第二个参数指定。如果不指定编码方式,则会使用默认的编码方式(据说Windows下默认编码是GBK,Linux下默认编码是UTF-8)。
Writer类
是字符输出流的所有类的超类,常用子类:
- BufferedWriter
- OutputStreamWriter,子类有FileWriter。前者可以设置编码集
以下仅介绍OutputStreamWriter
的常见用法。
OutputStreamWriter
将字符流转换为字节流。常见用法:
void write(int c) // 写入单个字符
void write(char[] c, int offset, int len) // 写入字符数组
void write(String str, int offset, int len) // 写入字符串
void flush()
void close()
具体实例:
public static void writeFile() {
try{
String filePath = "IOStreamLearning/file4.txt";
// 1.使用write(int c)方法
OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream(filePath), "UTF-8");
int c = 66;
osw1.write(c);
osw1.flush();
osw1.close();
// 2。使用write(char[] c, int offset, int len)方法
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(filePath), "UTF-8");
char[] c2 = new char[3];
c2[0] = 'a';
c2[1] = 'a';
osw2.write(c2, 0, c2.length);
osw2.flush();
osw2.close();
// 2。使用write(String str, int offset, int len)方法
OutputStreamWriter osw3 = new OutputStreamWriter(new FileOutputStream(filePath), "UTF-8");
String content = "hello world";
osw3.write(content, 0, content.length());
osw3.flush();
osw3.close();
} catch (IOException e) {
e.printStackTrace();
}
}