@@ -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