文件 IO
1 文件 IO
硬盘(磁盘)上的文件
硬盘 != 磁盘
磁带是一种用于记录声音、图像、数字或其他信号的载有磁层的带状材料
后来就有了 mp3 --> mp4 --> 手机
磁盘,是外存的一种
最开始是软盘(比磁带出现的更早,也是一种磁盘)
后来出现了硬盘(机械硬盘),也是通过磁性介质来存储二进制数据,过去资料硬盘也称为磁盘
但 2017 年之后 ssd 硬盘(固态硬盘)兴起了,内部完全是集成电路,和磁性介质是没关系的
现在用的这些计算机基本上都是 ssd 为主,机械硬盘越来越少了
ssd 相比于机械硬盘最大的优势就是读写速度快了很多,10几倍左右
一般 ssd 每秒读写 3-4GB,好一点能达到 6-7GB,机械硬盘大概就是 200M
长江存储、海康威视等国产厂商兴起,就把 ssd 价格打下来了
不仅家用电器,包括公司的服务器里面也是使用 ssd 越来越多了
计算机中存储数据设备:
1.CPU(寄存器、缓存) 存储数据是最快的 存储空间最小 最贵 丢失数据
2.内存 其次 其次 其次 丢失
3.硬盘 最慢 最大 最便宜 不丢失
此处谈到的文件是硬盘上的文件,很多的特性都和硬盘特性相关
对于计算机来说 “文件” 是个广义的概念,硬盘上的普通文件,硬盘上的目录(文件夹),很多的硬件设备也被操作系统抽象成了文件,如键盘、显示器、网卡
机械硬盘不要自行拆开,要求无尘的
2 文件定位
绝对路径、相对路径
这个过程只是访问其中一小部分节点,不是遍历(树上把所有节点访问一遍,不重不漏)
实际开发中更多使用相对路径
正反斜杠:“/ ” “ \”
everything 初装时所谓的遍历也是特别快的,用到 Windows 自带文件系统内置索引
3 文件重要分类
文本文件、二进制文件
cmd 记事本打开:win+R 打开 cmd 输入 notepad
文件拖入记事本即可打开
4 文件系统操作
Java 对于文件操作的 API:使用 File 类来进行
File
属性
构造方法
方法
返回绝对路径
package io;
import java.io.File;
import java.io.IOException;
public class IODemo1 {
public static void main(String[] args) throws IOException {
File f = new File("D:/Fiddler/credits.txt");
//获取属性
System.out.println(f.getParent());
//获取文件名
System.out.println(f.getName());
//获取路径
System.out.println(f.getPath());
//返回绝对路径
System.out.println(f.getAbsolutePath());
//返回修饰过的绝对路径
System.out.println(f.getCanonicalPath());
}
}
返回相对路径
public class IODemo1 {
public static void main(String[] args) throws IOException {
//File f = new File("D:/Fiddler/credits.txt");
File f = new File("./Fiddler/credits.txt");
//获取属性
System.out.println(f.getParent());
//获取文件名
System.out.println(f.getName());
//获取路径
System.out.println(f.getPath());
//返回绝对路径
System.out.println(f.getAbsolutePath());
//返回修饰过的绝对路径
System.out.println(f.getCanonicalPath());
}
}
package io;
import java.io.File;
public class IODemo2 {
public static void main(String[] args) {
File file = new File("./credits.txt");
//判断一下路径是否存在
System.out.println(file.exists());
//判断是不是文件
System.out.println(file.isFile());
//是不是目录
System.out.println(file.isDirectory());
}
}
结果 false,因为当前目录(也就是当前项目)下并没有 credits.txt,因此既不是文件也不是目录
创建文件
package io;
import java.io.File;
import java.io.IOException;
public class IODemo2 {
public static void main(String[] args) throws IOException {
File file = new File("./credits.txt");
//判断一下路径是否存在
System.out.println(file.exists());
//判断是不是文件
System.out.println(file.isFile());
//是不是目录
System.out.println(file.isDirectory());
//创建文件
Boolean ret = file.createNewFile();
System.out.printf("ret = " + ret);
}
}
此时项目目录下就多出了一个文件,和 out 同级,不是在 src 下
查看当前目录
System.out.println(" 当前工作目录: " + System.getProperty("user.dir"));
删除文件
package io;
import java.io.File;
public class IODemo3 {
public static void main(String[] args) {
File file = new File("./credits.txt");
System.out.println(file.delete());
}
}
此时文件消失了
deleteOnExit :退出时再删除
package io;
import java.io.File;
public class IODemo3 {
public static void main(String[] args) throws InterruptedException {
File file = new File("./credits.txt");
file.deleteOnExit();
Thread.sleep(5000);
}
}
有些程序退出了文件还能看到,很有可能是 idea 延迟,此时文件已经没了
退出时再删除也称为临时文件,有些程序就带有临时文件功能
为隐藏文件,需要显示出才能看到
返回目录下所有文件
list() :返回 File 对象代表的⽬录下的所有⽂件名
import java.io.File;
import java.util.Arrays;
public class IODemo4 {
public static void main(String[] args) {
File file = new File(".");
String[] files = file.list();
System.out.println(Arrays.toString(files));
}
}
listFile() :返回 File 对象代表的⽬录下的所有⽂件,以 File 对象表示
package io;
import java.io.File;
import java.util.Arrays;
public class IODemo4 {
public static void main(String[] args) {
File file = new File(".");
File[] files = file.listFiles();
System.out.println(Arrays.toString(files));
}
}
得到的每一个都是 File 对象
创建目录
mkdir() :创建 File 对象代表的⽬录
package io;
import java.io.File;
public class IODemo5 {
public static void main(String[] args) {
File f = new File("./aaa");
System.out.println(f.mkdir());
}
}
mkdirs() :创建 File 对象代表的⽬录,如果必要,会创建中间⽬录
package io;
import java.io.File;
public class IODemo5 {
public static void main(String[] args) {
File f = new File("./aaa/bbb/ccc");
System.out.println(f.mkdirs());
}
}
文件改名
package io;
import java.io.File;
public class IODemo6 {
public static void main(String[] args) {
File src = new File("./test.txt");
System.out.println(src.exists());
File dest = new File("./test2,txt");
src.renameTo(dest);
}
}
renameTo(File dest) 还可以移动⽂件
package io;
import java.io.File;
public class IODemo6 {
public static void main(String[] args) {
File src = new File("./test2.txt");
File dest = new File("./aaa/test2.txt");
src.renameTo(dest);
}
}
5 文件内容的操作
文件内容的读写---数据流
文件流类比水流的特点
打开文件
创建对象就是打开文件过程
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class IODemo7 {
public static void main(String[] args) throws FileNotFoundException {
InputStream inputStream = new FileInputStream("./test.txt");
}
}
关闭文件
import java.io.IOException;
import java.io.InputStream;
public class IODemo7 {
public static void main(String[] args) throws IOException {
//打开文件
InputStream inputStream = new FileInputStream("./test.txt");
//关闭文件
inputStream.close();
}
}
文件资源泄漏
即出现文件资源泄漏
由于 Java 提供了 GC(垃圾回收),就不用担心内存泄漏,每隔一定周期定时清理
修改后代码
package io;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class IODemo7 {
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
try {
//打开文件
inputStream = new FileInputStream("./test.txt");
//其它逻辑
} finally {
//关闭文件
inputStream.close();
}
}
}
一旦代码出了 try 代码块,此时 try 自动调用 inputStream 的 close
读文件:InputStream
方法
第一个打印0,修改形参不影响实参打印;第二个打印100,修改引用本身;第三个打印空,修改的是另一个对象
看修改引用本身还是修改对象
FileInputStream
构造⽅法
代码示例
一次读一个字节
try (InputStream is = new FileInputStream("test.txt")) {
while (true) {
int b = is.read();
if (b == -1) {
// 代表⽂件已经全部读完
break;
}
System.out.printf("%x ", b);
}
}
若文件内容为 “abcdef” 则输出结果为
61 62 63 64 65 66
若文件内容为 “你好” 则输出结果为
e4 bd a0 e5 a5 bd
一次读若干个字节
try (InputStream is = new FileInputStream("test.txt")) {
byte[] buffer = new byte[1024];
int n;
while (true) {
n = is.read(buffer);
if (n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("%c", buffer[i]);
}
}
}
结果:
Hello
代码执行流程
-
打开文件
test.txt
,准备读取。 -
进入
while (true)
循环,调用is.read()
读取一个字节。 -
如果读取到的字节是
-1
,表示文件已经读完,执行break
跳出循环。 -
如果读取到的字节不是
-1
,则将其转换为字符并输出。 -
重复步骤 2-4,直到文件读完。
test.txt
文件内容为 "Hello"
,其字节内容如下(ASCII 码):
-
H
-> 72 -
e
-> 101 -
l
-> 108 -
l
-> 108 -
o
-> 111
代码执行过程:
-
第一次读取:
b = 72
,输出H
。 -
第二次读取:
b = 101
,输出e
。 -
第三次读取:
b = 108
,输出l
。 -
第四次读取:
b = 108
,输出l
。 -
第五次读取:
b = 111
,输出o
。 -
第六次读取:
b = -1
,跳出循环,程序结束。
总结
-
b = -1
只是用来判断文件是否读完的标志,不会通过printf
输出。 -
只有
b
的值在0
到255
范围内时,System.out.printf("%c", b)
才会将其转换为字符并输出。 -
因此,程序只会输出文件的实际内容(如
"Hello"
),而不会输出-1
。
嗑瓜子,垃圾桶比较远,嗑一次丢一次到垃圾桶低效,但如果先放手里,一把后再丢就高效了
读取文字
基于字节数组构成字符串
package io;
import java.io.*;
public class IODemo8 {
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("./test.txt")) {
while (true) {
byte[] buffer = new byte[1024];
int n = inputStream.read(buffer);
String s = new String(buffer,0,n);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
写文件:OutputStream
方法
代码示例
package io;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class IODemo9 {
public static void main(String[] args) {
try(OutputStream outputStream = new FileOutputStream("./test.txt")) {
byte[] buffer = new byte[] {97,98,99,100,101,102};
outputStream.write(buffer);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
结果: text.txt 文本数据从 “你好” 变为 “abcdef ”
try(OutputStream outputStream = new FileOutputStream("./test.txt",true)) {
byte[] buffer = new byte[] {97,98,99,100,101,102};
outputStream.write(buffer);
} catch (IOException e) {
throw new RuntimeException(e);
}
结果: text.txt 文本数据从 “你好” 变为 “你好abcdef ”
文件内容的读写---字符流
读文件:reader
方法
此处是按照 char 为单位进行操作
代码示例
package io;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class IODemo10 {
public static void main(String[] args) {
try(Reader reader = new FileReader("./test.txt")) {
while (true) {
char[] buffer = new char[1024];
int n = reader.read(buffer);
if (n == -1) {
break;
}
String s = new String(buffer,0,n);
System.out.println(s);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
结果:
你好
abcdefabcdef
写文件:writer
方法
代码示例
package io;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class IODemo11 {
public static void main(String[] args) {
try(Writer writer = new FileWriter("./test.txt",true)) {
String s = new String("你好啊");
writer.write(s);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
加入 true 完成续写,结果文件内容为 “你好啊你好啊”
文件流---总结
文件读取---Scanner
代码示例
package io;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class IODemo12 {
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream("./test.txt")) {
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNext()) {
String s = scanner.next();
System.out.println(s);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
结果:
你好啊你好啊
6 综合运用
案例一:查找硬盘上的文件位置
前面是已知路径查找文件是否存在,案例是查找文件路径
代码示例
package io;
import java.io.File;
import java.util.Scanner;
public class IODemo13 {
public static void main(String[] args) {
//1.输入必要信息
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要搜索的文件名: ");
String fileName = scanner.next();
System.out.println("请输入要搜索的目录: ");
String rootPath = scanner.next();
File rootFile = new File(rootPath);
if (!rootFile.isDirectory()) {
System.out.println("输入路径有误!");
return;
}
//2.有了搜索路径以后就可以按照递归的方式搜索
// 知道递归的起点,还需要知道查询的文件名
scanDir(rootFile,fileName);
}
private static void scanDir(File rootFile, String fileName) {
//1.把当前目录中的文件和子目录都列出来
File[] files = rootFile.listFiles();
if (files == null) {
//空的目录直接返回
return;
}
//2.遍历上述 files,判断每一个 file 是文件还是目录
for (File f : files) {
if (f.isFile()) {
//普通文件,判定文件名是否是搜索的文件
if (fileName.equals(f.getName())) {
//打印绝对路径
System.out.println("找到了符合要求的文件! " + f.getAbsolutePath());
}
} else if (f.isDirectory()) {
//目录文件,就需要进一步的递归
scanDir(f,fileName);
}
}
}
}
结果
请输入要搜索的文件名:
test.txt
请输入要搜索的目录:
D:\Java\target
找到了符合要求的文件! D:\Java\target\aaa\a2\test.txt
为了更清楚看见递归过程修改代码
package io;
import java.io.File;
import java.util.Scanner;
public class IODemo13 {
public static void main(String[] args) {
//1.输入必要信息
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要搜索的文件名: ");
String fileName = scanner.next();
System.out.println("请输入要搜索的目录: ");
String rootPath = scanner.next();
File rootFile = new File(rootPath);
if (!rootFile.isDirectory()) {
System.out.println("输入路径有误!");
return;
}
//2.有了搜索路径以后就可以按照递归的方式搜索
// 知道递归的起点,还需要知道查询的文件名
scanDir(rootFile,fileName);
}
private static void scanDir(File rootFile, String fileName) {
//1.把当前目录中的文件和子目录都列出来
File[] files = rootFile.listFiles();
if (files == null) {
//空的目录直接返回
return;
}
//2.遍历上述 files,判断每一个 file 是文件还是目录
for (File f : files) {
//更清楚看见这个过程
System.out.println("当前遍历到:" + f.getAbsolutePath());
if (f.isFile()) {
//普通文件,判定文件名是否是搜索的文件
if (fileName.equals(f.getName())) {
//打印绝对路径
System.out.println("找到了符合要求的文件! " + f.getAbsolutePath());
}
} else if (f.isDirectory()) {
//目录文件,就需要进一步的递归
scanDir(f,fileName);
}
}
}
}
结果
请输入要搜索的文件名:
test.txt
请输入要搜索的目录:
D:\Java\target
当前遍历到:D:\Java\target\aaa
当前遍历到:D:\Java\target\aaa\a1
当前遍历到:D:\Java\target\aaa\a1\a1.txt
当前遍历到:D:\Java\target\aaa\a1\a2.txt
当前遍历到:D:\Java\target\aaa\a2
当前遍历到:D:\Java\target\aaa\a2\test.txt
找到了符合要求的文件! D:\Java\target\aaa\a2\test.txt
当前遍历到:D:\Java\target\bbb
当前遍历到:D:\Java\target\bbb\b1
当前遍历到:D:\Java\target\bbb\b1.txt
当前遍历到:D:\Java\target\bbb\b2
当前遍历到:D:\Java\target\ccc
案例二:实现文件复制
代码示例
package io;
import java.io.*;
import java.util.Scanner;
public class IODemo14 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要复制的原文件: ");
String srcPath = scanner.next();
System.out.println("请输入要复制的目标文件: ");
String destPath = scanner.next();
//合法判定
//1.srcPath 对应的文件是否存在
File srcFile = new File(srcPath);
if (!srcFile.isFile()) {
System.out.println("源文件路径有误!");
return;
}
//2.destPath 不要求对应的文件存在,但目录得存在
File destFile = new File(destPath);
if (!destFile.getParentFile().isDirectory()) {
System.out.println("目标路径有误!");
return;
}
//复制操作
try(InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)) {
while (true) {
byte[] buffer = new byte[1024];
int n = inputStream.read(buffer);
if (n == -1) {
//读取完毕
break;
}
//把读取的内容写入到 outputStream 中
outputStream.write(buffer,0,n);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
结果
请输入要复制的原文件:
C:\Users\PC\OneDrive\图片\图1\cat.jpg
请输入要复制的目标文件:
C:\Users\PC\OneDrive\图片\图1\cat2.jpg
Process finished with exit code 0
案例三:结合案例一、二
代码示例
package io;
import java.io.*;
import java.util.Scanner;
public class IODemo15 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要搜索的路径: ");
String rootPath = scanner.next();
System.out.println("请输入要查询的词: ");
String word = scanner.next();
File rootFile = new File(rootPath);
if (!rootFile.isDirectory()) {
System.out.println("输入的要搜索的路径不正确!");
return;
}
scanDir(rootFile,word);
}
private static void scanDir(File rootFile, String word) {
File[] files = rootFile.listFiles();
if (files == null) {
return;
}
for (File f : files) {
System.out.println("当前遍历到: " + f.getAbsolutePath());
if (f.isFile()) {
//在文件内容中搜索
searchInFile(f,word);
} else if (f.isDirectory()) {
//递归遍历
scanDir(f,word);
}
}
}
private static void searchInFile(File f, String word) {
//通过这个方法在文件内部进行搜索
//1.把这个文件内容读取出来
try(InputStream inputStream = new FileInputStream(f)) {
StringBuilder stringBuilder = new StringBuilder();
while (true) {
byte[] buffer = new byte[1024];
int n = inputStream.read(buffer);
if (n == -1) {
break;
}
//此处只是读取出文件的一部分,需要把文件内容整体拼接在一起
String s = new String(buffer,0,n);
stringBuilder.append(s);
}
//加了打印内容后,可以看到文件内容
System.out.println("文件内容: " + stringBuilder);
//当文件读取完毕,循环结束之后,此时 stringBuilder 就是包含整个内容的字符串了
if (stringBuilder.indexOf(word) == -1) {
//没找到要返回
return;
}
//找到了,打印文件的路径
System.out.println("找到了" + "word" + "存在于" + f.getAbsolutePath());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
结果
请输入要搜索的路径:
D:\Java\target
请输入要查询的词:
word
当前遍历到: D:\Java\target\aaa
当前遍历到: D:\Java\target\aaa\a1
当前遍历到: D:\Java\target\aaa\a1\a1.txt
文件内容: hello Java
当前遍历到: D:\Java\target\aaa\a1\a2.txt
文件内容: hello C++
当前遍历到: D:\Java\target\aaa\a2
当前遍历到: D:\Java\target\aaa\a2\test.txt
文件内容: hello word
找到了word存在于D:\Java\target\aaa\a2\test.txt
当前遍历到: D:\Java\target\bbb
当前遍历到: D:\Java\target\bbb\b1
当前遍历到: D:\Java\target\bbb\b1.txt
文件内容:
当前遍历到: D:\Java\target\bbb\b2
当前遍历到: D:\Java\target\ccc
注:当出现错误时在合适位置加上打印日志(System.out.println)