File
. 您可以在这里阅读有关他的作品。但在 Java 7 中,该语言的创建者决定改变他们处理文件和目录的方式。这是因为该课程File
有许多缺点。例如,它没有一种方法copy()
可以让您将文件从一个位置复制到另一个位置(看似显然需要的功能)。此外,该类File
还有很多返回boolean
值的方法。如果发生错误,此类方法将返回false而不是抛出异常,这使得诊断错误并确定其原因变得非常困难。出现的不是单个类,而是File
多达 3 个类:Paths
、Path
和Files
。嗯,准确地说,Path
这是一个接口,而不是一个类。让我们弄清楚它们之间有何不同以及为什么需要它们。让我们从最简单的事情开始—— Paths
。
路径
Paths
是一个非常简单的类,只有一个静态方法get()
。它的创建只是为了从传递的 string 或 URI 中获取类型的对象Path
。它没有其他功能。这是他的工作示例:
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) {
Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt");
}
}
不是最难的课程,对吧?:) 好吧,既然我们得到了一个类型为 的对象Path
,那么让我们弄清楚它是什么Path
以及为什么需要它:)
小路
Path
总的来说,是重新设计的File
. 与 一起工作比与 一起工作要容易得多File
。 首先,许多实用(静态)方法被从中删除并移至类中Files
。 其次,Path
方法的返回值是有序的。在类中,File
方法返回了这个String
、那个boolean
、那个File
——这并不容易弄清楚。例如,有一个方法getParent()
以字符串形式返回当前文件的父路径。但同时有一个方法getParentFile()
返回相同的东西,但以对象的形式File
!这显然是多余的。因此,在接口中,Path
该方法getParent()
和其他处理文件的方法只是返回一个对象Path
。没有太多的选择——一切都很简单。 它有哪些有用的方法呢Path
? 以下是其中的一些以及他们的工作示例:
-
getFileName()
— 从路径返回文件名; -
getParent()
— 返回相对于当前路径的“父”目录(即目录树中较高的目录); -
getRoot()
— 返回“根”目录;即位于目录树顶部的那个; -
startsWith()
,endsWith()
— 检查路径是否以传递的路径开始/结束:import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt"); Path fileName = testFilePath.getFileName(); System.out.println(fileName); Path parent = testFilePath.getParent(); System.out.println(parent); Path root = testFilePath.getRoot(); System.out.println(root); boolean endWithTxt = testFilePath.endsWith("Desktop\\testFile.txt"); System.out.println(endWithTxt); boolean startsWithLalala = testFilePath.startsWith("lalalala"); System.out.println(startsWithLalala); } }
控制台输出:
testFile.txt
C:\Users\用户名\桌面
C:\
true
false注意该方法是如何运作的
endsWith()
。它检查当前路径是否以传递的路径结束。它位于路径上,而不是字符集上。比较这两个调用的结果:
import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt"); System.out.println(testFilePath.endsWith("estFile.txt")); System.out.println(testFilePath.endsWith("Desktop\\testFile.txt")); } }
控制台输出:
假
真您需要将完整路径传递给方法
endsWith()
,而不仅仅是一组字符:否则结果将始终为false,即使当前路径实际上以这样的字符序列结尾(如“estFile.txt”的情况) ”在上面的例子中)。此外,还有
Path
一组方法可以简化绝对(完整)和相对路径的使用。
-
boolean isAbsolute()
—如果当前路径是绝对路径,则返回true :import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath = Paths.get("C:\\Users\\Username\\Desktop\\testFile.txt"); System.out.println(testFilePath.isAbsolute()); } }
控制台输出:
真的
-
Path normalize()
—“规范化”当前路径,从中删除不必要的元素。您可能知道,流行的操作系统在表示路径时经常使用“.”字符。(“当前目录”)和“..”(父目录)。例如:“ ./Pictures/dog.jpg ”表示我们现在所在的目录下有一个Pictures文件夹,里面有一个文件“dog.jpg”所以就是这样。如果您的程序有一个使用“.”的路径 或“..”,该方法
normalize()
将删除它们并获取不包含它们的路径:import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path path5 = Paths.get("C:\\Users\\Java\\.\\examples"); System.out.println(path5.normalize()); Path path6 = Paths.get("C:\\Users\\Java\\..\\examples"); System.out.println(path6.normalize()); } }
控制台输出:
C:\Users\Java\examples
C:\Users\examples -
Path relativize()
— 计算当前路径和传递路径之间的相对路径。例如:
import java.nio.file.Path; import java.nio.file.Paths; public class Main { public static void main(String[] args) { Path testFilePath1 = Paths.get("C:\\Users\\Users\\Users\\Users"); Path testFilePath2 = Paths.get("C:\\Users\\Users\\Users\\Users\\Username\\Desktop\\testFile.txt"); System.out.println(testFilePath1.relativize(testFilePath2)); } }
控制台输出:
用户名\桌面\testFile.txt
Path
相当大。您可以在Oracle 文档中找到它们。我们将继续进行审查Files
。
文件
Files
- 这是一个实用程序类,其中移动了类中的静态方法File
。- 这与orFiles
大致相同,只是它适用于文件,而不适用于数组和集合:) 它专注于管理文件和目录。使用静态方法,我们可以创建、删除和移动文件和目录。对于这些操作,使用方法(对于目录 - )和。以下是如何使用它们: Arrays
Collections
Files
createFile()
createDirectory()
move()
delete()
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
public class Main {
public static void main(String[] args) throws IOException {
//file creation
Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
System.out.println("Was the file created successfully?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
// create directory
Path testDirectory = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory"));
System.out.println("Was the directory successfully created?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory")));
//move file from desktop to testDirectory. You need to move with the name of the file in the folder!
testFile1 = Files.move(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt"), REPLACE_EXISTING);
System.out.println("Is our file left on the desktop?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
System.out.println("Has our file been moved to testDirectory?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));
//remove file
Files.delete(testFile1);
System.out.println("Does the file still exist?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory\\testFile111.txt")));
}
}
Files.createFile()
这里我们首先在桌面上 创建一个文件(方法),然后在那里创建一个文件夹(方法Files.createDirectory()
)。之后,我们将文件(method Files.move()
)从桌面移动到这个新文件夹,最后我们删除该文件(method Files.delete()
)。 控制台输出: 文件创建成功吗?true 目录创建成功吗?true 我们的文件还在桌面上吗?false 我们的文件是否已移至 testDirectory?true 文件是否仍然存在?错误的 注意:就像接口方法一样Path
,许多方法Files
返回一个对象Path
。大多数类方法Files
也接受Path
. 这里有一个方法将成为你忠实的助手Paths.get()
——积极使用它。还有什么有趣的地方Files
?旧类真正缺少的是!File
方法。copy()
我们在讲座一开始就谈到了他,现在是时候认识他了!
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
public class Main {
public static void main(String[] args) throws IOException {
//file creation
Path testFile1 = Files.createFile(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt"));
System.out.println("Was the file created successfully?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
// create directory
Path testDirectory2 = Files.createDirectory(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2"));
System.out.println("Was the directory successfully created?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2")));
//copy the file from the desktop to the directory testDirectory2.
testFile1 = Files.copy(testFile1, Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt"), REPLACE_EXISTING);
System.out.println("Is our file left on the desktop?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testFile111.txt")));
System.out.println("Has our file been copied to testDirectory?");
System.out.println(Files.exists(Paths.get("C:\\Users\\Username\\Desktop\\testDirectory2\\testFile111.txt")));
}
}
控制台输出: 文件创建成功吗?true 目录创建成功吗?true 我们的文件还在桌面上吗?true 我们的文件是否已复制到 testDirectory ?true 现在您可以通过编程方式复制文件!:) 但是该类Files
不仅允许您管理文件本身,还允许您使用其内容。要将数据写入文件,它有一个方法write()
,并且有一个方法用于读取 - 多达 3 个:read()
,readAllBytes()
我们readAllLines()
将详细讨论后者。为什么要上它?因为它有一个非常有趣的返回类型 - List<String>
! 也就是说,它返回文件中的行列表。当然,这使得处理内容变得非常方便,因为整个文件可以逐行输出到控制台,例如定期循环for
:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import static java.nio.charset.StandardCharsets.UTF_8;
public class Main {
public static void main(String[] args) throws IOException {
List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);
for (String s: lines) {
System.out.println(s);
}
}
}
控制台输出: 我记得一个美妙的时刻:你出现在我面前,像转瞬即逝的幻象,像一个纯粹美丽的天才。 很舒服!:)这个特性在Java 7中出现。在Java 8中,出现了Stream API,它为Java添加了一些函数式编程的元素。包括更丰富的文件管理功能。想象一下,我们有一个任务:找到文件中以“How”一词开头的所有行,将它们转换为大写并将它们输出到控制台。在 Java 7 中使用类的解决方案是什么Files
样的?像这样的东西:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import static java.nio.charset.StandardCharsets.UTF_8;
public class Main {
public static void main(String[] args) throws IOException {
List<String> lines = Files.readAllLines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"), UTF_8);
List<String> result = new ArrayList<>();
for (String s: lines) {
if (s.startsWith("How")) {
String upper = s.toUpperCase();
result.add(upper);
}
}
for (String s: result) {
System.out.println(s);
}
}
}
控制台输出: LIKE A FASTING VISION, LIKE A GENIUS OF PURE BEAUTY。 我们似乎已经做到了,但是您不认为对于这样一个简单的任务,我们的代码有点……冗长吗?使用 Java 8 Stream API,解决方案看起来更加优雅:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) throws IOException {
Stream<String> stream = Files.lines(Paths.get("C:\\Users\\Username\\Desktop\\pushkin.txt"));
List<String> result = stream
.filter(line -> line.startsWith("How"))
.map(String::toUpperCase)
.collect(Collectors.toList());
result.forEach(System.out::println);
}
}
我们取得了相同的结果,但代码少得多!而且,也不能说我们输在了“可读性”上。我认为即使您不熟悉 Stream API,您也可以轻松评论这段代码的作用。但简而言之,Stream是一个元素序列,您可以在其中执行不同的功能。我们从方法中获取 Stream 对象Files.lines()
,然后对其应用 3 个函数:
-
使用该方法,
filter()
我们仅从文件中选择以“How”开头的那些行。 -
我们使用该方法遍历所有选定的行
map()
,并将它们中的每一行都变为大写。 -
我们将所有生成的行组合起来
List
使用collect()
.
Files.walkFileTree()
. 这就是我们需要做的。首先,我们需要FileVisitor
. FileVisitor
是一个特殊的接口,描述了遍历文件树的所有方法。具体来说,我们将在那里放置逻辑来读取文件的内容并检查它是否包含我们需要的文本。这就是我们的样子FileVisitor
:
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
public class MyFileVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
List<String> lines = Files.readAllLines(file);
for (String s: lines) {
if (s.contains("This is the file we need")) {
System.out.println("Required file found!");
System.out.println(file.toAbsolutePath());
break;
}
}
return FileVisitResult.CONTINUE;
}
}
在这种情况下,我们的类继承自SimpleFileVisitor
. 这是一个实现 的类FileVisitor
,您只需重写其中一个方法:visitFile()
。这里我们描述了每个目录中的每个文件需要做什么。如果您需要更复杂的遍历逻辑,您应该编写自己的实现FileVisitor
。在那里你需要实现另外 3 个方法:
-
preVisitDirectory()
— 进入文件夹之前必须执行的逻辑; -
visitFileFailed()
— 如果无法输入文件(无法访问或其他原因)该怎么办; -
postVisitDirectory()
——进入文件夹后必须执行的逻辑。
SimpleFileVisitor
。该方法内部的逻辑visitFile()
非常简单:从文件中读取所有行,检查它们是否包含我们需要的内容,如果包含,则将绝对路径打印到控制台。唯一可能给你带来麻烦的一行是这一行:
return FileVisitResult.CONTINUE;
其实,一切都很简单。这里我们简单描述一下文件输入并完成所有必要的操作后程序应该做什么。在我们的例子中,我们需要继续遍历树,所以我们选择选项CONTINUE
。但是,例如,我们可能有另一项任务:不是找到所有包含“这是我们需要的文件”的文件,而是只找到一个这样的文件。此后,必须终止该程序。在这种情况下,我们的代码看起来完全相同,但不是中断;会:
return FileVisitResult.TERMINATE;
好吧,让我们运行我们的代码,看看它是否有效。
import java.io.IOException;
import java.nio.file.*;
public class Main {
public static void main(String[] args) throws IOException {
Files.walkFileTree(Paths.get("C:\\Users\\Username\\Desktop\\testFolder"), new MyFileVisitor());
}
}
控制台输出: 已找到所需文件!C:\Users\用户名\Desktop\testFolder\FileWeNeed1.txt 找到所需文件!C:\Users\用户名\Desktop\testFolder\level1-a\level2-aa\FileWeNeed2.txt 找到所需文件!C:\Users\用户名\Desktop\testFolder\level1-b\level2-bb\FileWeNeed3.txt 太好了,我们做到了!:) 如果你想了解更多walkFileTree()
,我推荐你这篇文章。您还可以完成一项小任务 - 将其替换SimpleFileVisitor
为常规任务FileVisitor
,实现所有 4 种方法并为该程序提出一个目的。例如,您可以编写一个程序来记录您的所有操作:在输入文件或文件夹之前/之后在控制台中显示文件或文件夹的名称。就这样 - 稍后见!:)
GO TO FULL VERSION