目录
一、三种路径的分类:
1、绝对路径:
绝对路径是从文件系统的根目录开始,完整地描述文件或目录位置的路径。
例如,在windows系统中,有个具体文件是 report.txt :
c:\User\user\docs\report.txt
2、相对路径:
相对路径不包含完整的路径信息,而是根据当前工作目录来确定目标文件或目录的位置。
( . ) :表示当前目录。例如,.\example.txt 表示当前目录下的example.txt 文件。
( .. ) :表示上级目录。例如,..\Documents\example.txt 表示当前目录的上级目录下的Documents文件夹的 example.txt 文件。
假如当前目录是home,..\..\表示的是当前home目录的上上级目录。
例子,假设当前的工作目录是 john,目标文件是c:\home\john\Documents\example.txt 。
绝对路径:c:\home\john\Documents\example.txt 。
相对路径:.\Documents\example.txt 。
在实际的开发中,绝对路径和相对路径用到的会更多。(尤其是相对路径,用的会更多)。
3、基准目录:
基准路径也就是当前工作目录。
基准路径 + 相对路径 = 绝对路径。
基准路径是指当前程序或者操作所基于的目录。在进行文件操作时,相对路径的计算就以这个基准路径为起点。例如,若基准路径是c:\User\john,相对路径Documents\example.txt (省略了前面的 .\ )就表示的绝对路径是 c:\User\john\Documents\example.txt 。
二、文件的种类:
文件分为两大类:文本文件 和 二进制文件 。
判断当前文件是文本文件还是二进制文件,初步手段是把文件拖入记事本里,如果是乱码就是二进制文件。反之是文本文件。(此方法有局限性)。
特性 | 文本文件 | 二进制文件 |
底层存储 | 由可读字符组成,使用字符编码(如 ASCII、UTF-8) | 由任意二进制数据组成(0 和 1 的序列),无需遵循字符编码规则。 |
人类可读性 | 可直接用文本编辑器查看和编辑(如记事本)。 | 无法直接阅读(显示为乱码),需专用程序解析。 |
文件示例 | .txt , .csv , .html , .py |
.exe , .jpg , .mp3 , .docx |
三、利用JAVA操作文件:
在JAVA中,我们使用 File 类和进行文件的操作:
File类主要作用于文件或目录,它不仅可以用于创建文件和目录,还能进行一些与文件和目录相关的操作,比如获取文件或目录的属性、删除文件或目录等。
1、File类的构造方法:
注意,由于 .\ 会发生转移字符的情况,所以使用 ./ 也可以(windows支持)。
有两种方法构造 File 类实例比较常用:
第一种( File(String pathname) ):
通过指定的路径名字符串创建一个 File 实例。这个路径名可以是绝对路径,也可以是相对路径。
// 使用绝对路径创建 File 对象
File absoluteFile = new File("C:/Users/example.txt");
// 使用相对路径创建 File 对象
File relativeFile = new File("./test.txt");
absoluteFile
依据绝对路径构建,而 relativeFile
则是基于相对路径构建,相对路径是相对于当前工作目录而言的。
第二种( File(String parent , String child ) ):
根据父路径名字符串和子路径名字符串创建一个 File 实例。系统会将父路径和子路径拼接起来,形成完整的路径。
String parent = "C:/Users";
String child = "./example.txt";
File file = new File(parent, child);
parent 代表父路径,child 代表子路径,二者组合后生成完整路径 c:/Users/example.txt 。
2、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() | 判断⽤⼾是否对⽂件有可写权限 |
使用例子:
我的 java 工程目录:
例子1:
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());
System.out.println(file.exists());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
代码运行结果(结合上面表格方法介绍看):
例子2:
File file = new File("./test.txt");
// 创建文件,成功创建返回true,若存在返回false
file.createNewFile();
// 进程退出之后(代码运行结束), 才触发删除.
file.deleteOnExit();
Scanner scanner = new Scanner(System.in);
System.out.println("输入任意内容, 退出程序");
scanner.next(); // 阻塞
代码运行结果(结合上面表格方法介绍看):
再看我们当前的工作目录:(test.txt文件已被删除)
例子3:
File file = new File("./");
System.out.println(Arrays.toString(file.list()));
System.out.println(Arrays.toString(file.listFiles()));
代码运行结果(结合上面表格方法介绍看):
例子4:
File file = new File("./test/aaa/bbb");
// 创建一级目录
System.out.println(file.mkdir()); // false
./test/aaa/bbb
是指定的路径。其中 ./
代表当前目录,整体意思是要在当前目录下的 test
目录中的 aaa
目录里创建 bbb
目录。
如果当前目录下已经有 test
目录,并且 test
目录下有 aaa
目录,而 aaa
目录中没有 bbb
目录,那么 mkdir()
方法会成功创建 bbb
目录,并且返回 true
。
要注意的是,如果当前目录下没有test 目录,或者 test 目录下没有aaa目录,mkdir()
方法无法创建父目录,也就不能创建 bbb
目录,此时会返回 false
。(因为我的工作目录没有当前的test目录)。
例子5:
File file = new File("./test/aaa/bbb");
// 创建多级目录
file.mkdirs();
mkdirs()这个方法,它会检查路径中各级目录是否存在,若不存在就会依次创建这些目录。例如,如果 test
目录不存在,它会先创建 test
目录;接着检查 aaa
目录,若不存在就创建 aaa
目录;最后检查并创建 bbb
目录。
例子6:
我的工程目录:
File file = new File("./test/test.txt");
File file2 = new File("./hello.txt");
file.renameTo(file2);
此时test目录下的test.txt文件已经没了。
这里实现了两个操作。
一是把 当前工作目录下的 test 目录 里的test.txt文件名字改为hello.txt,二是把名字改成 hello.txt 的文件移动到当前工作目录下。
但是如果当前目录存在hello.txt文件,调用 file.renameTo(file2)
方法进行重命名和移动操作时会失败,renameTo()
方法会返回 false
。
四、利用JAVA操作文件内容:
在 Java 里,操作文件内容时,有字节流和字符流两种不同的输入输出方式。
字节流:
以字节(Byte)作为处理单位。一个字节为 8 位(bit),可处理任意类型的文件,像图片、音频、视频等二进制文件,也能处理文本文件。
字符流:
以字符(Character)作为处理单位。在 Java 中,一个字符通常是 16 位(2 字节),采用 Unicode 编码,适合处理文本文件,能直接处理字符数据,避免了字节流处理文本时的编码转换问题。
1、字节流:
读取操作:
使用 InputStream 类来读取,但时,InputStream 是一个抽象类,它是所有字节输入流类的超类(超类是一种位于继承层次结构较高位置的类,其他类可以从它继承属性和方法),不能直接实例化,通常需要使用它的具体子类来完成实例化。
实例化对象:
我们使用 FileInputStream 这个类来创建实例对象:
第一种:( FileInputStream( String name ) )
InputStream inputStream = new FileInputStream("./test.txt");
在上述代码中,使用 FileInputStream("./test.txt")
创建了一个输入流对象,用于读取当前目录下的 test.txt
文件。如果文件不存在,会抛出 FileNotFoundException
异常。
第二种:( FileInputStream( File file ) )
File file = new File("./test.txt");
InputStream inputStream = new FileInputStream(file);
此代码先创建了一个 File
对象,然后使用 FileInputStream(file)
创建输入流对象。用于读取当前目录下的 test.txt
文件。如果文件不存在,会抛出 FileNotFoundException
异常。
FileInputStream 核心方法:
方法 | 说明 |
int read( ) | 此方法从输入流一次读取读取一个字节的数据,并将其作为一个 0 到 255 之间的整数返回。若到达输入流的末尾,返回 -1 。 |
int read(byte[ ] b) | 尝试从输入流中读取最多 b.length 个字节的数据,并将其存储在字节数组 b 中。返回实际读取的字节数。若到达输入流的末尾,返回 -1 。 |
int read(byte[ ] b, int off, int len) | 从输入流中读取最多 len 个字节的数据,并将其存储在字节数组 b 中,从索引 off 开始存储。返回实际读取的字节数。若到达输入流的末尾,返回 -1 |
使用例子1:
InputStream inputStream = new FileInputStream("./test.txt");
int data;
while ((data = inputStream.read()) != -1) {
System.out.print((char) data);
}
// 读取完毕, 循环 break 之后, 需要关闭文件.
inputStream.close();
上述代码中,(data = inputStream.read()) 这个表达式的结果就是赋给 data 的值,通过 while
循环不断调用 read()
方法读取文件内容,直到返回 -1
表示文件读取完毕。每次读取的字节数据被转换为字符并输出。
使用例子2:
InputStream inputStream = new FileInputStream("./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.print((char)bytes[i]);
}
}
// 读取完毕, 循环 break 之后, 需要关闭文件.
inputStream.close();
当文件剩余字节数大于字节数组长度,read方法就会尝试读取 bytes.length 个字节到 bytes 数组中,并返回 bytes.length 个字节个数给到 n 。
当文件剩余字节数小于或等于字节数组长度,返回当前的有效字节的个数,再下一次循环就返回-1。
使用例子3:
InputStream inputStream = new FileInputStream("./test.txt");
byte[] buffer = new byte[1024];
int offset = 0;
int length = 512;
int bytesRead;
while(true) {
bytesRead = inputStream.read(buffer, offset, length);
if(bytesRead == -1) {
//读取完毕了.
break;
}
// 处理读取到的数据
for (int i = offset; i < offset + bytesRead; i++) {
System.out.print((char) buffer[i]);
}
//下一次获取文件字节起始位置
offset += bytesRead;
}
// 读取完毕, 循环 break 之后, 需要关闭文件.
inputStream.close();
此read方法从文件中最多读取len个字节的数据,从索引 offset 处开始存储,同理,与上述的例子2一样,读取到文件,末尾返回-1。
输出操作:
使用 OutputStream 类来输出,但时,OutputStream 是一个抽象类,不能直接实例化,通常需要使用它的具体子类来完成实例化。
实例化对象
我们使用 FileOutputStream 这个类来创建实例对象。
第一种:( FileOutputStream(Sting name) )
OutputStream fos = new FileOutputStream("./test.txt");
name 就是要打开的具体文件路径和文件名称。
第二种:( FileOutputStream(Sting name , boolean append) )
OutputStream fos = new FileOutputStream("./test.txt", true);
name 就是要打开的具体文件路径和文件名称。
append 如果为true,则将字节数据从文件末尾处开始写入;如果为false,则会覆盖原文件内容。
FileOutputStream 核心方法:
方法 | 解释 |
write ( int a) | 写⼊要给字节的数据 |
write(byte[ ] b) | 将字符数组 b 中的数据全部写入文件中 |
write(byte[ ] c , int off, int len) | 将 c 这个字符数组中从 off 开始的 数据写⼊文件 中,⼀共写 len 个 |
flush() | 我们知道 I/O (可以理解为从磁盘读取数据和把数据写入磁盘)的速度是很慢的,所以,⼤多的 OutputStream 为了减少设备操作的次数,在写数 据的时候都会将数据先暂时写⼊内 存的⼀个指定区域⾥,直到该区域 满了或者其他指定条件时才真正将 数据写⼊设备中,这个区域⼀般称 为缓冲区。但造成⼀个结果,就是 我们写的数据,很可能会遗留⼀部 分在缓冲区中。需要在最后或者合适的位置,调⽤ flush(刷新)操 作,将数据刷到设备中。 |
使用例子:
OutputStream fos = new FileOutputStream("./test.txt");
int a = 'A';
fos.write(a);
byte[] arr = {'a',68,69};
fos.write(arr);
//刷新缓冲区
fos.flush();
写入后,对应文件的内容。
2、字符流:
读取操作:
基于 Reader 这个抽象类来进行字符流读取,不能直接实例化,通常需要使用它的具体子类来完成实例化。
实例化对象:
使用 FileReader 这个子类来进行实例化:
第一种:( FileReader(String name) )
Reader reader = new FileReader("./test.txt")
name 为具体的文件路径和文件名称。
第二种:( FileReader(File file) )
File file = new File("./test.txt");
Reader reader = new FileReader(file);
指定的 File
对象创建一个新的 FileReader
对象。若指定的 File
对象表示的文件不存在、是一个目录而非普通文件,或者由于其他原因无法打开该文件,会抛出 FileNotFoundException
异常。
FileReader 核心方法:
方法 | 介绍 |
int read( ) | 读取单个字符,返回一个 0 到 65535 之间的整数,表示读取的字符。 如果已经到达流的末尾,则返回 -1。 |
int read(char[ ] arr ) | 将字符读入数组,返回读取的字符数。 如果已经到达流的末尾,则返回 -1。 |
int read(char[ ] arr , int off , int len) | 将字符读入数组的一部分,数组从偏移量 返回实际读取的字符数。 如果已经到达流的末尾,则返回 -1。 |
使用例子1:
文件含有内容:
Reader reader = new FileReader("./test.txt");
while (true) {
int ch = reader.read();
if (ch == -1) {
break;
}
char c = (char) ch;
System.out.println(c);
}
运行结果:
使用例子2:
文件含有内容:
Reader reader = new FileReader("./test.txt");
while (true) {
char[] chars = new char[1024];
int n = reader.read(chars);
if (n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.println(chars[i]);
}
}
运行结果:
输出操作:
使用 Writer 类来输出,因为它是抽象类,所以不能直接实例化,通常要使用它的具体子类来完成实际的字符写入操作。
实例化对象:
使用 FileWriter 这个子类来实例化对象。
第一种:( FileWriter(String name) )
Writer writer = new FileWriter("./test.txt");
name 就是要打开的具体文件路径和文件名称。
第二种:( FileWriter(File file ,boolean append) )
Writer writer = new FileWriter("./test.txt", true);
name 就是要打开的具体文件路径和文件名称。
append 如果为true,则将字节数据从文件末尾处开始写入;如果为false,则会覆盖原文件内容。
FileWriter 核心方法:
方法 | 解释 |
write( ) | 写入单个字符,或者字符串 |
write(char[ ] arr ) | 将字符数组中的所有字符写入文件。 |
write(char[ ] arr ,int off , int len) | 将字符数组的 off 位置开始写入,最多写入 len 个 |
flush( ) | 刷新缓冲区 |
使用例子:
Writer writer = new FileWriter("./test.txt");
writer.write("你好世界");
char[] arr = {'z','k'};
writer.write(arr);
writer.flush();
代码运行结果:
五、使用文件操作的案例:
1、根据用户输入的目录和文件名,来进行文件的查找和删除
public static void main(String[] args) {
//用户输入要查询的文件名
Scanner scanner = new Scanner(System.in);
System.out.println("温馨提示,输入的目录要符合d:/ 或者 d:/User/...的格式");
System.out.println("请输入要查询的目录: ");
String dir = scanner.nextLine();
System.out.println("请输入要查询的文件名: ");
String filename = scanner.nextLine();
//判断目录是否存在
File rootFile = new File(dir);
if(!rootFile.isDirectory()) {
System.out.println("目录不存在!");
return;
}
//进行搜索,递归遍历目录中所有的文件和子目录
scanDir(rootFile,filename);
}
private static void scanDir(File rootFile, String filename) {
//列出 rootFile 中的内容
File[] files = rootFile.listFiles();
if(files == null) {
//空目录
return;
}
//遍历 files 数组,判断每个元素的类型
for (File file : files) {
if(file.isDirectory()) {
//是一个目录,递归调用 scanDir 方法.
scanDir(file,filename);
}else if(file.isFile()) {
//是一个文件,判断文件名是否匹配。
if(file.getName().contains(filename)) {
tryDelete(file);
}
}
}
}
private static void tryDelete(File file) {
System.out.println("已找到改文件,文件路径为:" + file.getAbsolutePath());
System.out.println("是否删除该文件(Y/N)");
Scanner scanner = new Scanner(System.in);
String choice = scanner.next();
if(choice.equals("Y") || choice.equals("y")) {
file.delete();
System.out.println("删除成功!");
}
}
进阶版:
public class Demo25 {
//记录文件是否存在
public static boolean count = true;
public static void main(String[] args) {
while(true) {
// 每次查找前重置 count 变量
count = true;
//用户输入要查询的文件名
Scanner scanner = new Scanner(System.in);
System.out.println("此程序用来进行文件的查找和删除");
System.out.println("温馨提示,输入的目录要符合d:/ 或者 d:/User/...的格式");
System.out.println("请输入要查询的目录: ");
String dir = scanner.next();
scanner.nextLine();
System.out.println("请输入要查询的文件名(文件名可以是部分名称): ");
String filename = scanner.next();
scanner.nextLine();
//判断目录是否存在
File rootFile = new File(dir);
if(!rootFile.isDirectory()) {
System.out.println("目录不存在!");
System.out.println();
continue;
}
//进行搜索,递归遍历目录中所有的文件和子目录
scanDir(rootFile,filename);
if(count == true) {
System.out.println("文件不存在,或者文件名输入错误!");
}
System.out.println("输入 1 继续查找,输入 0 退出程序");
int a = 0;
while(true) {
if(scanner.hasNextInt()) {
a = scanner.nextInt();
if(a == 1) {
break;
}else if(a == 0) {
return;
}else {
System.out.println("输入的数字无效,请重新输入: ");
// 除去无效输入的内容
scanner.nextLine();
}
}else {
System.out.println("输入错误,请输入数字: ");
//除去无效输入的内容
scanner.nextLine();
}
}
}
}
private static void scanDir(File rootFile, String filename) {
//列出 rootFile 中的内容
File[] files = rootFile.listFiles();
if(files == null) {
//空目录
return;
}
//遍历 files 数组,判断每个元素的类型
for (File file : files) {
if(file.isDirectory()) {
//是一个目录,递归调用 scanDir 方法.
scanDir(file,filename);
}else if(file.isFile()) {
//是一个文件,判断文件名是否匹配。
if(file.getName().contains(filename)) {
tryDelete(file);
count = false;
}
}
}
}
private static void tryDelete(File file) {
System.out.println("已找到该文件,文件路径为:" + file.getAbsolutePath());
System.out.println("是否删除该文件(Y/N)");
Scanner scanner = new Scanner(System.in);
String choice = scanner.next();
if(choice.equals("Y") || choice.equals("y")) {
file.delete();
System.out.println("删除成功!");
}
}
}
2、基根据用户输入的源文件路径,复制源文件到指定目录
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();
//判断源文件是否存在
File srcFile = new File(srcPath);
if(!srcFile.isFile()) {
System.out.println("该文件不存在!");
return;
}
// 因为destPath最后包含了文件名,而我们所需要的只是前面的目录
File destFile = new File(destPath);
//得到用户输入的目标目录的父目录
File parentFile = destFile.getParentFile();
//判断得到的目标目录的父目录计算机中是否存在
if(!parentFile.isDirectory()) {
System.out.println("目标父目录不存在");
return;
}
//进行拷贝
copy(srcFile,destFile);
}
private static void copy(File srcFile, File destFile) {
try(InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)) {
while(true) {
byte[] arr = new byte[1024];
int len = inputStream.read(arr);
if(len == -1) {
break;
}
outputStream.write(arr,0,len);
}
}catch (IOException e){
e.printStackTrace();
}
}