文件
文件有两个概念,在广义来看就是操作系统上对硬件和软件资源抽象为文件。
在侠义上来看,就是我们保存在硬盘上的文件
在这里我们讨论的是狭义的文件,在外面的硬盘上的文件细分又可以分为二进制文件和文本文件,文本文件可以通过码表转换成现实生活中有意义的文字,而二进制文件则是我们看不懂的文件。
我们可以通过记事本打开一个文件,如果里面的乱码则说明这是二进制文件,如果你能看懂说明这是文本文件。
其实文件本质上都是二进制文件,上面的文件只是根据文件是否表达人类语言的意义来进行划分的,在计算机里文件的存储都是以二进制的方式进行存储的。
绝对路径:就是文件的具体位置信息,没有省略
相对路径:./text.txt 就在当前路劲下的 text.txt
…/text.txt 表示当前路劲的上一个路径
windows 操作系统路径表示支持斜杠 / 和 反斜杠 \ 两种符号,其他操作系统只能使用 斜杠 / 来对路径进行分割
我们使用 斜杠 / 来分割路径就可以了,反正 windows 操作系统也能支持。
File
在Java 中标准库给我们提供了可以操作文件或者目录的类 File。
注意 File 不仅仅可以操作文件,也可以操作目录。
下面是 File 的构造方法:
这里如果你使用的是相对路径进行传参,那这个相对路径的基准值就是程序存在在哪个文件夹下,这个路径就是基准值,因为基准值是根据程序运行而改变的。
File 提供的方法:
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
System.out.println(file.getPath());
System.out.println(file.getParent());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath());
System.out.println(file.exists());
}
}
首先要注意即使没有存在 file 这个文件,File 在实例化的时候是没有问题的。
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
System.out.println(file.createNewFile());
}
如果文件没有存在,会成功创建并且返回 true,如果文件已经存在就会创建失败并且返回false.
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
file.createNewFile();
System.out.println(file.exists());
System.out.println(file.delete());
System.out.println(file.exists());
}
这个就是简单的删除,没什么好说的。
public static void main(String[] args) throws IOException {
File file = new File("./test.txt");
file.createNewFile();
System.out.println(file.exists());
file.deleteOnExit();
System.out.println(file.exists());
}
deleteOnExit() 这个是等到进程结束才删除这个文件。
public static void main(String[] args) {
File file = new File("./text.txt");
file.mkdir();
}
mkdir 是创建一级目录,如果涉及到多级目录的创建是无法完成的,多级目录的创建要使用 mkdirs() 方法。
public static void main(String[] args) {
File file = new File("./text.txt/2005/04/02");
file.mkdirs();
}
list() 和 listFile() 都是获取当前目录下的所有文件,只是返回值类型不同,list() 是返回 String 类型,listFile() 则是返回 File 类型。
public static void main(String[] args) {
File file = new File("D:/code/Java/java_study/J2024_11_2");
String[] list = file.list();
System.out.println(Arrays.toString(list));
File[] list2 = file.listFiles();
System.out.println(Arrays.toString(list2));
}
IO 流
字节流
字节流是以字节为单位的读取,主要用于二进制文件的读写。
字符流在骄Java中有两个类需要掌握,一个是 InputStream ,另一个是 OutputStream 。
首先我们要知道这两个类都是抽象类,是不能直接实例化的。
接着我们还要知道输入流是用来读取还是写入???
输入输出的定义是我们站在CPU 的视角,数据往 CPU 来就是输入,数据从 CPU 离开就是 输出。
因此输入流 InputStream 就是用来读取数据的,OutputStream 是用来写入数据的。
最后,我们知道文件的打开是会消耗资源的,当我们使用完文件之后我们需要关闭资源,避免资源泄漏,其实这也是因为操作系统在每次打开文件,就会占用文件描述表的一个位置,并且这个文件描述表是不会自动扩容的,所以能打开的文件有限,不能只打开不关闭文件,这样文件描述表在某一个时刻就会被消耗殆尽导致后续再打开文件的操作失败引发 BUG。
我们关闭文件除了可以使用 close 方法之外,我们可以直接在 try(打开文件的代码){},当出了 try 代码块就会自动关闭文件,这是因为Java 的 IO 流的类 实现了 Closable 接口。
既然我们不能直接实例化 InputStream 和 OutputStream 的话,我们可以使用 FileInputStream
和 FileOutputStream
来实例化(因为它们分别继承了InputStream 和 OutputStream),听名字大家应该知道这两个类其实就是用来操作文件的。
FileInputStream
InputStream 提供的方法:
FileInoutStream 的构造方法:
代码演示:
首先在 test.txt 文件中写上 hello java
read() 演示:
public static void main(String[] args) throws IOException {
try(InputStream inputStream = new FileInputStream("./test.txt");) {
int n = 0;
while (n != -1) {
n = inputStream.read();
System.out.println(n);
}
}
}
read(byte[] b) 演示:这个会首先填满 byte 数组,并且返回一个数值表示读取了多少个字节。
这种也叫做输出型参数,就是我们传入的引用参数到一个方法里面,这个方法会直接修改我们传入的引用类型参数,并且这个参数的修改也影响到方法外部的参数,这种参数我们称之为输出型参数。
public static void main(String[] args) throws IOException {
try(InputStream inputStream = new FileInputStream("./test.txt");) {
byte[] b = new byte[1024];
while(true) {
int n = inputStream.read(b);
if(n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.println(b[i]);
}
}
}
}
FileOutputStream
OutputStream 提供的方法
FileOutputStream 的构造方法和 FileInputStream 的参数是一样的。
如果文件不存在的话,FileOutputStream 会自动创建好文件,然后进行写操作。
public static void main(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./test.txt");) {
outputStream.write(65);
outputStream.write(66);
outputStream.write(67);
}
}
public static void main(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./test.txt");) {
byte[] b = {97,98,99};
outputStream.write(b);
}
}
当你进行写操作的时候,是直接覆盖之前的内容再进行写操作,如果你想要进行的是追加写的话,在构造方法中加上一个
true
,这样就是追加写。
public static void main(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./test.txt",true);) {
byte[] b = {97,98,99};
outputStream.write(b);
}
}
字符流
字符流是以字符为单位进行存储的,主要用于文本文件的读写
字符流一样给我们提供了两个抽象类 Reader
和 Writer
,我们可以通过实例化 FileReader
和 FileWriter
我们先将文本内容改为 你好
Reader
Reader 提供的方法:
read() 是读取一个字符,read(char[] chuf) 是将字符数组填充读取到的字符,read(CharBuffer target) 其中的CharBuffer 是对char[] 进行了封装。
read(char[] cbuf, int off, int len) 就是指定将读取到的字符填充到字符数组的哪个位置。
public static void main(String[] args) throws IOException {
try(Reader reader = new FileReader("./test.txt")) {
while(true) {
char[] b = new char[1];
int n = reader.read(b);
if(n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.println(b[i]);
}
}
}
}
在Java中使用 utf8进行编码,明明一个中文占3个字节,为什么我用 char(两个字节) 就可以读取出来???
字符流读取到的是文件中原始的数据也就是 两个汉字占 6 个字节(utf8 编码),字符流会根据文件的编码方式进行解析,read() 一次就会读取到 3 个字节,返回的时候会针对这 3 个字节进行转码,也就是先将 这个 3 个字节对照 utf8 码表查询到 ‘你’ 这个汉字之后,read 又将 “你” 这个汉字在 unicode 这个码表进行查询得到 unicode 编码,这样就是两个字节,最后将这两个字节的数值返回到 char 变量中。
public static void main(String[] args) throws IOException {
try(Reader reader = new FileReader("./test.txt")) {
char[] arr = new char[2];
while(true) {
int n = reader.read(arr);
if(n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.println(arr[i]);
}
}
}
}
Writer
Writer 提供的方法:
public static void main(String[] args) throws IOException {
try(Writer writer = new FileWriter("./test.txt")) {
writer.write("hello world");
}
}
你想要追加写,也是一样加上 true 就可以了。
public static void main(String[] args) throws IOException {
try(Writer writer = new FileWriter("./test.txt",true)) {
writer.write("hello world");
}
}
实战演练
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
在遍历文件的时候,我们只能遍历到一层的文件,如果该文件夹里面还有文件的话,我们需要进行递归才能获取到内部的文件。
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Demo1 {
public static void main(String[] args) {
System.out.println("请输入指定目录:");
Scanner scan = new Scanner(System.in);
String str = scan.next();
File file = new File(str);
if(!file.isDirectory()) {
System.out.println("您输入的不是目录或者该目录不存在...");
return;
}
System.out.println("请输入指定的字符:");
String token = scan.next();
List<File> list = new ArrayList<>();
scanFile(file, token);
}
private static void scanFile(File file, String token) {
File[] ret = file.listFiles();
if(ret == null) {
return;
}
for (int i = 0; i < ret.length; i++) {
if(ret[i].isDirectory()) {
scanFile(ret[i], token);
} else {
delFile(ret[i], token);
}
}
}
private static void delFile(File file, String token) {
if(file.getName().contains(token)) {
System.out.println("该文件包含你指定的字符内容:" + file.getAbsoluteFile());
System.out.println("是否进行删除:Y / N");
Scanner scan = new Scanner(System.in);
String ret = scan.next();
if(ret.equals("Y")) {
file.delete();
}
}
}
}
进行普通文件的复制
import java.io.*;
import java.util.Scanner;
public class Demo2 {
public static void main(String[] args) throws IOException {
System.out.println("请输入你要复制的文件路径:");
Scanner scan = new Scanner(System.in);
String str = scan.next();
File file = new File(str);
if(!file.isFile()) {
System.out.println("你输入的不是文件或者该文件不存在...");
return;
}
System.out.println("请输入你要复制的路径:");
String end = scan.next();
File file2 = new File(end);
if(!file2.getParentFile().isDirectory()) {
System.out.println("要复制到的文件路径不存在...");
return;
}
byte[] b = new byte[1024];
try(InputStream inputStream = new FileInputStream(str);
OutputStream outputStream = new FileOutputStream(end);
) {
while(true) {
int n = inputStream.read(b);
if(n == -1) {
break;
}
outputStream.write(b,0, n);
}
}
System.out.println("复制成功");
}
}
扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)
import java.io.*;
import java.util.Scanner;
public class Demo3 {
public static void main(String[] args) throws IOException {
System.out.println("请输入你要指定的目录");
Scanner scan = new Scanner(System.in);
String str = scan.next();
File file = new File(str);
if(!file.isDirectory()) {
System.out.println("你输入的不是目录或者该目录不存在");
return;
}
System.out.println("请输入你要指定的内容:");
String token = scan.next();
scanDir(file, token);
}
public static void scanDir(File rootFile, String token) throws IOException {
File[] list = rootFile.listFiles();
for(File file: list) {
if(file.isDirectory()) {
scanDir(file, token);
} else {
checkFile(file, token);
}
}
}
private static void checkFile(File file, String token) throws IOException {
if(file.getName().contains(token)) {
System.out.println("文件名包含指定关键字:" + file.getAbsolutePath());
return;
}
Reader reader = new FileReader(file);
StringBuilder check = new StringBuilder();
char[] b = new char[1024];
while(true) {
int n = reader.read(b);
if(n == -1) {
break;
}
check.append(b);
}
if(check.toString().contains(token)) {
System.out.println("文件内容包含指定关键字:" + file.getAbsoluteFile());
}
}
}