文章目录
- 文件IO
- 1. File
- 2. IO流
- 2.1 字符流
- 2.1.1 Reader
- 2.1.2 Writer
- 2.2 字节流
- 2.2.1 InputStream
- 2.2.2 FileInputStream
- 2.2.3 利用Scanner进行字符读取
- 2.2.4 OutputStream
文件IO
I
: Input, 从硬盘往内存读数据
O
: Output, 从内存往硬盘输出数据
1. File
Java 中通过 java.io.File
类来对一个文件(包括目录)进行抽象的描述。注意,有 File 对象,并不代表真实存在该文件。
构造方法
方法 | 说明 |
---|---|
File(File parent, String child) | 根据父目录 + 孩子文件路径,创建一个新的 File 实例 |
File(String pathname) | 根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径 |
File(String parent, String child) | 根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示 |
方法
修饰符及返回值类型 | 方法名称 | 说明 |
---|---|---|
String | getParent() | 返回 File 对象的父目录文件路径 |
String | getName() | 返回 FIle 对象的纯文件名称 |
String | getPath() | 返回 File 对象的文件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
boolean | exists() | 判断 File 对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
boolean | createNewFile() | 根据 File 对象,自动创建一个空文件。成功创建后返回 true |
boolean | delete() | 根据 File 对象,删除该文件。成功删除后返回 true |
void | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作会到JVM 运行结束时才会进行 |
String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象表示 |
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操作 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
代码示例
public static void main(String[] args) throws IOException {
File file1 = new File("D/text.txt");
System.out.println(file1.getName());
System.out.println(file1.getParent());
System.out.println(file1.getPath());
System.out.println(file1.getAbsolutePath());
System.out.println(file1.getCanonicalPath());
System.out.println("===========================");
File file2 = new File("./text.txt");
System.out.println(file2.getName());
System.out.println(file2.getParent());
System.out.println(file2.getPath());
System.out.println(file2.getAbsolutePath());
System.out.println(file2.getCanonicalPath());
}
输出
text.txt
D:\
D:\text.txt
D:\text.txt
D:\text.txt
===========================
.
.\text.txt
D:\JAVA\java\system_code.\text.txt
D:\JAVA\java\system_code\text.txt
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
System.out.println(file.exists());
System.out.println(file.isFile());
System.out.println(file.isDirectory());
//创建文件
file.createNewFile();//若当前文件已经存在, 也不会重新创建一个
System.out.println(file.exists());
System.out.println(file.isFile());
System.out.println(file.isDirectory());
}
输出
true
true
false
===========================
true
true
false
public static void main(String[] args) throws IOException {
File file = new File("./text.txt");
System.out.println(file.exists());
file.delete();
System.out.println("文件已删除");
System.out.println(file.exists());
}
输出
false
文件已删除
false
public static void main(String[] args) throws IOException {
File file = new File("text.txt");
System.out.println(file.exists());
//等到程序结束前再删除
file.deleteOnExit();
System.out.println(file.exists());
}
输出
true
true
public static void main(String[] args) {
File file = new File("./testDir");
file.mkdir();
System.out.println(file.exists());
System.out.println(file.isDirectory());
File file1 = new File("./testDir/aaa/bbb/ccc");
file1.mkdirs();
System.out.println(file1.exists());
System.out.println(file1.isDirectory());
}
输出
true
true
true
true
public static void main(String[] args) {
File file = new File("./test.txt");
File file1 = new File("./testRename.txt");
System.out.println("file是否存在: " + file.exists());
System.out.println("file1是否存在: " + file1.exists());
System.out.println("renameTo执行是否成功: "+file.renameTo(file1));
System.out.println("file是否存在: " + file.exists());
System.out.println("file1是否存在: " + file1.exists());
}
输出
file是否存在: true
file1是否存在: false
renameTo执行是否成功: true
file是否存在: false
file1是否存在: true
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
File file1 = new File("./src/test2.txt");
System.out.println("file是否存在: " + file.exists());
System.out.println("file1是否存在: " + file1.exists());
System.out.println("file的路径" + file.getCanonicalPath());
System.out.println("renameTo执行是否成功: "+file.renameTo(file1));
System.out.println("file是否存在: " + file.exists());
System.out.println("file1是否存在: " + file1.exists());
System.out.println("file1的路径" + file1.getCanonicalPath());
}
输出
file是否存在: true
file1是否存在: false
file的路径D:\JAVA\java\system_code\test.txt
renameTo执行是否成功: true
file是否存在: false
file1是否存在: true
file1的路径D:\JAVA\java\system_code\src\test2.txt
2. IO流
将硬盘中的数据比喻为池中的水
往池中加水, 相当于数据从内存往硬盘中流动, 称作输出流
从池中放水, 相当于数据从硬盘往内存中流, 称作输入流
IO流可以分为 字节流 , 字符流 两大类
这些类都有各自不同的特性, 但是使用方法还是类似的:
-
构造方法: 打开文件
-
close方法: 关闭文件
close方法一定要执行到, 如果使用完不关闭, 就会导致文件资源泄露 (尤其是在7*24小时运转的服务器上) 进而导致文件都打不开, 使服务器宕机.
如何保证close方法一定能执行的到呢?
-
使用try-finally, 将close写入finally代码块
-
使用try with resources创建流对象. 例如
try (Reader reader = new FileReader("D:/test.txt"); Reader reader1 = new FileReader(""); Reader reader2 = new FileReader(""); ...) { //代码 }
只要try代码块执行结束, 就会自动调用close方法.
-
-
如果衍生自InputStream或者Reader, 就可以使用read方法读文件
-
如果衍生自OutputStream或者Writer, 就可以使用write方法写文件
2.1 字符流
字符流, 顾名思义, 以操作字符为单位, 针对文本文件
2.1.1 Reader
read
方法
方法 | 作用 | 返回值 |
---|---|---|
int read() | 一次读一个字符 | 以int类型返回读到的字符. 如果返回值为-1, 表示文件已经读完, 或者读到EOF. |
int read(char cbuf[]) | 一次读若干个字符, 并把读到的内容填充到cbuf[]中 | 返回实际读到的字符个数 |
int read(char cbuf[], int off, int len) | 从第off个字符开始, 将字符读入数组, 一共读len个 | 读取的字符数,如果已到达流的结尾,则返回-1 |
代码案例
public static void main(String[] args) throws IOException {
try (Reader reader = new FileReader("D:/test.txt")) {
while (true) {
char[] buf = new char[1024];
int n = reader.read(buf);
if(n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.print(buf[i] + " ");
}
}
}
}
2.1.2 Writer
write
方法
方法 | 说明 |
---|---|
void write(int c) | 写入单个字符。 |
void write(char cbuf[]) | 写入字符数组。 |
void write(char cbuf[], int off, int len) | 写入字符数组的一部分。cbuf -字符数组off -开始写入字符的偏移量len -要写入的字符数 |
void write(String str) | 写入字符串 |
void write(String str, int off, int len) | 写入字符串的一部分。str -字符串off -开始写入字符的偏移量len -写入字符的数目 |
代码示例
public static void main(String[] args) throws IOException {
try(Writer writer = new FileWriter("D:/test.txt")) {
writer.write("hello java");
}
}
这里可能就会有疑问了, 文件里面之前的内容呢?
输出流对象(无论字节流还是字符流), 再打开文件之后, 清空文件内容.
如果不想被清空, 那就使用追加写方式打开文件.
Writer writer = new FileWriter("D:/test.txt", true)
在构造方法上, 写一个true
, 便可
public static void main(String[] args) throws IOException {
try(Writer writer = new FileWriter("D:/test.txt", true)) {
writer.write("hello world");
}
}
2.2 字节流
字节流, 顾名思义, 以操作字节为单位, 针对二进制文件
2.2.1 InputStream
方法
返回值类型 | 方法 | 说明 |
---|---|---|
int | read() | 读取一个字节的数据,返回 -1 代表已经完全读完了 |
int | read(byte[] b) | 最多读取 b.length 字节的数据到 b 中,返回实际读到的数; -1 代表以及读完了 |
int | read(byte[] b, int off, int len) | 最多读取 len - off 字节的数据到 b 中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了 |
void | close() | 关闭字节流 |
说明
InputStream 只是一个抽象类,要使用还需要具体的实现类。关于 InputStream 的实现类有很多,基本可以认为不同的输入设备都可以对应一个 InputStream 类,我们现在只关心从文件中读取,所以使用FileInputStream
2.2.2 FileInputStream
构造方法
方法 | 说明 |
---|---|
FileInputStream(File file) | 利用 File 构造文件输入流 |
FileInputStream(String name) | 利用文件路径构造文件输入流 |
代码示例
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("D:/test.txt")) {
while (true) {
byte[] bytes = new byte[1024];
int n = inputStream.read(bytes);
if (n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("%x ", bytes[i]);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
读到的字符, 我们更期望的是把他转化成字符串, 更直观. 所以我们可以借助一些额外的工具类, 就可以完成从字节/字符到字符串的转换.
一. 是使用String的构造方法. 但这种方式不够优雅.
String s = new String(bytes, 0, n, "utf8");
System.out.println(s);
二. 是Scanner
.
2.2.3 利用Scanner进行字符读取
我们来看一下Scanner
的构造方法
其中, 构造方法的参数中有InputStream
, 也就是说我们可以往里面传字节流. 实际上, 我们经常写的new Scanner(System.in)
中的in
也是一个字节流
其实在操作系统中, "文件"是一个广义的概念
System.in
是一个特殊的文件, 对应到"标准输入"- 硬盘上的文件, 也是文件
- 网卡(socket), 也是文件
- …
Scanner
都是一视同仁的, 只是把当前读到的字节数据进行转换, 不关心这个数据的来源.
那么上述读取文件的代码就可以这样写了
public static void main(String[] args) throws IOException {
try(InputStream inputStream = new FileInputStream("D:/test.txt")) {
Scanner scanner = new Scanner(inputStream);
//Scanner scanner = new Scanner(inputStream, "utf8");
//从文件中读取
while (scanner.hasNext()) {
String s = scanner.next();
System.out.print(s + " ");
}
}
}
但是
Scanner
只适合读取文本文件, 不适合读取二进制文件
2.2.4 OutputStream
方法
返回值类型 | 方法 | 说明 |
---|---|---|
void | write(int b) | 将指定字节写入此输出流。 |
void | write(byte[] b) | 将 b 这个字节数组中的数据全部写入 os 中 |
void | write(byte b[], int off, int len) | 将 b 这个字符数组中从 off 开始的数据写入 os 中,一共写 len 个 |
void | close() | 关闭字节流 |
void | flush() | 刷新此输出流并强制写出任何缓冲的输出字节。 |
我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内存的一个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域一般称为缓冲区。但造成一个结果,就是我们写的数据,很可能会遗留一部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。
OutputStream 同样只是一个抽象类,要使用还需要具体的实现类。我们现在还是只关心写入文件中,
所以使用 FileOutputStream
OutputStream 的使用方法和Writer完全一样, 只不过不能写入字符, 字符串.