一、文件的基础知识
1、文件可分为侠义和广义
侠义的文件:
是指存储在硬盘上的数据,以文件为单位进行组织。常见的就是普通的文件(如文本文件,视频,图片,可执行文件等)
文件夹是一种特殊的文件,也叫做目录
广义的文件:
操作系统要负责软硬件资源的管理,而操作系统往往会把这些都统一的抽象为“文件”来经行管理。
例如
网卡:就是把网卡这个设备抽象成一个文件,创建出一个特殊的文件,表示网卡。从网卡接受到的数据,就读进这个文件。往网卡里发送数据,就写入这个文件。
键盘:从键盘读取数据,也是把键盘抽象成一个文件(stdin),读设个文件就能读到用户输入的按键内容了。
本文讨论的文件是侠义的文件。
2、描述文件位置
描述文件的具体位置,分为绝对路径和相对路径。
1)绝对路径:从盘符(C盘,D盘.....)开始,目录之间用\(或者/)分隔。
如 D:\Program Files(x86)\Tencent\WeChat\WeChat.exe
2)相对路径:要先确定一个基准路径,以它为起点。
如绝对路径的栗子:
如果基准路径是D:
此时相对路径就是 .\Program Files(x86)\Tencent\WeChat\WeChat.exe
如果基准路径是D:\Program Files(x86)\Tencent\WeChat:
此时相对路径就是.\WeChat.exe
二、File类
文件操作是属于操作系统层面的,操作系统会提供一些API。所以不同操作系统提供的API是不一样的。
而JAVA作为一个跨平台的语言,为了统一代码,在JVM中把不同系统的API进行封装。JAVA就可以使用JAVA中的库的代码来操作文件了。
Java 中通过 java.io.File 类对一个文件(包括目录)进行操作。
1、get基本信息
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException {
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());
}
}
2、判断文件是否存在,不存在就创建。
import java.io.File;
import java.io.IOException;
public class Demo2 {
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("__________");
//判断文件名
System.out.println(f.exists());
//判断是否为目录
System.out.println(f.isDirectory());
//判断是否为文件
System.out.println(f.isFile());
}
}
3、删除文件
import java.io.File;
public class Demo3 {
public static void main(String[] args) {
File f = new File("./test.txt");
f.delete();
}
}
4、创建目录
import java.io.File;
//创建目录
public class Demo4 {
public static void main(String[] args) {
File f = new File("./testDir/aaa/bbb");
f.mkdirs();
}
}
(mkdir是创建单层目录,mkdirs是创建多层目录)
5、重命名文件
import java.io.File;
public class Demo5 {
public static void main(String[] args) {
//重命名
File srcFile = new File("aaa.txt");
File desFile = new File("bbb.txt");
srcFile.renameTo(desFile);
}
}
三、流(文件内容的操作)
java中提供了两种类来完成读写文件的操作。
上述两种类因为是抽象类,所以有了后面的子类来实现读写需求!
1、InputStream.read() 字节流读文件
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class Demo7 {
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() 字节流写文件
write中包含三个版本:
第一个是写一个字节;
第二个是把字节数组的所有内容,都写到文件中;
第三个是把b字节数组从下标off位置开始写,写len个字节。
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class Demo1 {
//写文件
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();
}
}
这里注意,使用outputStream写文件时,只要打开文件,就会把文件原有内容清空。
3、Reader 字符流读文件
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class Demo2 {
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("./bbb.txt");
while (true){
int ret = reader.read();
//判断一下ret是不是-1,如果是,就是读完了
if (ret == -1){
break;
}
char ch = (char)ret;
System.out.println(ch);
}
reader.close();
}
}
ps:要注意read读到的是int类型,要用int类型来接收,最后打印时强转成char类型。
4、Writer 字符流写文件
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class Demo3 {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("./bbb.txt");
writer.write("hello francis");
writer.close();
}
}
5、Scnner 字符流读文件
针对文本文件,使用字符流的时候,可以使用Scanner来读取。
InputStream是字节流。
Scnner是在InputStream的基础上,包装的字符流。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class Demo4 {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("./bbb.txt");
Scanner scanner = new Scanner(inputStream);
while (scanner.hasNext()){
System.out.println(scanner.next());
}
inputStream.close();
}
}
6、PrintWriter 字符流写文件
针对写文本文件来说,还可以使用PrintWriter来简化开发。
import java.io.*;
public class Demo5 {
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("./bbb.txt");
PrintWriter writer = new PrintWriter(outputStream);
writer.println();
writer.printf("a = %d\n",10);
outputStream.close();
}
}
注意!
在进行文件操作时,一定要记得close!!!
每一个进程都对应着PCB,PCB里面有一个字段:文件表述符 表(同一个进程里多个PCB共同使用同一份文件描述符表的)
文件描述表相当于一个数组/顺序表。进程每次打开一个文件,就会在这个表里创建一个项。这个项就表示一个文件。
如果关闭一个文件,就会把表里的对应项给释放掉。
如果不关闭,这个项就会占着位置,如果你持续打开文件,且从来不关,这个表项就会被耗尽,导致后面再打开文件就会打开失败(文件资源泄露)!
所以上面的代码可以改一下
import java.io.*;
public class Demo5 {
public static void main(String[] args) throws IOException {
OutputStream outputStream = null;
try{
outputStream = new FileOutputStream("./bbb.txt");
PrintWriter writer = new PrintWriter(outputStream);
writer.println();
writer.printf("a = %d\n",10);
}finally {
outputStream.close();
}
}
}
这样,就算程序抛出异常,close也会执行到。
import java.io.*;
public class Demo5 {
public static void main(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./bbb.txt");){
PrintWriter writer = new PrintWriter(outputStream);
writer.println();
writer.printf("a = %d\n",10);
}
}
}
也可以直接把OutputStream写道try里(try with resources,就是带资源的try)
把要关闭的对象写到try()里,当try结束时,就会自动调用对应对象的close方法。
四、结合实现个程序
1、扫描目录找到并删除
扫描指定目录,并找到名称中包含指定字符的所以普通文件(不包含目录),并且后续询问用户是否要删除该文件。
1、先让用户输入要扫描的路径+要查找的词
2、遍历目录,找到名字匹配的文件
遍历目录需要一个核心方法:
listFiles() 能够把当前目录里的文件和子目录列举出来,但是这个方法只能列出一层,所以解决方法是,遍历listFiles的结果,
如果是普通文件,就直接判定文件名是否包含了要查的词
如果是目录,递归调用listFiles
3、询问用户是否删除
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
public class Demo6 {
public static void main(String[] args) throws IOException {
//1、先让用户输入要扫描的路径+要查找的词
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要扫描的路径");
File rootDir = new File(scanner.next());
if (!rootDir.isDirectory()){
System.out.println("输入的目录不存在");
return;
}
System.out.println("请输入要搜索的关键词");
String toDelete = scanner.next();
//2、遍历目录(rootDir),需要借助一个核心方法:listFiles。
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 {
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();
}
}
}
2、文件复制
把第一个文件打开,把里面的内容逐个字节读取出来,写到第二个文件就行。
import java.io.*;
import java.util.Scanner;
//进行普通文件的复制
public class Demo8{
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 Demo7 {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要扫描的路径");
File rootDir = new File(scanner.next());
System.out.println("请输入要查询的词");
String toFind = scanner.next();
scanDir(rootDir,toFind);
}
private static void scanDir(File rootDir, String toFind) throws IOException {
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 {
//先检查文件名
if (f.getName().contains(toFind)){
System.out.println(f.getCanonicalPath() + "文件名包含" + toFind);
}
//检查文件内容
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);
}
}
}
}