目录
1. 基础知识
(1)狭义上的文件就是,存储在硬盘上的数据,这些以“文件”为单位,组织在一起
常见的文件(文本文件、图片、视频、音频、可执行程序...)
文件夹也叫做“目录”,这也是一种特殊的文件
我们在电脑文件资源管理器中,随便打开一个,可以看到这些就是狭义上的文件
(2)广义上的文件就是,操作系统负责管理软硬件资源,操作系统(Linux)一般都会将这些资源都统一抽象为“文件”来进行管理。
比如,电脑上的键盘,电脑想从键盘读取数据,就是把键盘抽象为一个文件,读这个文件就能读到用户输入的案件内容。
再比如,电脑上的音响,电脑想给音响发送数据播放音乐,就是把音响抽象为一个文件,写这个文件就可以让音响播放音乐
本篇所写的文件操作就是,指狭义上的文件
(3)描述电脑上一个文件的具体位置,通过“路径”来描述,有两种
a)绝对路径: D:\腾讯软件\QQ音乐\QQMusic\QQMusicUp.exe
目录之间的分割符,可以使用 \ (反斜杠) ,也可以使用 / (斜杠)
但是我们在写代码时,最好使用 / (斜杠),来表示路径
b)相对路径: 写相对路径,必须要知道基准路径是什么
需要注意,如果使用当前的基准路径找不到QQMusicUp.exe
那就可以使用 ..返回到上级目录(..表示当前路径的上级路径)
当前是: D:\腾讯软件\腾讯会议\WeMeet
第一次使用.. 返回到 D:\腾讯软件\腾讯会议
第二次使用.. 返回到 D:\腾讯软件
这样就可以在 D:\腾讯软件 中往下找了
文件操作
文件操作,是属于操作系统层面,提供的一些API
不同的操作系统,提供的API是不一样的
而Java就在JVM中把不同系统的操作文件的API进行封装了,这样我们就可以使用Java中的库的代码来操作文件了
2. File类
注意下面的这操作,只是在操作“文件系统”(操作系统中管理文件的核心功能)
新增文件、删除文件、新增目录、列出目录内容。重命名、获取路径...
这些都不是针对文件内容的操作
具体FIle的构造和方法,大家可以翻看一下JDK帮助手册,没有的可以私信我给你发一份
demo1,看一下File的常用方法
public class demo01 {
public static void main(String[] args) throws IOException {
// File f = new File("D:/test.txt");
File f = new File("./test.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());
}
}
demo02, 判断文件是否存在,不存在就创建
public class demo02 {
public static void main(String[] args) throws IOException {
File f = new File("./test.txt");
//判定存在
System.out.println(f.exists());
//判定是否是目录
System.out.println(f.isDirectory());
//判定是否是文件
System.out.println(f.isFile());
//把这个文件创建出来
f.createNewFile();
//判定存在
System.out.println(f.exists());
//判定是否是目录
System.out.println(f.isDirectory());
//判定是否是文件
System.out.println(f.isFile());
}
}
public class demo03 {
public static void main(String[] args) {
File f = new File("./test.txt");
f.delete();
}
}
可以看到代码运行后,test.txt 删除成功
还有一个是,deleteOnExit 进程退出时删除文件
demo04,创建目录
//创建目录
public class demo04 {
public static void main(String[] args) {
File f = new File("./testDir");
f.mkdir();
}
}
运行代码后,可以看到目录创建成功
注意 mkdir 是创建单层目录 mkdirs 是创建多层目录
public class demo04 {
public static void main(String[] args) {
File f1 = new File("./testDir/aaa/bbb");
f1.mkdirs();
}
}
demo05,重命名
public class demo05 {
//重命名
public static void main(String[] args) {
File srcFile = new File("aaa.txt");
File destFile = new File("bbb.txt");
destFile.renameTo(destFile);
}
}
程序运行前,程序运行后,
demo06,返回 File 对象代表的目录下的所有文件名
public class demo06 {
public static void main(String[] args) {
File f = new File("./testDir");
String[] results = f.list();
System.out.println(Arrays.toString(results));
}
}
3. 流(针对文件内容操作读写)
3.1 InputStream 概述
Java标准库,在“流”的概念上,提供了一组类,完成读写文件的操作这组类,分为两种( 这两种都是抽象类,不能实例化)a)字节流(以字节为基本单位),适用于二进制文件InputStream 读 OutputStream 写b)字符流(以字符为基本单位),适用于文本文件Reader 读 Writer 写上面的这些都是 父类,在标准库中,给这些父类还提供了各种子类的实现,以适应各种不同常见下的读写需求InputStream --> FIleInputStream OutputStream --> FileOutputStreamReader --> FileReader Writer --> FileWriter
方法 | 说明 |
---|---|
int read() | 读取一个字节的数据,返回-1代表已经完全读完了 |
int read(byte[] b) | 最多读取b.length字节的数据到b中,返回实际读到的数量;-1代表已经读完了 |
int read(byte[] b, int off, int len) | 最多读取len - off 字节的数据到b中,放在从 off 开始,返回实际读到的数量;-1代表已经读完 |
void close() | 关闭字节流 |
int read(byte[] b) 和 int read(byte[] b, int off, int len) 是把读到的内容放到参数字节b数组中参数b用来存放方法读取到的结果需要注意正常情况下,对于方法来说,参数表示的是“输入信息”,返回值才是“输出信息”但此时,是用参数b来作为存放输出结果的内容,b称为是“输出型参数”
(1)InputStream read()读取文件中的字节数据
public class demo07 {
public static void main(String[] args) throws IOException {
//打开文件
InputStream inputStream = new FileInputStream("./bbb.txt");
while(true) {
int b = inputStream.read();
if(b == -1) {
break;
}
System.out.println(b);
}
//关闭文件
inputStream.close();
}
}
(2)OutputStream write()写文件
需要注意的是,使用OutputStream写文件时,只要打开文件成功,就会把文件原有内容清空,重新给进写
public class demo08 {
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("./bbb.txt");
outputStream.write(97);
outputStream.write(98);
outputStream.write(99);
outputStream.close();
}
}
(3)Reader 使用字符流读文件
需要注意read读取到的是int类型,要用int类型来接收,最后打印时强转为char类型
//使用字符流读文件
public class demo09 {
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("./bbb.txt");
while(true) {
//把读到的内容用ret接收一下
int ret = reader.read();
//判断一下ret是不是-1如果是,那就是读完了
if(ret == -1) {
break;
}
//如果没有读完就再强转接收打印出来
char ch = (char)ret;
System.out.println(ch);
}
reader.close();
}
}
(4)Writer 使用字符流来写文件
public class demo10 {
public static void main(String[] args) throws IOException {
//使用字符流来读文件
Writer writer = new FileWriter("./bbb.txt");
writer.write("hello world");
writer.close();
}
}
3.2 Scanner
针对文本文件,使用字符流的时候,还可以使用Scanner来进行读取
InputStream是字节流
Scanner 是在InputStream的基础上,包装的字符流
public class demo11 {
public static void main(String[] args) throws IOException {
//使用scanner 读取文本文件
InputStream inputStream = new FileInputStream("./bbb.txt");
//可以给Scanner的构造对象中传入一个 字符流对象
Scanner scanner = new Scanner(inputStream);
while(scanner.hasNext()) {
System.out.println(scanner.next());
}
inputStream.close();
}
}
使用PrintWriter可以简化写文件操作开发
public class demo12 {
public static void main(String[] args) throws IOException {
//针对写文件来说,还可以使用 PrintWriter 来简化开发
OutputStream outputStream = new FileOutputStream("./bbb.txt");
PrintWriter writer = new PrintWriter(outputStream);
writer.println();
writer.printf("a = %d\n", 10);
outputStream.close();
}
}
进行文件操作,一定要进行close(否则会造成文件资源泄露)
如果在代码中,close 前面的代码抛出异常了,后面的 close 就执行不到了
所以在写代码时,可以使用 try_finally来保证 close一定会执行到
public static void main(String[] args) throws IOException {
//针对写文件来说,还可以使用 PrintWriter 来简化开发
OutputStream outputStream = null;
try{
OutputStream outputStream1 = new FileOutputStream("./bbb.txt")
PrintWriter writer = new PrintWriter(outputStream);
writer.println();
writer.printf("a = %d\n", 10);
} finally {
outputStream.close();
}
}
也可以直接将OutputStream写进try中(try with resources 也就是带资源的try)
把要关闭的对象写到try的()中,当try结束,就会自动的调用到,对应对象的close方法
而且支持一个()放多个对象,多个对象的创建之间使用 封号 “;”分割就可以了
public class demo12 {
public static void main(String[] args) throws IOException {
//针对写文件来说,还可以使用 PrintWriter 来简化开发
try(OutputStream outputStream = new FileOutputStream("./bbb.txt")){
PrintWriter writer = new PrintWriter(outputStream);
writer.println();
writer.printf("a = %d\n", 10);
}
}
}
4. 代码小练习
(1) 扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件
a)先让用户输入:要扫描的路径+要查找的词
b)遍历目录,找到名字匹配的文件
这个需要用到一个核心方法:listFiles() 如果当前是一个目录,直接调用这个方法,就能把当前目录中包含的文件和子目录都能够列举出来
但是这个方法只能列举出一层,没法直接列出子目录中的子目录
解决的方法就是,遍历listFIles的结果,针对每一个元素,进行判定,看是一个普通文件,还是一个目录。(此处的目录结构本质上是一个“树”)
如果是普通文件,直接判定文件名包含了要查找的词,如果是目录,递归的调用listFiles
c)询问用户是否删除
import java.io.*;
import java.util.Scanner;
public class demo01 {
public static void main(String[] args) throws IOException {
//1.让用户输入 要扫描的路径+要查找的关键词
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要扫描的路径:");
File rootDir = new File(scanner.next());
//用isDirectory判断一下rootDir是不是目录
if(!rootDir.isDirectory()) {
System.out.println("您输入的目录不存在!");
return;
}
System.out.println("请输入要搜索的关键词:");
String toDelete = scanner.next();
//2.遍历目录,找到名字匹配的文件
scanDir(rootDir, toDelete);
}
//借助这个方法进行递归遍历
private static void scanDir(File rootDir, String toDelete) throws IOException {
System.out.println("当前访问:" + rootDir.getCanonicalPath());
File[] files = rootDir.listFiles();
if(files == null) {
//说明 rootDir是一个空的目录
return;
}
//如果目录非空,则循环遍历每个元素
for (File f : files) {
if(f.isDirectory()) {
scanDir(f, toDelete);
}else {
//不是目录,是普通文件,判断文件名是否是所要查找的,是否要进行删除
checkDelete(f, toDelete);
}
}
}
private static void checkDelete(File f, String toDelete) throws IOException {
//看一下这个 文件 中是否包含了 toDelete
if(f.getName().contains(toDelete)) {
System.out.println("这个关键词" + toDelete + "被" +
f.getCanonicalPath() + "包含了,是否要进行删除?(Y/N)");
Scanner scanner = new Scanner(System.in);
String choice = scanner.next();
if(choice.equals("Y") || choice.equals("y")) {
f.delete();
System.out.println("删除成功!");
}
}
}
}
(2)进行普通文件的复制
把第一个文件打开,把里面的内容逐个字节的读出来,写入到第二个文本中就可以了
使用字节流来进行操作,字节流也是可以用来拷贝文件的
import java.io.*;
import java.util.Scanner;
//进行普通文件的复制
public class demo02 {
public static void main(String[] args) {
//1.先输入要复制哪个文件 和 将文件复制到哪里去(目标文件)
Scanner scanner = new Scanner(System.in);
System.out.println("请输入源文件:");
//使用File的作用是可以判断文件是否存在
File srcFile = new File(scanner.next());
System.out.println("请输入目标文件:");
File destFile = new File(scanner.next());
if(srcFile.isFile()) {
System.out.println("输入的源文件有误!");
return;
}
//getParentFile()如果文件不存在就抛出异常
if(!destFile.getParentFile().isDirectory()) {
System.out.println("输入的目标文件有误!");
return;
}
//2.打开源文件,按照字节读取内容,依次写入到目标文件中
try(InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)) {
//读 src 的每个字节,写入到 dest中
while (true) {
int ret = inputStream.read();
//如果读到-1就结束
if(ret == -1) {
break;
}
outputStream.write(ret);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
(3)扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class demo03 {
public static void main(String[] args) throws IOException {
//1.输入路径和要查询的关键词
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要扫描的路径:");
File rootDir = new File(scanner.next());
System.out.println("请输入要查询的关键词:");
String toFind = scanner.next();
//2.递归的进行扫描目录
scanDir(rootDir, toFind);
}
private static void scanDir(File rootDir, String toFind) throws IOException {
//listFiles将当前目录中的文件和子目录都列举出来
File[] files = rootDir.listFiles();
if(files == null) {
return;
}
for (File f : files) {
if(f.isDirectory()) {
scanDir(f, toFind);
} else {
checkFile(f, toFind);
}
}
}
private static void checkFile(File f, String toFind) throws IOException {
//1.先检查文件名
if(f.getName().contains(toFind)) {
System.out.println(f.getCanonicalPath() + " 文件名中包含 " + toFind);
}
//2.检查文件内容
try(InputStream inputStream = new FileInputStream(f)) {
StringBuilder stringBuilder = new StringBuilder();
Scanner scanner = new Scanner(inputStream);
//这里需要按行读取了
while (scanner.hasNextLine()) {
stringBuilder.append(scanner.nextLine() + "\n");
}
if(stringBuilder.indexOf(toFind) > -1) {
System.out.println(f.getCanonicalPath() + " 文件内容包含 " + toFind);
}
}
}
}