Skip to content

Commit 0c1674d

Browse files
author
Dragon Dove
committed
完成“文件”章节翻译
1 parent c68e16e commit 0c1674d

File tree

1 file changed

+195
-0
lines changed

1 file changed

+195
-0
lines changed

docs/book/17-Files.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,15 +644,210 @@ evt.kind(): ENTRY_DELETE
644644

645645
<!-- Finding Files -->
646646
## 文件查找
647+
到目前为止,为了找到文件,我们一直使用相当粗糙的方法,在 `path` 上调用 `toString()`,然后使用 `string` 操作查看结果。事实证明,`java.nio.file` 有更好的解决方案:通过在 `FileSystem` 对象上调用 `getPathMatcher()` 获得一个 `PathMatcher`,然后传入您感兴趣的模式。模式有两个选项:`glob``regex``glob` 比较简单,实际上功能非常强大,因此您可以使用 `glob` 解决许多问题。如果您的问题更复杂,可以使用 `regex`,这将在接下来的 `Strings` 一章中解释。
647648

649+
在这里,我们使用 `glob` 查找以 `.tmp``.txt` 结尾的所有 `Path`
650+
651+
```java
652+
// files/Find.java
653+
// {ExcludeFromGradle}
654+
import java.nio.file.*;
655+
656+
public class Find {
657+
public static void
658+
main(String[] args) throws Exception {
659+
Path test = Paths.get("test");
660+
Directories.refreshTestDir();
661+
Directories.populateTestDir();
662+
// Creating a *directory*, not a file:
663+
Files.createDirectory(test.resolve("dir.tmp"));
664+
665+
PathMatcher matcher = FileSystems.getDefault()
666+
.getPathMatcher("glob:**/*.{tmp,txt}");
667+
Files.walk(test)
668+
.filter(matcher::matches)
669+
.forEach(System.out::println);
670+
System.out.println("***************");
671+
672+
PathMatcher matcher2 = FileSystems.getDefault()
673+
.getPathMatcher("glob:*.tmp");
674+
Files.walk(test)
675+
.map(Path::getFileName)
676+
.filter(matcher2::matches)
677+
.forEach(System.out::println);
678+
System.out.println("***************");
679+
680+
Files.walk(test) // Only look for files
681+
.filter(Files::isRegularFile)
682+
.map(Path::getFileName)
683+
.filter(matcher2::matches)
684+
.forEach(System.out::println);
685+
}
686+
}
687+
/* Output:
688+
test\bag\foo\bar\baz\5208762845883213974.tmp
689+
test\bag\foo\bar\baz\File.txt
690+
test\bar\baz\bag\foo\7918367201207778677.tmp
691+
test\bar\baz\bag\foo\File.txt
692+
test\baz\bag\foo\bar\8016595521026696632.tmp
693+
test\baz\bag\foo\bar\File.txt
694+
test\dir.tmp
695+
test\foo\bar\baz\bag\5832319279813617280.tmp
696+
test\foo\bar\baz\bag\File.txt
697+
***************
698+
5208762845883213974.tmp
699+
7918367201207778677.tmp
700+
8016595521026696632.tmp
701+
dir.tmp
702+
5832319279813617280.tmp
703+
***************
704+
5208762845883213974.tmp
705+
7918367201207778677.tmp
706+
8016595521026696632.tmp
707+
5832319279813617280.tmp
708+
*/
709+
```
710+
711+
`matcher` 中,`glob` 表达式开头的 `**/` 表示“当前目录及所有子目录”,这在当你不仅仅要匹配当前目录下特定结尾的 `Path` 时非常有用。单 `*` 表示“任何东西”,然后是一个点,然后大括号表示一系列的可能性---我们正在寻找以`.tmp``.txt` 结尾的东西。您可以在 `getPathMatcher()` 文档中找到更多详细信息。
712+
713+
`matcher2` 只使用 `*.tmp`,通常不匹配任何内容,但是添加 `map()` 操作会将完整路径减少到末尾的名称。
714+
715+
注意,在这两种情况下,输出中都会出现 `dir.tmp`,即使它是一个目录而不是一个文件。要只查找文件,必须像在最后 `files.walk()` 中那样对其进行筛选。
648716

