JavaDoc:https://javadocs.techempower.com/
一、读取文件
参考链接:https://howtodoinjava.com/java8/read-file-line-by-line-in-java-8-streams-of-lines-example/
在下面的实例中,将使用Stream来一行一行的读取文件。将要读取的文件 data.txt 内容如下:
Never
store
password
except
in mind.
将上面的文件一行一行的读取,如果有一行包含 password ,将这行打印出来
1. Java 8 Stream按行读取文件
利用Java 8 Stream按行读取文件内容,过滤包含 password 的行,并取出第一条打印出来
private static void readStreamOfLinesUsingFiles() throws IOException
{
Stream<String> lines = Files.lines(Paths.get("c:/temp", "data.txt"));
Optional<String> hasPassword = lines.filter(s -> s.contains("password")).findFirst();
if(hasPassword.isPresent())
{
System.out.println(hasPassword.get());
}
//Close the stream and it's underlying file as well
lines.close();
}
2. FileReader 按行读取文件
直到java7,可采用下面的方式来读取文件,实现方法有很多,但不是本文的重点,只是与上面的例子对比来看
private static void readLinesUsingFileReader() throws IOException
{
File file = new File("c:/temp/data.txt");
FileReader fr = new FileReader(file);
BufferedReader br = new BufferedReader(fr);
String line;
while((line = br.readLine()) != null)
{
if(line.contains("password")){
System.out.println(line);
}
}
br.close();
fr.close();
}
3. try-with-resources模式自动关闭流
第一个例子已经能够满足我们在应用程序中逐行读取文件的需要了,但如果你还想让它变得更好,那么我们可以使用 try-with-resources 的方式,这样会省去最后关闭流 close() 的操作。
private static void readStreamOfLinesUsingFilesWithTryBlock() throws IOException
{
Path path = Paths.get("c:/temp", "data.txt");
//The stream hence file will also be closed here
try(Stream<String> lines = Files.lines(path))
{
Optional<String> hasPassword = lines.filter(s -> s.contains("password")).findFirst();
if(hasPassword.isPresent()){
System.out.println(hasPassword.get());
}
}
}
当一个Stream产生另外一个Stream的时候,close 方法会链式调用(它下面的Stream也会调用),可以采用下面的方法编写:
private static void readStreamOfLinesUsingFilesWithTryBlock() throws IOException
{
Path path = Paths.get("c:/temp", "data.txt");
//When filteredLines is closed, it closes underlying stream as well as underlying file.
try(Stream<String> filteredLines = Files.lines(path).filter(s -> s.contains("password")))
{
Optional<String> hasPassword = filteredLines.findFirst();
if(hasPassword.isPresent()){
System.out.println(hasPassword.get());
}
}
}
你想测试一下它下面的Stream的有没有触发 close ,可以用 onClose 来测试一下
private static void readStreamOfLinesUsingFilesWithTryBlock() throws IOException
{
Path path = Paths.get("c:/temp", "data.txt");
//When filteredLines is closed, it closes underlying stream as well as underlying file.
try(Stream<String> filteredLines = Files.lines(path)
//test if file is closed or not
.onClose(() -> System.out.println("File closed"))
.filter(s -> s.contains("password"))){
Optional<String> hasPassword = filteredLines.findFirst();
if(hasPassword.isPresent()){
System.out.println(hasPassword.get());
}
}
}
输出
password
File closed
二、写入文件
参考链接:https://howtodoinjava.com/java8/java-8-write-to-file-example/
1. Java 8使用BufferedWriter写入文件
BufferedWriter 用于将文本写入字符或字节流。在打印字符之前,它将字符存储在缓冲区中,并批量打印。如果没有缓冲,每次调用 print () 方法都会导致字符转换为字节,然后立即写入文件,效率会很低。
//Get the file reference
Path path = Paths.get("c:/output.txt");
//Use try-with-resource to get auto-closeable writer instance
try (BufferedWriter writer = Files.newBufferedWriter(path))
{
writer.write("Hello World !!");
}
2. 使用Files.write()写入文件
使用 Files.write() 也很漂亮简洁.
String content = "Hello World !!";
Files.write(Paths.get("c:/output.txt"), content.getBytes());
三、遍历迭代目录
参考链接:https://howtodoinjava.com/java8/java-8-list-all-files-example/
学习使用Java 8 API,例如Files.list()
和DirectoryStream
,并递归列出目录中存在的所有文件,包括隐藏文件。
对于使用外部迭代(用于循环),请使用
DirectoryStream
。要使用Stream API操作(映射,过滤,排序,收集),请Files.list()
改用。
1. Files.list()–遍历所有文件和子目录
Files.list() 方法 遍历当前目录下所有的文件和子目录
Files.list(Paths.get("."))
.forEach(System.out::println);
// 或者
List<File> files = Files.list(Paths.get(dirLocation))
.map(Path::toFile)
.collect(Collectors.toList());
files.forEach(System.out::println);
Output:
.\filename1.txt
.\directory1
.\filename2.txt
.\Employee.java
2. Files.list()–仅列出不包括子目录的文件
使用过滤器表达式 Files::isRegularFile
检查文件是否是普通文件,来过滤掉子目录,保留文件并打印
Files.list(Paths.get("."))
.filter(Files::isRegularFile)
.forEach(System.out::println);
// 或者
List<File> files = Files.list(Paths.get("."))
.filter(Files::isRegularFile)
.map(Path::toFile)
.collect(Collectors.toList());
files.forEach(System.out::println);
Output:
.\filename1.txt
.\filename2.txt
.\Employee.java
3. Files.newDirectoryStream()–列出所有文件和子目录
Java提供了一个更加灵活遍历目录内容的方式:Files.newDirectoryStream()
注意,如果对一个大的目录进行操作,使用
DirectoryStream
将提高代码的运行速度
Files.newDirectoryStream(Paths.get("."))
.forEach(System.out::println);
Output:
.\filename1.txt
.\directory1
.\filename2.txt
.\Employee.java
4. Files.newDirectoryStream()–仅迭代不包含子目录的文件
只遍历文件,排除掉目录,通过path filter的第二个参数来控制
Files.newDirectoryStream(Paths.get("."), path -> path.toFile().isFile())
.forEach(System.out::println);
Output:
.\filename1.txt
.\filename2.txt
.\Employee.java
5. 仅列出一定范围的所有文件
要仅获取某些扩展名的所有文件的列表,请同时使用两个谓词 Files::isRegularFile
和path.toString().endsWith(".java")
。
使用上述谓词,我们将列出.java
文件夹中的所有文件。
Files.newDirectoryStream(Paths.get("."),
path -> path.toString().endsWith(".java"))
.forEach(System.out::println);
// 或者
List<File> files = Files.list(Paths.get(dirLocation))
.filter(Files::isRegularFile)
.filter(path -> path.toString().endsWith(".java"))
.map(Path::toFile)
.collect(Collectors.toList());
files.forEach(System.out::println);
Output:
.\Employee.java
6. 查找目录中的所有隐藏文件
要查找所有隐藏文件,可以file -> file.isHidden()
在上述任何示例中使用过滤器表达式。
final File[] files = new File(".").listFiles(file -> file.isHidden());
// 或者 方法引用写法
final File[] files = new File(".").listFiles(File::isHidden);
// 或者 使用Files工具类
List<File> files = Files.list(Paths.get("."))
.filter(path -> path.toFile().isHidden())
.map(Path::toFile)
.collect(Collectors.toList());
四、监视目录,子目录和文件中的更改
参考链接:https://howtodoinjava.com/java8/java-8-watchservice-api-tutorial/
在此将使用 Java 7 WatchService
API观察目录及其中的所有子目录和文件。WatchService
是jdk1.7版本引进的,位于 java.nio.file 包下。WatchService
是基于本机操作系统实现对文件的监控。
1. 如何注册WatchService
要注册WatchService
,请获取目录路径并使用path.register()
方法。
Path path = Paths.get(".");
WatchService watchService = path.getFileSystem().newWatchService();
path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
2. 观察变化事件
要获取目录及其中文件的更改,请使用watchKey.pollEvents()
方法,该方法以流的形式返回所有更改事件的集合。
WatchKey watchKey = null;
while (true) {
watchKey = watchService.poll(10, TimeUnit.MINUTES);
if(watchKey != null) {
watchKey.pollEvents().stream().forEach(event -> System.out.println(event.context()));
}
watchKey.reset();
}
该密钥一直有效,直到:
- 通过调用它的
cancel
方法显式地取消它,或者 - 隐式取消,因为该对象不再可访问,或者
- 通过关闭手表服务。
如果要在循环中多次重复使用同一键来获取更改事件,请不要忘记调用 watchKey.reset()
将键重新设置为就绪状态的方法。
请注意,诸如如何检测事件,其及时性以及是否保留其顺序之类的几件事高度依赖于底层操作系统。某些更改可能导致一个操作系统中的单个条目,而类似的更改可能导致另一操作系统中的多个事件。
3. 监视目录,子目录和文件中的更改示例
在此示例中,我们将看到一个观看目录的示例,该目录中包含所有子目录和文件。我们将维护监视键和目录的映射,Map<WatchKey, Path> keys
以正确识别已修改的目录。
下面的方法将向观察者注册一个path,然后将path和key存储在map中。
private void registerDirectory(Path dir) throws IOException
{
WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
keys.put(key, dir);
}
在遍历目录结构并为遇到的每个目录调用此方法时,将递归调用此方法。
private void walkAndRegisterDirectories(final Path start) throws IOException {
// register directory and sub-directories
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
registerDirectory(dir);
return FileVisitResult.CONTINUE;
}
});
}
请注意,无论何时创建新目录,我们都会在 watchservice
中注册该目录,并将新key添加到map中。
WatchEvent.Kind kind = event.kind();
if (kind == ENTRY_CREATE) {
try {
if (Files.isDirectory(child)) {
walkAndRegisterDirectories(child);
}
} catch (IOException x) {
// do something useful
}
}
将以上所有内容与处理事件的逻辑放在一起,完整的示例如下所示:
import static java.nio.file.StandardWatchEventKinds.*;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;
public class Java8WatchServiceExample {
private final WatchService watcher;
private final Map<WatchKey, Path> keys;
/**
* Creates a WatchService and registers the given directory
*/
Java8WatchServiceExample(Path dir) throws IOException {
this.watcher = FileSystems.getDefault().newWatchService();
this.keys = new HashMap<WatchKey, Path>();
walkAndRegisterDirectories(dir);
}
/**
* Register the given directory with the WatchService; This function will be called by FileVisitor
*/
private void registerDirectory(Path dir) throws IOException
{
WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
keys.put(key, dir);
}
/**
* Register the given directory, and all its sub-directories, with the WatchService.
*/
private void walkAndRegisterDirectories(final Path start) throws IOException {
// register directory and sub-directories
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
registerDirectory(dir);
return FileVisitResult.CONTINUE;
}
});
}
/**
* Process all events for keys queued to the watcher
*/
void processEvents() {
for (;;) {
// wait for key to be signalled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
Path dir = keys.get(key);
if (dir == null) {
System.err.println("WatchKey not recognized!!");
continue;
}
for (WatchEvent<?> event : key.pollEvents()) {
@SuppressWarnings("rawtypes")
WatchEvent.Kind kind = event.kind();
// Context for directory entry event is the file name of entry
@SuppressWarnings("unchecked")
Path name = ((WatchEvent<Path>)event).context();
Path child = dir.resolve(name);
// print out event
System.out.format("%s: %s\n", event.kind().name(), child);
// if directory is created, and watching recursively, then register it and its sub-directories
if (kind == ENTRY_CREATE) {
try {
if (Files.isDirectory(child)) {
walkAndRegisterDirectories(child);
}
} catch (IOException x) {
// do something useful
}
}
}
// reset key and remove from set if directory no longer accessible
boolean valid = key.reset();
if (!valid) {
keys.remove(key);
// all directories are inaccessible
if (keys.isEmpty()) {
break;
}
}
}
}
public static void main(String[] args) throws IOException {
Path dir = Paths.get("c:/temp");
new Java8WatchServiceExample(dir).processEvents();
}
}
运行该程序,并在给定输入目录中,新增更改文件或者目录后,您将在控制台中注意到捕获的事件。
输出:
ENTRY_CREATE:c:\ temp \ New文件夹
ENTRY_DELETE:c:\ temp \ New文件夹
ENTRY_CREATE:c:\ temp \ data
ENTRY_CREATE:c:\ temp \ data \ New Text Document.txt
ENTRY_MODIFY:c:\ temp \ data
ENTRY_DELETE:c:\ temp \ data \ New Text Document.txt
ENTRY_CREATE:c:\ temp \ data \ tempFile.txt
ENTRY_MODIFY:c:\ temp \ data
ENTRY_MODIFY:c:\ temp \ data \ tempFile.txt
ENTRY_MODIFY:c:\ temp \ data \ tempFile.txt
ENTRY_MODIFY:c:\ temp \ data \ tempFile.txt
WatchService
需要牢记两件事:
WatchService
不会为监视目录的子目录拾取事件。- 我们仍然需要轮询
WatchService
的事件,而不是接收异步通知。 WatchService
能立即监控到文件的改动。 如果事件是创建,删除或更新,并且该事件相对于监视目录,则WatchEvent.context
方法将返回Path对象。 重要的是要知道,当收到事件时,不能保证执行该操作的程序已经完成,因此可能需要一定程度的协调,比如判断文件操作成功。