目录
😄一.认识文件
1.1文件的概念与组成:
我们先来认识狭义上的文件(File),对于硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公室桌上的一份份真实的文件一般。
⽂件除了有数据内容之外,还有⼀部分信息,例如⽂件名、⽂件类型、⽂件⼤⼩等并不作为⽂件的数 据而存在,我们把这部分信息可以视为⽂件的元信息。
1.2树形结构组织与目录:
同时,随着⽂件越来越多,对⽂件的系统管理也被提上了⽇程,如何进行⽂件的组织呢,⼀种合乎⾃ 然的想法出现了,就是按照层级结构进⾏组织⸺也就是我们数据结构中学习过的树形结构。这样, ⼀种专门用来存放管理信息的特殊⽂件诞⽣了,也就是我们平时所谓⽂件夹(folder)或者⽬录 (directory)的概念。
1.3文件路径:
- 绝对路径
大家都知道,在我们平时使用计算机时要找到需要的文件就必须知道文件的位置,而表示文件的位置的方式就是路径,例如只要看到这个路径:c:/website/img/photo.jpg我们就知道photo.jpg文件是在c盘的website目录下的img子目录中。类似于这样完整的描述文件位置的路径就是绝对路径。
- 相对路径
除了可以从根开始进⾏路径的描述,我们可以从任意结点出发,进⾏路径的描述,⽽这种描述⽅式就 被称为相对路径(relative path),相对于当前所在结点的⼀条路径。
举例:
file1的位置为:C:\ABC\path1\file1;(绝对路径)
file2的位置为:C:\ABC\path2\file2;(相对路径)
这时候让file1说出file2的位置则为:../path2/file2;两个点(..)表示回退到下一层。解释一下就是,file1在path1文件夹下,先点点,回退到ABC文件夹下,然后加上path2/file2,也就是 ../path2/file2,这就是相对路径了
😚二.文件系统操作
java通过java.io.File 类对一个文件(包括目录)进行抽象类的描述,注意,有File对象,并不代表真实存在该文件
2.1File类概述:
- File类常见属性:
- File类常见构造方法:
- 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(Filedest) | 进⾏⽂件改名,也可以视为我们平 时的剪切、粘贴操作 |
boolean | canRead() | 判断⽤⼾是否对⽂件有可读权限 |
boolean | canWrite() | 判断⽤⼾是否对⽂件有可写权限 |
2.2案例演示:
案例演示一:
import java.io.File;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
File file = new File("../test.txt");
//1.获取File对象的父目录文件路径
System.out.println(file.getParent());
//2.获取File对象的纯文件名称
System.out.println(file.getName());
//3.获取File对象的文件路径
System.out.println(file.getPath());
//4.获取File对象的绝对文件路径
System.out.println(file.getAbsoluteFile());
//5.过去File对象修饰过的绝对路径
System.out.println(file.getCanonicalPath());
//6.根据File对象,⾃动创建⼀个空⽂件。成功创建后返回true
boolean ok = file.createNewFile();
System.out.println(ok);
//7.判断当前创建的文件是否存在
System.out.println(file.exists());
//8.判断File对象代表的⽂件是否是⼀ 个普通⽂件
System.out.println(file.isFile());
//9.判断File对象代表的⽂件是否是⼀个⽬录
System.out.println(file.isDirectory());
//10.删除该文件
System.out.println(file.delete());
//11.查看当前文件是否存在
System.out.println(file.exists());
}
}
运行结果:
..
test.txt
..\test.txt
D:\java_code\Test-java\..\test.txt
D:\java_code\test.txt
false
true
true
false
true
false
演示案例二:
import java.io.File;
public class Demo {
public static void main(String[] args) {
File dir = new File("some-parent/some-dir");
System.out.println(dir.isDirectory());
System.out.println(dir.isFile());
//创建File对象代表的⽬录
System.out.println(dir.mkdir());
System.out.println(dir.isDirectory());
System.out.println(dir.isFile());
//使用mkdir() 的时候,如果中间⽬录不存在,则⽆法创建成功;mkdirs()可以解决这个问题
System.out.println("===================================");
//创建File对象代表的⽬录,如果必要,会创建中间⽬录
System.out.println(dir.mkdirs());
System.out.println(dir.isDirectory());
System.out.println(dir.isFile());
}
}
运行结果:
false
false
false
false
false
===================================
true
true
false
🤪三.文件内容操作(“文件流”/ "IO"流)
IO简介:
IO 即 Input/Output
,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。数据传输过程类似于水流,因此称为 IO 流。IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。
Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
-
InputStream
/Reader
: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。 -
OutputStream
/Writer
: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
3.1InputStream概述:
方法简介:
说明:
InputStream 只是⼀个抽象类,要使⽤还需要具体的实现类。关于InputStream的实现类有很多,基本可以认为不同的输⼊设备都可以对应⼀个InputStream类,我们现在只关心从⽂件中读取,所以使 ⽤FileInputStream
FileInputStream构造方法概述:
示例:读取文件中的数据
注:原本文本文件中只有两个汉字:你好
实现方式1:
import java.io.*;
public class Demo {
public static void main(String[] args) throws FileNotFoundException {
try (InputStream inputStream = new FileInputStream("./src/iotest/test.txt")) {
while (true) {
int b = inputStream.read();
if (b == -1) {
// 读取完毕了
break;
}
// 表示字节, 更习惯使用 十六进制 打印显示.
System.out.printf("0x%x\n", b);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
实现方式2:
import java.io.*;
public class Demo1 {
public static void main(String[] args) {
try (InputStream inputStream = new FileInputStream("./src/iotest/test.txt")) {
while (true) {
byte[] buffer = new byte[1024];
// n 返回值表示 read 操作, 实际读取到多少个字节.
int n = inputStream.read(buffer);
System.out.println(n);
if (n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("0x%x\n", buffer[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
3.2OutputStream概述:
方法简介:
示例:项文件中写入数据
public class Demo2 {
public static void main(String[] args) {
try (OutputStream outputStream = new FileOutputStream("./src/iotest/test.txt", true)) {
// outputStream.write(97);
byte[] buffer = new byte[] { (byte)0xe4, (byte)0xbd, (byte)0xa0, (byte)0xe5, (byte)0xa5, (byte)0xbd };
//1.直接从0位置开始写6个单位长度的字符
outputStream.write(buffer, 0, 6);
//2.一个一个的写
outputStream.write(0xe4);
outputStream.write(0xbd);
outputStream.write(0xa0);
outputStream.write(0xe5);
outputStream.write(0xa5);
outputStream.write(0xbd);
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
3.3Reader概述:
Reader
用于从源头(通常是文件)读取数据(字符信息)到内存中,java.io.Reader
抽象类是所有字符输入流的父类。
Reader
用于读取文本, InputStream
用于读取原始字节。
Reader
常用方法:
read()
: 从输入流读取一个字符。read(char[] cbuf)
: 从输入流中读取一些字符,并将它们存储到字符数组cbuf
中,等价于read(cbuf, 0, cbuf.length)
。read(char[] cbuf, int off, int len)
:在read(char[] cbuf)
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字符数)。skip(long n)
:忽略输入流中的 n 个字符 ,返回实际忽略的字符数。close()
: 关闭输入流并释放相关的系统资源。
3.4Writer概述:
Writer
用于将数据(字符信息)写入到目的地(通常是文件),java.io.Writer
抽象类是所有字符输出流的父类。
Writer
常用方法:
write(int c)
: 写入单个字符。write(char[] cbuf)
:写入字符数组cbuf
,等价于write(cbuf, 0, cbuf.length)
。write(char[] cbuf, int off, int len)
:在write(char[] cbuf)
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字符数)。write(String str)
:写入字符串,等价于write(str, 0, str.length())
。write(String str, int off, int len)
:在write(String str)
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字符数)。append(CharSequence csq)
:将指定的字符序列附加到指定的Writer
对象并返回该Writer
对象。append(char c)
:将指定的字符附加到指定的Writer
对象并返回该Writer
对象。flush()
:刷新此输出流并强制写出所有缓冲的输出字符。close()
:关闭输出流释放相关的系统资源。
🤩四.小案例测试:
4.1案例一:
要求:扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含目录),并且后续询问用户是否要删除该文件
代码详解:
import java.io.File;
import java.util.Scanner;
public class IOTest1 {
//递归扫描文件
public static void scan(File currentFile,String key){
//如果当前文件不是目录,直接返回
if(!currentFile.isDirectory()){
return ;
}
File[] files = currentFile.listFiles();
//如果当前文件不存在或者当前文件为空时,直接返回
if(files == null || files.length == 0){
return ;
}
//递归遍历该目录下的文件
for(File f : files){
//如果是普通文件,判断文件名是否符合要求并提示用户删除
if(f.isFile()){
doDelete(f,key);
}else{
//如果是目录,继续递归
scan(f,key);
}
}
}
private static void doDelete(File f, String key) {
if(!f.getName().contains(key)){
//文件名中不包含指定的关键字
return ;
}
//提示用户,是否确认要删除
Scanner scanner = new Scanner(System.in);
System.out.println("查找到一个对应的文件路径: " + f.getAbsolutePath());
System.out.print("这个文件路径 " + f.getAbsolutePath() + " 是否确认要删除 Y/N -> ");
String choice = scanner.next();
if(choice.equals("Y") || choice.equals("y")){
f.delete();
}
}
public static void main(String[] args) {
System.out.print("请输入要搜索的路径:");
Scanner scanner = new Scanner(System.in);
String rootPath = scanner.next();
File rootFile = new File(rootPath);
if(!rootFile.isDirectory()){
System.out.println("输入的文件路径不存在");
return ;
}
System.out.print("请输入要删除的文件名的关键字:");
String key = scanner.next();
//进行递归查找
scan(rootFile,key);
}
}
运行结果(这里查找到的文件路径有许多,展示部分):
4.2案例二:
要求:实现复制文件, 把一个文件里的每个字节都读出来, 写入到另一个文件中
import java.io.*;
import java.util.Scanner;
public class IOTest2 {
// 实现复制文件, 把一个文件里的每个字节都读出来, 写入到另一个文件中
public static void main(String[] args) {
// 1. 输入路径, 并且做校验
Scanner scanner = new Scanner(System.in);
System.out.print("请输入源文件的路径: ");
String srcPath = scanner.next();
File srcFile = new File(srcPath);
if (!srcFile.isFile()) {
System.out.println("源文件路径有误!");
return;
}
System.out.print("请输入目标文件的路径: ");
String destPath = scanner.next();
File destFile = new File(destPath);
if (!destFile.getParentFile().isDirectory()) {
System.out.println("目标文件的路径有误!");
return;
}
// 2. 执行复制的过程
try (InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)) {
while (true) {
byte[] buffer = new byte[1024];
int n = inputStream.read(buffer);
System.out.println("n = " + n);
if (n == -1) {
break;
}
// 需要把 buffer 写入到 outputStream 中
outputStream.write(buffer, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果(原本test1.txt中数据为“你好世界”,而test2.txt中为空,复制完成后):
4.3.案例三:
要求:扫描指定⽬录,并找到名称或者内容中包含指定字符的所有普通⽂件(不包含⽬录)
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Scanner;
public class IOTest3 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入要查找的目录: ");
String srcString = scanner.next();
File srcFile = new File(srcString);
if(!srcFile.isDirectory()){
System.out.println("输入的文件目录有误!");
return ;
}
System.out.print("请输入要查询的关键词: ");
String key = scanner.next();
scan(srcFile,key);
}
private static void scan(File srcFile, String key) {
if(!srcFile.isDirectory()){
//如果不是目录,结束递归
return ;
}
File[] files = srcFile.listFiles();
if(files == null || files.length == 0){
return ;
}
//递归遍历目录下的文件
for(File f : files){
if(f.isFile()){
//进行后续的查询操作
doReserch(f,key);
}else{
scan(f,key);
}
}
}
private static void doReserch(File f, String key) {
//打开文件,读取文件内容,判断文件内容中是否包含 key
StringBuilder stringBuilder = new StringBuilder();
try(Reader reader = new FileReader(f)){
char[] buffer = new char[1024];
while(true){
int n = reader.read(buffer);
if(n == -1){
break;
}
String s = new String(buffer,0,n);
stringBuilder.append(s);
}
} catch (IOException e) {
e.printStackTrace();
}
if(stringBuilder.indexOf(key) == -1){
//没找到
return ;
}
//找到了
System.out.println("找到匹配文件: " + f.getAbsolutePath());
}
}
运行结果:
参考文章:
结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!