浅谈Java中的文件处理 前言 本篇文章面向小白,各位大佬无需浪费时间
今天来总结一下Java中的文件处理api,主要以java.nio.file
包中的api为主(事实上,java.io.file
中的相关api在过去更多的被使用,但nio包中的api技术更新,因此主要展示nio包中的api)
Path api path api 所对应的是文件的地址(可以是绝对的,也可以是相对的),从性质上来讲,它是一个引用。下面是一些简单的实例:
1 2 3 4 Path path1 = Path.of("E:/some/s.txt" ); System.out.println(path1);Path path2 = Path.of("E:/some/" ).resolve("some.txt" ); System.out.println(path2);
(如果你使用的是Java11或更以前的版本,应当使用Path.get
而不是of)
我们可以先看看运行结果:
1 2 E:\some \s.txt E:\some \some .txt
这里其实很难给出一个恰当的对resolve的翻译,你可以简单的理解为在原来的Path上继续增加一段字符串,到后面会有进一步的解释
另外,path.of
还支持另一种使用方法
像这样Path path3 =Path.of("E:","/some","/some.txt");
但我个人对此并不推荐,显然它会使输入更加的麻烦
应当注意的是path只是一个引用 ,他不能实际的确定文件是否存在,应当对此单独做出检查
1 System.out.println(Files.exists(path1));
输出
常见的文件操作 接下来我们将了解一些与文件的增删改查相关的操作
首先是创建目录与文件,这里我们需要使用Files包中相关的方法
1 2 3 4 Path nweDir=Files.createDirectories(Path.of("E:/some" )); System.out.println(nweDir); Path newFile=Files.createFile(nweDir.resolve("newFile.txt" )); System.out.println(newFile);
在这里其实就可以看的出resolve的作用是什么
1 2 E:\some E:\some\newFile.txt
此时目录与文件就被顺利的创建了
另外,其实还存在着创建临时目录与文件的Files.createTempDirectory()
Files.createTempFile()
方法,但同样的,我并不推荐,应为它们虽然被称为临时文件,但并不会在某个时间点自动被删除,与其如此,不如直接使用一般的文件创建方式
接下来我们看一看文件的移动
就像这样
1 2 Files.move(Path.of("E:/some/newFile.txt" ), Path.of("E:/some/s/newFile.txt" )); Files.move(Path.of("E:/some/newFile.txt" ), Path.of("E:/some/s" ));
在移动对应的文件时应当指明其所对应的绝对路径,而不是指明将其移向哪个目录下
当然,如果文件已存在,该操作不会被执行,而是会抛出对应的异常,也可以这样
1 Files.move (Path .of("E:/some/newFile.txt" ), Path .of("E:/some/s/newFile.txt" ),StandardCopyOption.REPLACE_EXISTING);
此时文件将会被强制覆盖
此外,我们应当了解如何获取当前目录下的文件列表
这分为两种情况,
1.仅获取当前目录下的文件,不对子目录的文件做要求
就像这样
1 2 Path dir=Path.of("E:/some" ); Files.list(dir).forEach(System.out::println);
值得一提的是list方法返回的是一个流,你可以对它进行一切流可以进行的操作,下面是输出结果
1 2 E: \some\newFile.txtE: \some\s
2.获取内容包括子目录的文件
1 2 Path dir=Path.of("E:/some" ); Files.walk(dir).forEach(System.out::println);
walk方法会遍历所有的子目录,这是输出结果
1 2 3 4 E:\some E:\some \newFile.txt E:\some \s E:\some \s\newFile2.txt
但我们发现.有时候我们并不需要目录名,而只需要获取文件名,这时可以这样
1 Files.newDirectoryStream(dir,"*.txt" ).forEach(System.out::println);
这样我们就可以只获得对应目录下的TXT文件
然后是目录与文件的删除,这点略有麻烦,java并没有提供删除一个非空目录的方法,而是仅提供了删除空目录的Files.delete
方法,类似于这样
1 2 3 4 5 6 Path dir=Path.of("E:/some/s" ); try { Files.delete(dir); }catch (DirectoryNotEmptyException e){ System.out.println("Directory does not exist" ); }
好吧我收回之前的话,再查了一通资料后我发现java.io.file
包中就有直接的删除目录的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public static boolean deleteDirectory (File directory) { if (!directory.exists()) { System.out.println("目录不存在: " + directory.getAbsolutePath()); return false ; } if (directory.isFile()) { return directory.delete(); } File[] files = directory.listFiles(); if (files != null ) { for (File file : files) { deleteDirectory(file); } } return directory.delete(); } public static void main (String[] args) { String dirPath = "path/to/directory" ; File directory = new File (dirPath); boolean result = deleteDirectory(directory); if (result) { System.out.println("目录删除成功: " + dirPath); } else { System.out.println("目录删除失败: " + dirPath); } }
这是我从网上找到的一段较为标准的删除目录树的方法,只使用了java.io.file
包中的方法,相较于调用java.nio.file.path
中的方法显得可读性更强,也可一看看使用path包的删除流程
1 2 3 4 5 6 7 8 9 10 try (Stream<Path> walk = Files.walk(tmpDir)) { walk.sorted(Comparator.reverseOrder()).forEach(path -> { try { Files.delete(path); } catch (IOException e) { e.printStackTrace(); } }); }
显然,还是java.io.file
中的方法可读性强
在这一部分的最后,我们可以了解一下关于绝对路径与相对路径的关系,我个人推荐在使用过程中尽可能使用绝对路径如E:/some/s
而不是
./s
(这代指当前目录下的s子目录)这样的相对路径,这总会带来奇奇怪怪的bug.所以涉及到的路径的转换方法略去不谈,有需要可以自行寻找
文件的读与写 文件的读写是程序很重要的一部分,但幸运的是,得益于前人的完善,这部分的工作相对比较简单
首先是读取文件
1 2 3 4 Path path=Path.of("E:/some/newFile.txt" ); String s= Files.readString(path);以字符串的形式读取 String s2=Files.readString(path, StandardCharsets.UTF_8); String s3= Arrays.toString(Files.readAllBytes(path));
默认将以utf-8的格式读取,你也可以通过对应的参数指定,就像s2与s3所展示的那样
当然,也可以选择使用流的方式进行读取,就像这样
1 2 BufferedReader bufferedReader = Files.newBufferedReader(utfFile)InputStream is = Files.newInputStream(utfFile)
接下来是文件的写入,同样的非常简单
1 2 Files.write(path,"this is a String" .getBytes(StandardCharsets.UTF_8),StandardOpenOption.APPEND); Files.writeString(path, "this is my string ää öö üü" , StandardCharsets.ISO_8859_1);
写和上面的读一一对应,默认的格式为UTF-8,但你也可以自己指定
不过需要强调的是,
如果对应的path不存在,文件将会被自行创建
在默认模式下,原文件的内容将会被覆盖,可以添加参数StandardOpenOption.APPEND
使添加的内容会在原内容尾被添加,下面是一些可用的(但不是全部)StandardOpenOption,可以了解一下
APPEND
将数据写入文件末尾(追加模式)。如果文件不存在,会抛出异常,除非同时指定了 CREATE
。
CREATE
如果文件不存在,则创建文件。如果文件已存在,则不会影响文件的内容。
CREATE_NEW
如果文件不存在,则创建新文件。如果文件已存在,则抛出 FileAlreadyExistsException
。
DELETE_ON_CLOSE
在关闭文件时删除文件。这通常用于临时文件操作。
同时,流的使用以没有任何问题
1 2 BufferedWriter bufferedWriter = Files.newBufferedWriter(utfFile)OutputStream os = Files.newOutputStream(utfFile)
最后,推荐在有能力的情况下亲自取阅读官方给出的文档,并了解:
内存文件系统
文件变化监测
写在最后 终于写完了!!!
当然,虽然花费了不少的心血,但受限于个人的学识与能力,难免会有各种疏漏之处,还望见谅,如有疑问或发现错误,欢迎通过邮箱联系
老样子,放张图镇楼