是时候摆脱所有笨拙的代码并使用 Java 8 了!这个答案还包括Path
class 已经是 Java 7 的一部分,但在 Java 8 中似乎得到了很大的改进。
代码:
private void init() throws IOException {
Path directory = Paths.get("C:\\Users\\Frank\\Downloads\\testjob");
Files.list(directory)
.filter(path -> Files.isRegularFile(path))
.filter(path -> path.getFileName().toString().startsWith("job"))
.filter(path -> path.getFileName().toString().endsWith(".script"))
.sorted(Comparator.comparingInt(this::pathToInt))
.map(path -> path.getFileName())
.forEach(System.out::println);
}
private int pathToInt(final Path path) {
return Integer.parseInt(path.getFileName()
.toString()
.replace("job", "")
.replace(".script", "")
);
}
的解释pathToInt
:
- 从给定的
Path
,获取文件的字符串表示形式。
- Remove
"job"
and ".script"
.
- 尝试将字符串解析为整数。
的解释init
,主要方法:
- 获得一个
Path
到文件所在的目录。
- 获取惰性填充列表
Path
目录中的 s,请注意:这些Path
还是完全有资格的!
- 保留常规文件。
- 保留最后部分的文件
Path
,因此是文件名(例如job1.script
) 以。。开始"job"
. Be aware您需要首先获得的字符串表示Path
在你可以检查它之前,否则你将检查整个Path
从一个名为的目录开始"job"
.
- 对以以下结尾的文件执行相同操作
".script"
.
- 现在有趣的地方来了。这里我们根据a对文件列表进行排序
Comparator
比较我们通过调用获得的整数pathToInt
on the Path
。这里我使用的是方法参考,方法comparingInt(ToIntFunction<? super T> keyExtractor
需要一个映射 a 的函数T
,在这种情况下Path
,到一个整数。这正是pathToInt
确实如此,因此它可以用作方法参考。
- 然后我映射每个
Path
to the Path
仅由文件名组成。
- 最后,对于每个元素
Stream<Path>
, 我打电话System.out.println(Path.toString())
.
看起来这段代码可以写得更容易,但我故意把它写得更详细。我这里的设计是保持完整Path
始终完好无损,代码的最后一部分forEach
实际上违反了该原则,因为在它仅映射到文件名之前不久,因此您无法处理完整的内容Path
稍后再讲。
该代码还被设计为快速失败,因此它期望文件以以下形式存在job(\D+).script
,并且会抛出一个NumberFormatException
如果情况并非如此。
输出示例:
job1.script
job2.script
job10.script
job11.script
可以说,更好的替代方案具有正则表达式的强大功能:
private void init() throws IOException {
Path directory = Paths.get("C:\\Users\\Frank\\Downloads\\testjob");
Files.list(directory)
.filter(path -> Files.isRegularFile(path))
.filter(path -> path.getFileName().toString().matches("job\\d+.script"))
.sorted(Comparator.comparingInt(this::pathToInt))
.map(path -> path.getFileName())
.forEach(System.out::println);
}
private int pathToInt(final Path path) {
return Integer.parseInt(path.getFileName()
.toString()
.replaceAll("job(\\d+).script", "$1")
);
}
这里我使用正则表达式"job\\d+.script"
,它匹配以以下开头的字符串"job"
,后跟一位或多位数字,然后是“.script”。
我使用几乎相同的表达方式pathToInt
方法,但是我使用了捕获组、括号和$1
使用该捕获组。
我还将提供一种简洁的方法来读取一个大文件中的文件内容,正如您在问题中所问的那样:
private void init() throws IOException {
Path directory = Paths.get("C:\\Users\\Frank\\Downloads\\testjob");
try (BufferedWriter writer = Files.newBufferedWriter(directory.resolve("masterjob.script"))) {
Files.list(directory)
.filter(path -> Files.isRegularFile(path))
.filter(path -> path.getFileName().toString().matches("job\\d+.script"))
.sorted(Comparator.comparingInt(this::pathToInt))
.flatMap(this::wrappedLines)
.forEach(string -> wrappedWrite(writer, string));
}
}
private int pathToInt(final Path path) {
return Integer.parseInt(path.getFileName()
.toString()
.replaceAll("job(\\d+).script", "$1")
);
}
private Stream<String> wrappedLines(final Path path) {
try {
return Files.lines(path);
} catch (IOException ex) {
//swallow
return null;
}
}
private void wrappedWrite(final BufferedWriter writer, final String string) {
try {
writer.write(string);
writer.newLine();
} catch (IOException ex) {
//swallow
}
}
请注意,lambda 不能抛出/捕获检查Exception
s,因此有必要在代码周围编写包装器,以决定如何处理错误。吞掉异常很少是一个好主意,我只是在这里简单地使用它来编写代码。
这里真正的大变化是,我没有打印出名称,而是将每个文件映射到其内容并将它们写入文件中。