649717
<!-- Reading & Writing Files -->
650718
## 文件读写
719+
此时,我们可以对路径和目录做任何事情。 现在让我们看一下操纵文件本身的内容。
720+
721+
如果一个文件很“小”,也就是说“它运行得足够快且占用内存小”,那么 `java.nio.file.Files` 类中的实用程序将帮助你轻松读写文本和二进制文件。
722+
723+
`Files.readAllLines()` 一次读取整个文件(因此,“小”文件很有必要),产生一个`List<String>`。 对于示例文件,我们将重用`streams/Cheese.dat`
724+
725+
```java
726+
// files/ListOfLines.java
727+
import java.util.*;
728+
import java.nio.file.*;
729+
730+
public class ListOfLines {
731+
public static void
732+
main(String[] args) throws Exception {
733+
Files.readAllLines(
734+
Paths.get("../streams/Cheese.dat"))
735+
.stream()
736+
.filter(line -> !line.startsWith("//"))
737+
.map(line ->
738+
line.substring(0, line.length()/2))
739+
.forEach(System.out::println);
740+
}
741+
}
742+
/* Output:
743+
Not much of a cheese
744+
Finest in the
745+
And what leads you
746+
Well, it's
747+
It's certainly uncon
748+
*/
749+
```
750+
751+
跳过注释行,其余的内容每行只打印一半。 这实现起来很简单:你只需将 `Path` 传递给 `readAllLines()` (以前的 java 实现这个功能很复杂)。 有一个 `readAllLines()` 的重载版本,它包含一个 `Charset` 参数来存储文件的Unicode编码。
752+
753+
`Files.write()` 被重载以写入 `byte` 数组或任何 `Iterable` 对象(它也有 `Charset` 选项):
754+
755+
```java
756+
// files/Writing.java
757+
import java.util.*;
758+
import java.nio.file.*;
759+
760+
public class Writing {
761+
static Random rand = new Random(47);
762+
static final int SIZE = 1000;
763+
public static void
764+
main(String[] args) throws Exception {
765+
// Write bytes to a file:
766+
byte[] bytes = new byte[SIZE];
767+
rand.nextBytes(bytes);
768+
Files.write(Paths.get("bytes.dat"), bytes);
769+
System.out.println("bytes.dat: " +
770+
Files.size(Paths.get("bytes.dat")));
771+
772+
// Write an iterable to a file:
773+
List<String> lines = Files.readAllLines(
774+
Paths.get("../streams/Cheese.dat"));
775+
Files.write(Paths.get("Cheese.txt"), lines);
776+
System.out.println("Cheese.txt: " +
777+
Files.size(Paths.get("Cheese.txt")));
778+
}
779+
}
780+
/* Output:
781+
bytes.dat: 1000
782+
Cheese.txt: 199
783+
*/
784+
```
785+
786+
我们使用 `Random` 来创建一个随机的 `byte` 数组; 你可以看到生成的文件大小是1000。
787+
788+
一个 `List` 被写入文件,任何 `Iterable` 对象也可以这么做。
789+
790+
如果文件大小有问题怎么办? 比如说:
791+
792+
1.文件太大,如果你一次读完整个文件,你可能会耗尽内存。
793+
794+
2.您只需要在文件的中途工作以获得所需的结果,因此读取整个文件会浪费时间。
795+
796+
`Files.lines()` 方便地将文件转换为行的 `Stream`
797+
798+
```java
799+
// files/ReadLineStream.java
800+
import java.nio.file.*;
801+
802+
public class ReadLineStream {
803+
public static void
804+
main(String[] args) throws Exception {
805+
Files.lines(Paths.get("PathInfo.java"))
806+
.skip(13)
807+
.findFirst()
808+
.ifPresent(System.out::println);
809+
}
810+
}
811+
/* Output:
812+
show("RegularFile", Files.isRegularFile(p));
813+
*/
814+
```
815+
816+
这本章中的第一个流式传输的示例,跳过13行,然后选择下一行并将其打印出来。
817+
818+
`Files.lines()` 对于处理行作为 *incoming* `Stream` 非常有用,但是如果你想在 `Stream` 中读,操作或写怎么办?这就需要稍微复杂的代码:
819+
820+
```java
821+
// files/StreamInAndOut.java
822+
import java.io.*;
823+
import java.nio.file.*;
824+
import java.util.stream.*;
825+
826+
public class StreamInAndOut {
827+
public static void main(String[] args) {
828+
try(
829+
Stream<String> input =
830+
Files.lines(Paths.get("StreamInAndOut.java"));
831+
PrintWriter output =
832+
new PrintWriter("StreamInAndOut.txt")
833+
) {
834+
input
835+
.map(String::toUpperCase)
836+
.forEachOrdered(output::println);
837+
} catch(Exception e) {
838+
throw new RuntimeException(e);
839+
}
840+
}
841+
}
842+
```
651843

844+
因为我们在同一个块中执行所有操作,所以这两个文件都可以在相同的try-with-resources语句中打开。`PrintWriter` 是一个旧式的`java.io` 类,允许你“打印”到一个文件,所以它是这个应用程序的理想选择。如果你看一下 `StreamInAndOut.txt`,你会发现它确实是大写的。
652845

653846
<!-- Summary -->
654847
## 本章小结
848+
虽然这是对文件和目录操作的相当全面的介绍,但是库中仍然有没被介绍的功能 - 一定要研究 `java.nio.file` 的Javadocs,尤其是`java.nio.file.Files` 的。
655849

850+
Java 7和8对于处理文件和目录的库中做了大量改进。如果您刚刚开始使用Java,那么您很幸运。在过去,它非常令人不愉快,我确信Java 设计者以前对于文件操作不够重视才没做简化。对于初学者来说这是一件很棒的事,对于教学者来说也一样。我不明白为什么花了这么长时间来解决这个明显的问题,但不过它被解决了,我很高兴。使用文件现在很简单,甚至很有趣,这是以前你永远想不到的。
656851

657852

658853
<!-- 分页 -->

0 commit comments

Comments
 (0)