目录
扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否 要删除该⽂件
扫描指定⽬录,并找到名称或者内容中包含指定字符的所有普通⽂件(不包含⽬录)
File是Java中的一个重要类,位于java.io包中,可以用来创建、删除、查询文件或目录等
什么是文件(File)?
文件可以是文本、图像、视频、音频、程序代码等多种类型的数据
什么是目录(Directory)?
通俗点来说,目录就是文件夹,用于将多个文件存放在一起,便于管理
什么是绝对路径,什么是相对路径,什么是工作路径?
绝对路径就是从根目录开始的,绝对路径不受当前工作路径的影响,而相对路径就是从当前目录开始,System.getProperty("user.dir") 这种方式来获取当前的工作路径
public static void main(String[] args) throws IOException {
File file = new File("test1.txt");
System.out.println(file.getName());//文件或目录最后一个路径元素
System.out.println(file.getPath());//返回文件的路径
System.out.println(file.getAbsoluteFile());//返回文件的绝对路径
System.out.println(file.getCanonicalFile());//返回文件规范的绝对路径
}
运行结果:
File的常见方法
返回值 | 方法名 | 说明 |
String | getParent() | 返回 File 对象的⽗⽬录⽂件路径 |
String | getName() | 返回文件或目录的最后一个名称 |
String | getPath() | 返回 File 对象的⽂件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回文件的规范绝对路径 |
boolean | exists() | 判断 File 对象描述的⽂件是否存在 |
boolean | isDirectory() | 判断 File 对象代表的⽂件是否是⼀个⽬录 |
boolean | isFile() | 判断 File 对象代表的⽂件是否是⼀个普通⽂件 |
boolean | createNewFile() | 根据 File 对象,⾃动创建⼀个空⽂件,成功创建后返回 true |
boolean | delete() | 根据 File 对象,删除该⽂件。成功 删除后返回 true |
void | deleteOnExit() | 根据 File 对象,标注⽂件将被删除, 删除动作会到 JVM 运⾏结束时才会进⾏ |
String[] | list() | 返回目录中的每个文件和目录的名称(不包括路径) |
File[] | listFiles() | 返回目录中所有文件和子目录的File 对象(包括路径) |
boolean | mkdir() | 创建单个目录,父目录必须存在 |
boolean | mkdirs() | 创建多级目录,父目录不存在则会自动创建父目录 |
boolean | renameTo(File dest) | 进⾏⽂件改名,也可以视为我们平时的剪切、粘贴操作 |
boolean | canRead() | 判断⽤⼾是否对⽂件有可读权限 |
boolean | canWrite() | 判断⽤⼾是否对⽂件有可写权限 |
普通文件的创建
public static void main(String[] args) throws IOException {
File file = new File("D:\\java");
System.out.println(file.exists());//true
System.out.println(file.isDirectory());//true,因为‘java’为目录
System.out.println(file.isFile());//false
System.out.println(file.createNewFile());//false,因为‘java’是个文件夹如果
//再次创建的话会返回false,如果改成其他名字就可以创建成功
}
普通文件的删除
public static void main(String[] args) throws IOException {
File file = new File("D:\\java\\test1.txt");
System.out.println(file.exists());//返回false,因为test1.txt并没有在’java‘这个路径上
System.out.println(file.createNewFile());//返回true,并成功创建出test1.txt这个文本文件
System.out.println(file.exists());//返回true,因为成功创建出了test1.txt,所以可以找到
System.out.println(file.delete());//返回true,因为存在所以可以删除,如果不存在则删除失败返回false
System.out.println(file.exists());//返回false,因为成功删除,所以在当前路径下找不到test1.txt
}
deleteOnExit
我们可以将这个理解成延迟删除,意思是只有当程序运行结束的时候才会删除
public static void main(String[] args) {
File file = new File("D:\\java\\test.txt");
file.deleteOnExit();
Scanner scanner = new Scanner(System.in);
System.out.println("请随便输入");
scanner.next();//输入,用来观察是否被删除
System.out.println("删除成功");
}
通过下面这张图可以观察到当程序还未结束的时候,这个文件是没有被删除的,但是当我输入完并按下回车之后,这个文件就会立刻被删掉
目录的创建
mkdir
public static void main(String[] args) {
File file = new File("D:\\java\\newFile1");
System.out.println(file.isDirectory());//用来判断是否为目录,如果是返回true,如果不是返回false
System.out.println(file.mkdir());//创建成功,因为‘java’这个目录存在
//如果‘java’这个目录不存在则创建失败,那么就使用mkdirs
}
mkdirs
mkdirs与mkdir不同的是,对于mkdirs来说如果父目录,也就是中间目录不存在的话则会自动创建父目录,而后者不会
public static void main(String[] args) {
File file = new File("D:\\java\\newFile2\\newFile3");
System.out.println(file.mkdirs());//在‘java’这个目录下创建一个newFile2目录
//并在newFile2这个目录下创建newFile3,因为newFile2这个目录不存在,但是我们使用的是
//mkdirs所以会自动帮我们创建好newFile2这个目录
}
文件的重命名和剪切
剪切
public static void main(String[] args) {
File src = new File("D:\\java\\test1.txt");
File dest = new File("D:\\java\\File1\\test2.txt");
System.out.println(src.exists());
System.out.println(dest.exists());
System.out.println(src.renameTo(dest));
}
没执行前
java文件中保存着test1.txt 和 test2.txt 这两个文本文件,并且File1 目录里面为空
程序执行后
将D:\java\test1.txt
移动到 D:\java\File1\
目录并重命名为 test2.txt
重命名
public static void main(String[] args) {
File src = new File("D:\\java\\test1.txt");
File dest = new File("D:\\java\\test3.txt");
System.out.println(src.exists());
System.out.println(dest.exists());//要求test3不存在
System.out.println(src.renameTo(dest));
}
运行结果
只有当目标文件不存在的时候才能完成重命名操作,所以上述代码成功将 test1.txt 重命名为 test3.txt
InputStream
返回值 | 方法名 | 作用 |
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() | 关闭字节流 |
InputStream 只是⼀个抽象类,要使⽤还需要具体的实现类。
read()
代码1
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:\\java\\test.txt");
while(true){
int b = inputStream.read();
if(b == -1){
break;
}
System.out.printf("%c",b);
}
inputStream.close();
}
代码2
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:\\java\\test.txt");
while(true){
byte[] buffer = new byte[1024];
int len = inputStream.read(buffer);
if(len == -1) break;
for(int i = 0;i<len;i++){
System.out.printf("%c",buffer[i]);
}
}
}
代码2相对于代码1来说减少了大量的 IO 操作,因为 read() 方法是一次读一个字节,而代码2就是利用这一点将读取到的数据放到 buffer 里面,直到读满1024个字节或者内容读取完毕才会执行打印,这样就减少了大量的 IO 操作
运行结果就是看你的 test.txt 里面的内容是什么,但是不推荐内容包含中文,如果需要使用
FileInputStream 来查看有包含中文内容的请继续往下看
使用 FileInputStream 来读取中文
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:\\java\\test.txt");
byte[] buffer = new byte[1024];
while(true){
int len = inputStream.read(buffer);
if(len == -1) break;
for(int i = 0;i<len;i+=3){
String s = new String(buffer,i,3,"UTF-8");
System.out.printf("%s",s);
}
}
inputStream.close();
}
但是这样写就会有一个弊端,就是文本中必须全部都是中文,因为对于UTF8来说,每个汉字都会被分割成3个字节
OutputStream
返回值 | 方法名 | 说明 |
void | write(int b) | 写⼊要给字节的数据,一次只能写入一个字节 |
void | write(byte[] b) | 将 b 这个字符数组中的数据全部写 ⼊ os 中 一次可以写入多个字节 |
int | write(byte[] b, int off, int len) | 读取 b 这个字符数组从 [off,off+len) 的长度 |
void | close() | 关闭字节流 |
void | flush() | 我们知道 I/O 的速度是很慢 的,所以,⼤多的 OutputStream 为了减少设备操作的次数,在写数 据的时候都会将数据先暂时写⼊内 存的⼀个指定区域⾥,直到该区域 满了或者其他指定条件时才真正将 数据写⼊设备中,这个区域⼀般称 为缓冲区。但造成⼀个结果,就是 我们写的数据,很可能会遗留⼀部 分在缓冲区中。需要在最后或者合 适的位置,调⽤ flush(刷新)操 作,将数据刷到设备中。 |
write()
在代码中,如果我们使用try-with-resources
语句,则不用手动进行字符/字节流的关闭,因为如果在try代码块结束的时候就会自动调用 close ,而不需要手动进行,用来放在忘记关闭而导致的内存泄漏问题
public static void main(String[] args) {
try(OutputStream os = new FileOutputStream("D:\\test.txt")){
os.write('H');
os.write('E');
os.write('L');
os.write('L');
os.write('O');
} catch (IOException e) {
throw new RuntimeException(e);
}
}
但是使用上述代码我们可以看到,如果我们再进行一次写操作,之前的内容就会消失不见,这是因为只要我们使用OutputStream 打开文件,里面的内容就会消失,如何解决这个问题也很简单,因为OutputStream 默认是会清空之前的内容的,但是我们还有一个追加操作,就是在路径后面写一个“ true” 参数
OutputStream os = new FileOutputStream("D:\\java\\test.txt",true)
我们也可以用 write(byte[] b)方法进行写入,方式如下
public static void main(String[] args) {
try(OutputStream os = new FileOutputStream("D:\\java\\test.txt",true)){
byte[] b = new byte[]{'a','b','c','d'};
os.write(b);
} catch (IOException e){
e.printStackTrace();
}
}
但是当我们使用InputStream 和 OutputStream 的时候读取和写入中文就比较麻烦,所以我们可以使用更简单的方法来进行中文的输入和输出,请看下文
Reader
public static void main(String[] args) {
try(Reader reader = new FileReader("D:\\java\\test.txt")){
while(true){
int n = reader.read();
if(n == -1){
return;
}
char ch = (char)n;
System.out.print(ch+" ");
}
} catch (IOException e){
e.printStackTrace();
}
}
对于这段代码来说,不管你的内容是中文还是英文还是中英混杂都可以正确的读取出来
输出结果:
看到这里可能就会有一个疑问?明明 java 中的 char 类型表示的是 2 个字节,但是 java 中的汉字是使用 utf8 来表示的,一个汉字 3 个字节,为什么可以使用 char 来表示 3个字节的汉字呢?其实
java 的 char 在表示汉字的时候并不是用 utf8 而是使用 unicode 的编码方式,在 unicode 中一个汉字就表示 2 个字节
在这里我也推荐一个很好用的查看字符编码的网站
查看字符编码(UTF-8)http://www.mytju.com/classCode/tools/encode_utf8.asp
Writer
write(String str)
这个方法可以直接写入字符串,非常方便,如果要进行追加操作也一样在后面加个true就可以
public static void main(String[] args) {
try(Writer writer = new FileWriter("D:\\java\\test.txt",true)){
writer.write("你好中国");
} catch (IOException e){
e.printStackTrace();
}
}
其他方法其实和前面介绍的都差不多,这里就不再重复了
代码练习
扫描指定⽬录,并找到名称中包含指定字符的所有普通⽂件(不包含⽬录),并且后续询问⽤⼾是否 要删除该⽂件
package FileTest;
import java.io.File;
import java.util.Scanner;
public class demo15 {
private static void scan(File currentFile,String key){
if(!currentFile.isDirectory()){
return;
}
File[] files = currentFile.listFiles();
if(files.length == 0 || files == null){
return;
}
for(File f : files){
if(f.isFile()){
FileDelete(f,key);
} else {
scan(f,key);
}
}
}
private static void FileDelete(File f,String key){
if(!f.getName().contains(key)){
//文件名中不包含指定的关键字
return;
}
Scanner scanner = new Scanner(System.in);
System.out.println(f.getAbsoluteFile()+"是否要删除Y/y");
String choice = scanner.next();
if(choice.equals("y") || choice.equals("Y")){
f.delete();
}
}
public static void main(String[] args) {
System.out.println("请输入要搜索的路径");
Scanner scanner = new Scanner(System.in);
String rootPath = scanner.next();
File rootFile = new File(rootPath);
if(!rootFile.isDirectory()){
System.out.println("输入的路径不存在");
return;
}
//走到此处就说明路径存在
System.out.println("请输入要删除的文件名字的关键字");
String key = scanner.next();
//利用递归进行查找
scan(rootFile,key);
}
}
进⾏普通⽂件的复制
如果文件在目录中不存在,就会自动创建,但是如果连目录都不存在那就不行了
package FileTest;
import java.io.*;
import java.util.Scanner;
public class demo17 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入源文件的路径");
String srcPath = scanner.next();
File srcFile = new File(srcPath);
if(!srcFile.isFile()){
System.out.println("源文件路径有误");
return;
}
System.out.println("请输入目标文件的路径");
String destPath = scanner.next();
File destFile = new File(destPath);
//注意: 这里目标文件可以不存在但是父目录必须存在
if(!destFile.getParentFile().isDirectory()){
System.out.println("目标文件路径有误");
return;
}
//执行复制的过程
try(InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)){
while(true){
byte[] buffer = new byte[1024];
int n = inputStream.read(buffer);//返回读到的字节数量
if(n == -1){
return;
}
outputStream.write(buffer,0,n);//读取0~n个,因为buffer可能装不满
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
代码执行前:
代码执行后:
执行过程:
D:\java\test.txt 的意思就是将 test.txt 复制到D:\java\File1 这个路径里面并重命名为 newtest.txt
这里的File1这个父目录必须存在,这段代码不仅可以复制文件还可以复制图片等
扫描指定⽬录,并找到名称或者内容中包含指定字符的所有普通⽂件(不包含⽬录)
package FileTest;
import java.io.*;
import java.util.Scanner;
public class demo18 {
public static void main(String[] args) {
System.out.println("请输入要搜索的路径");
Scanner scanner = new Scanner(System.in);
String rootPath = scanner.next();
File rootFile = new File(rootPath);
if(!rootFile.isDirectory()){
System.out.println("路径不存在");
return;
}
System.out.println("请输入要找的关键字");
String key = scanner.next();
scan(rootFile,key);
}
private static void scan(File rootFile, String key) {
if(!rootFile.isDirectory()){
return;
}
File[] files = rootFile.listFiles();
if(files.length == 0 || files == null){
return;
}
for(File f : files){
if(f.isFile()){
search(f,key);
} else {
scan(f,key);
}
}
}
private static void search(File f, String key) {
StringBuilder sb = 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);//将每个字符拼接成字符串
sb.append(s);//将读到的字符串拼接到一起最后判断
}
} catch (IOException e){
e.printStackTrace();
}
if(sb.indexOf(key) == -1){
//没有找到
return;
}
System.out.println("找到匹配的文件: "+f.getAbsolutePath());
}
}
上述代码只要文件中包含了关键字的都能被找的,但是这个并不能适用太复杂的目录或者文件太大的情况