Java文件操作/IO
一.什么是文件?
文件是一种在硬盘上存储数据的方式,操作系统帮我们把硬盘的一些细节都封装起来了,程序员只需要了解文件相关的接口即可,相当于操作文件就是间接的操作硬盘了,
硬盘用来存储数据,和内存相比硬盘的存储空间更大,访问速度更慢,成本更低,持久化存储,操作系统通过“文件系统”这样的模块来管理硬盘
别看我电脑上有c盘d盘,但是我电脑只装载着一块硬盘,操作系统可以通过文件系统把这个硬盘抽形成多个硬盘就如我的CD两盘,NTFS是windows上的文件系统,背后有一定的格式来组织硬盘的数据,EXT4是Linux上的文件系统
不同的文件系统管理文件的方式都是类似的
通过目录(directory,平常叫文件夹,专业术语叫目录)构成了N叉树的树形结构
我们在文件系统中都是通过路径来确定一个具体的文件
同样是一个cat.jpg文件,站在不同的基准目录上,查找的路径是不相同的
文件系统上存储的文件,具体来说又分成两大类
1.文本文件–存储的是字符
字符怎么定义呢?
有个表叫utf8,这个表上数据的组合就是字符
2.二进制文件–存储的是二进制数据
判断文本文件和二进制文件最简单的方式就是
直接用记事本打开,如果打开之后能看懂,就是文本,否则就是二进制像word文档,ppt,excel这些,如果托到记事本上,都是二进制文件,虽然word文档里存的东西是汉字等可以看懂的内容,但是word文档不仅仅包含我们自己输入的内容,还有行间距,文字格式等众多内容
但是如果把excel的后缀改成csv格式,就是文本文件了
utf8是文件的编码格式,此时一个汉字是三个字节的大小,在java中采用的编码格式是unicode,一个汉字是两字节的大小正好能放进char类型,那么java操作文件的时候不做处理肯定会出现问题,但是JVM已经处理好了
二.Java标准库中的操作文件类
操作文件可以理解成通过java代码把你硬盘的文件删除修改创建等,也就是通过java代码操作文件系统
Java 中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意, File 对象可以对应到一个真实存在的文件,也可以对应到一个不存在的文件
所有外部设备(包括硬盘)向系统传输数据的过程,对CPU 而言都属于"输入操作"。
File类里面的方法
获得文件路径
import java.io.File;
import java.io.IOException;public class Demo {public static void main(String[] args) throws IOException {//File file = new File ("D:\\JavaEE\\java-ee-beginner\\test.txt");//绝对路径File file = new File("./test.txt");//相对路径,相对于当前项目目录System.out.println(file.getParent());//获得父目录System.out.println(file.getName());//获得文件名System.out.println(file.getPath());//获得文件路径(与上述构造方法路径相同)System.out.println(file.getAbsolutePath());//获得绝对路径System.out.println(file.getCanonicalPath());//获得规范路径(去除..等)}
}
创建文件和判断文件
import java.io.File;
import java.io.IOException;public class Demo {public static void main(String[] args) throws IOException {File file = new File("D:\\JavaEE\\java-ee-beginner\\test.txt");file.createNewFile();// 创建文件System.out.println(file.exists());// 判断文件是否存在System.out.println(file.isFile());// 判断是否是文件System.out.println(file.isDirectory());// 判断是否是目录}
}
文件跟目录的区别可以认为是文件是这个路径的终点,而目录下面还有别的路径
删除文件
import java.io.File;public class Demo {public static void main(String[] args) {File file=new File("./test.txt");file.delete();}
}
import java.io.File;public class Demo {public static void main(String[] args) throws InterruptedException {File file=new File("./test.txt");//不是立刻删除,等到程序运行结束再删除file.deleteOnExit();Thread.sleep(2000);//程序运行两秒会结束,所以这个文件在两秒之后删除}
}
创建目录
public static void main(String[] args) {File file1 = new File("./testDir");File file2 = new File("./testDir/111/222");// mk => make dir => directory// mkdir 一次只能创建一层目录. mkdirs 可以一次创建多级目录file1.mkdir();//file2.mkdirs();//创建多级目录}
列出目录内容
mport java.io.File;
import java.util.Arrays;public class Demo4 {public static void main(String[] args) {//File file = new File("./test.txt");/*//文件是无法进行list(列出当前目录下的文件)String[] list = file.list();System.out.println(Arrays.toString(list));*/File file = new File("D:\\JavaEE\\java-ee-beginner");String[] list = file.list();//列出当前目录下的文件System.out.println(Arrays.toString(list));File[] files = file.listFiles();//列出当前目录下的文件(前缀带路径) 用File来接受System.out.println(Arrays.toString(files));}
}
文件重命名
import java.io.File;public class Demo {public static void main(String[] args) {File file = new File("./test.txt");File file2 = new File("./test2.txt");boolean result = file.renameTo(file2);//把file重命名为file2}
}
mport java.io.File;public class Demo {public static void main(String[] args) {/*File file = new File("./test.txt");File file2 = new File("./test2.txt");boolean result = file.renameTo(file2);//把file重命名为file2*/File file = new File("./test2.txt");File nweFile = new File("D:\\JavaEE\\java-ee-beginner\\20250607-File\\test2.txt");boolean result = file.renameTo(nweFile);//重命名操作还能起到移动文件的作用/*** 从操作系统的角度来看,重命名操做和移动操作(剪切)本质是一致的 移动操作极快,时间复杂度o1 (如果跨硬盘了就很慢了(复制+删除))* 复制文件,时间复杂度o(n),文件/目录里所有的数据,遍历,再写入写的文件/目录*/System.out.println(result);}
}
对文件内容操作
Reader类
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;// Reader 使用.
public class Demo
{public static void main(String[] args) throws IOException{// FileReader 构造方法, 可以填写一个文件路径(绝对路径/相对路径都行), 也可以填写一个构造好的 File 对象
// Reader reader = new FileReader("。/test.txt");
// try {
// // 中间的代码无论出现啥情况, close 都能保证执行到.
// } finally {
// // 抛出异常, 或者 return, close 就都执行不到了~~
// reader.close();
// }// 上述使用 finally 的方式能解决问题, 但是不优雅.// 使用 try with resources 是更好的解决方案.try (Reader reader = new FileReader("d:/test.txt")){while (true) {//一直/* int c = reader.read();//返回值int(最大值0xFFFF两个字节的最大值)if (c == -1) {break;}System.out.println((char) c);*/char[] buf = new char[1024];//可以起到一个缓冲区的作用int n = reader.read(buf);if (n == -1) {break;}for (int i = 0; i < n; i++) {System.out.println(buf[i]);}}}
}
InputStream类
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;public class Demo {public static void main(String[] args) throws IOException {try (InputStream inputStream = new FileInputStream("./test.txt")) {//出了try块,资源会自动释放,相当于自动调用inputStream.close()//读文件操作while (true) {/*int date = inputStream.read();//不带参数表示读取一个字节if (date == -1) {//读完文件break;}System.out.printf("0x%x\n", date);*///一次读取多个字节,数组长度进行自定义byte[] bytes = new byte[3];//读操作,就会尽可能把字节数组给填满//填不满的话,能填几个就是几个int len = inputStream.read(bytes);System.out.println("len= " + len);//len就是表示实际读取了几个字节if (len == -1) {//读完文件break;}for (int i = 0; i < len; i++) {System.out.printf("0x%x \n", bytes[i]);//打印每个字节的十六进制数}System.out.println("===========");}}}
}
于是我们可以借助Scanner来完成上述操作
平时我们输入是
Scanner scanner=new Scanner(System.in);
那么system.in的类型也是一个InputStream
所以就有以下操作
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;public class Demo {public static void main(String[] args) throws IOException {try (InputStream inputStream = new FileInputStream("./test.txt")) {Scanner scanner = new Scanner(inputStream);// 此时就是从 test.txt 这个文件中读取数据了!!String s = scanner.next();System.out.println(s);}}
}
Scanner只是用来读取文本文件,不适合读取二进制文件,标准库中需要用到一些其他类
Write类
mport java.io.*;public class Demo {public static void main(String[] args) {try (Writer writer = new FileWriter("./output.txt", true)) {//true是实现第二次输入的时候能够拼接第一次的输入//writer.write("666");BufferedWriter bufferedWriter = new BufferedWriter(writer);//带缓冲区的流bufferedWriter.write("777");//将内容写到缓冲区去里bufferedWriter.flush();//将缓冲区里的内容写入文件bufferedWriter.close();//可以放到try块里 自动关闭流} catch (IOException e) {e.printStackTrace();}}
}
* 缓冲区通常是一段内存的运行空间 提高编程效率 减少磁盘IO操作 但是在一些特殊情况下 如网络传输 数据库操作等 就需要使用非缓冲区的流 如FileWriter(false)
* 因此进行IO的时候就希望能使用缓冲区把要写的数据先放到缓冲区里 攒一波再一起写
* 或者读取的时候也不是一个一个的读 而是一次读取一批数据 放到缓冲区再慢慢解析
* 当前IO流 read和write就是属于直接读取文件了 想要提升效率
* 1.手动创建缓冲区 byte[]手动减少read和write的次数
* 2.就需要使用缓冲区流 如BufferedInputStream和BufferedOutputStream