@author ixenoshtml
一、Path表示的是一个目录名序列,其后还能够跟着一个文件名,路径中第一个部件是根部件时就是绝对路径,例如 / 或 C:\ ,而容许访问的根部件取决于文件系统;java
二、以根部件开始的路径是绝对路径,不然就是相对路径;正则表达式
三、静态的Paths.get方法接受一个或多个字符串,字符串之间自动使用默认文件系统的路径分隔符链接起来(Unix是 /,Windows是 \ ),这就解决了跨平台的问题,接着解析链接起来的结果,若是不是合法路径就抛出InvalidPathException异常,不然就返回一个Path对象;shell
1 //假设是Unix的文件系统 2 Path absolute = Paths.get("/home", "cat"); //绝对路径 3 4 Path relative = Pahts.get("ixenos", "config", "user.properties"); //相对路径
四、由String路径获取Path对象数组
get还能够获取一整条路径(即多个部件构成的单个字符串),例如从配置文件中读取路径:ide
1 String baseDir = properties.getProperty("base.dir"); 2 //可能得到 /opt/ixenos 或者 C:\Program Files\ixenos 3 Path basePath = Paths.get(baseDir);
五、组合或解析路径工具
1) 调用 p.resolve(q) 将按下面的规则返回一个Path:若是q是绝对路径,则返回q,不然追加路径返回 p/q 或者 p\q性能
1 Path workRelative = Paths.get("work"); 2 Path workPath = basePath.resolve(workRelative); 3 4 //resolve也能够接受字符串形参 5 Path workPath = basePath.resolve("work");
2) 调用 p.resolveSibling("q") 将解析指定路径 p 的父路径 o ,并产生兄弟路径 o/qthis
1 Path tempPath = workPath.resolveSibling("temp"); 2 /* 3 若是workPath是 /opt/ixenos/work 4 那么将建立 /opt/ixenos/temp 5 */
3) 调用 p.relativize(r) 将产生一个冗余路径q,对q进行解析将产生相对路径r,最终r不包含和p的交集路径spa
1 /* 2 pathA为 /home/misty 3 pathB为 /home/ixenos/config 4 5 现已pathA对pathB进行相对化操做,将产生冗余路径 6 */ 7 Path pathC = pathA.relativize(pathB); //此时pathC为 ../ixenos/config 8 9 /* 10 normalize方法将移除冗余部件 11 */ 12 Path pathD = pathC.normalize(); //pathD为 /ixenos/config
4) toAbsolutePath 将产生给定路径的绝对路径,从根部件开始
5) Path类还有一些有用的断开和组合路径的方法,好比 getParent、getFileName、getRoot//得到根目录
6) Path有个toFile方法用来跟遗留类File类打交道,File类也有个toPath方法
方法签名:
static path write(Path path, byte[] bytes, OpenOption... options)
static path write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options)
其中OpenOption是个nio接口,StandardOpenOption是其枚举实现类,各枚举实例功能请查看API文档
1 /* 2 Files提供的简便方法适用于处理中等长度的文本文件 3 4 若是要处理的文件长度较大,或者二进制文件,那么仍是应该使用经典的IO流 5 6 */ 7 8 //将文件全部内容读入byte数组中 9 byte[] bytes = Files.readAllBytes(path); //传入Path对象 10 11 //以后能够根据字符集构建字符串 12 String content = new String(bytes, charset); 13 14 //也能够直接看成行序列读入 15 List<String> lines = Files.readAllLines(path, charset); 16 17 //相反,也能够写一个字符串到文件中,默认是覆盖 18 Files.write(path, content.getBytes(charset)); //传入byte[] 19 20 //追加内容,根据参数决定追加等功能 21 Files.write(path, content.getBytes(charset), StandardOpenOption.APPEND); //传入枚举对象,打开追加开关 22 23 //将一个行String的集合List写出到文件中 24 Files.write(path, lines);
方法签名:
static path copy(Path source, Path target, CopyOption... options)
static path move(Path source, Path target, CopyOption... options)
static void delete(Path path) //若是path不存在文件将抛出异常,此时调用下面的比较好
static boolean deleteIfExists(Path path)
其中CopyOption是个nio接口,StandardCopyOption是其枚举实现类,各枚举实例功能请查看API文档
其中有个ATOMIC_MOVE能够填入用来保证原子性操做,要么移动成功完成,要么源文件保持在原位置
1 //复制 2 Files.copy(fromPath, toPath); 3 4 //剪切 5 Files.move(fromPath, toPath); 6 7 /* 8 以上若是toPath已存在,那么操做失败, 9 若是要覆盖,需传入参数REPLACE_EXISTING 10 还要复制文件属性,传入COPY_ATTRIBUTES 11 */ 12 Files.copy(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
1 //建立新目录,除了最后一个部件,其余必须是已存在的 2 Files.createDirectory(path); 3 4 //建立路径中的中间目录,能建立不存在的中间部件 5 Files.createDirectories(path); 6 7 /* 8 建立一个空文件,检查文件存在,若是已存在则抛出异常 9 而检查文件存在是原子性的,所以在此过程当中没法执行文件建立操做 10 */ 11 Files.createFile(path); 12 13 //添加前/后缀建立临时文件或临时目录 14 Path newPath = Files.createTempFile(dir, prefix, suffix); 15 Path newPath = Files.createTempDirectory(dir, prefix);
略,具体看API文档,或者corejava page51
旧的File类有两个方法获取目录中全部文件构成的字符串数组,String[] list() 和String[] list(FileFilter filter),可是当目录中包含大量文件时,这两方法性能会很是低。
缘由分析:
1 一、//File类list全部文件 2 public String[] list() { 3 SecurityManager security = System.getSecurityManager(); //文件系统权限获取 4 if (security != null) { 5 security.checkRead(path); 6 } 7 if (isInvalid()) { 8 return null; 9 } 10 return fs.list(this); //底层调用FileSystem的list 11 } 12 13 //FileSystem抽象类的list 14 //File类中定义fs是由DefaultFileSystem静态生成的 15 private static final FileSystem fs = DefaultFileSystem.getFileSystem(); 16 17 //所以咱们来看一下DefaultFileSystem类,发现是生成一个WinNtFileSystem对象 18 class DefaultFileSystem { 19 20 /** 21 * Return the FileSystem object for Windows platform. 22 */ 23 public static FileSystem getFileSystem() { 24 return new WinNTFileSystem(); 25 } 26 } 27 28 29 //而WinNtFileSystem类继承于FileSystem抽象类,这里咱们主要观察它的list(File file)方法 30 @Override 31 public native String[] list(File f); 32 /*咱们能够看到这是个native方法,说明list的操做是由操做系统的文件系统控制的,当目录中包含大量的文件时,这个方法的性能将会很是低。 33 由此为了替代,NIO的Files类设计了newDirectoryStream(Path dir)及其重载方法,将生成Iterable对象(可用foreach迭代)*///~ 34 38 39 二、//回调过滤 40 public String[] list(FilenameFilter filter) { //采用接口回调 41 String names[] = list(); //调用list全部 42 if ((names == null) || (filter == null)) { 43 return names; 44 } 45 List<String> v = new ArrayList<>(); 46 for (int i = 0 ; i < names.length ; i++) { 47 if (filter.accept(this, names[i])) { //回调FilenameFileter对象的accept方法 48 v.add(names[i]); 49 } 50 } 51 return v.toArray(new String[v.size()]); 52 }
所以,Files类设计了newDirectoryStream(Path dir)及其重载方法,将生成Iterable对象(可用foreach迭代)
遍历目录获得一个可迭代的子孙文件集合
static DirectoryStream<Path> |
newDirectoryStream(Path dir)
Opens a directory, returning a
DirectoryStream to iterate over all entries in the directory.
|
static DirectoryStream<Path> |
newDirectoryStream(Path dir, DirectoryStream.Filter<? super Path> filter)
Opens a directory, returning a
DirectoryStream to iterate over the entries in the directory.
|
static DirectoryStream<Path> |
newDirectoryStream(Path dir, String glob) |
返回一个 目录流 ,能够当作一个存放着所有Path的实现了Iterable的集合,
所以可用迭代器或foreach迭代,只是使用迭代器的时候要注意不能invoke另外一个Iterator:
While DirectoryStream
extends Iterable
, it is not a general-purpose Iterable
as it supports only a single Iterator
; invoking the iterator
method to obtain a second or subsequent iterator throws IllegalStateException
.
示例:
1 try(DirectoryStream<Path> entries = Files.newDirectoryStream(dir)) 2 { 3 for(Path entry : entries) 4 { 5 ... 6 } 7 }
能够传入glob参数,即便用glob模式来过滤文件(以取代list(FileFilter filter)):
newDirectoryStream(Path dir, String glob) 注意是String类型
1 try(DirectoryStream<Path> entries = Files.newDirectoryStream(dir, "*.java")) // 2 { 3 ... 4 }
glob模式
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。
1.星号 * 匹配路径组成部分0个或多个字符;例如 *.java 匹配当前目录中的全部Java文件
2.两星号 ** 匹配跨目录边界0个或多个字符;例如 **.java 匹配在全部子目录中的Java文件
3.问号(?)只匹配一个字符;例如 ????.java 匹配全部四个字符的Java文件,不包括扩展名;使用?是由于*是通配符不指定数量
4.[...] 匹配一个字符集合,能够用连线 [0-9] 和取反符 [!0-9];例如 Test[0-9A-F].java 匹配Testx.java,假设x是一个十六进制数字,[0-9A-F]是匹配单个字符为十六进制数字,好比B(十六进制不区分大小写)
若是在方括号中使用短划线分隔两个字符,表示全部在这两个字符范围内的均可以匹配(好比 [0-9] 表示匹配全部 0 到 9 的数字)。
5.{...} 匹配由逗号隔开的多个可选项之中的一个;例如 *.{java,class} 匹配全部Java文件和类class文件
6.\ 转义上述任意模式中的字符;例如 *\** 匹配全部子目录中文件名包含*的文件,这里为 ** 转义,前面是匹配0个或多个字符
下面是网友总结的Glob模式:
Glob模式 | 描述 |
---|---|
*.txt | 匹配全部扩展名为.txt的文件 |
*.{html,htm} | 匹配全部扩展名为.html或.htm的文件。{ }用于组模式,它使用逗号分隔 |
?.txt | 匹配任何单个字符作文件名且扩展名为.txt的文件 |
. | 匹配全部含扩展名的文件 |
C:\Users\* | 匹配全部在C盘Users目录下的文件。反斜线“\”用于对紧跟的字符进行转义 |
/home/** | UNIX平台上匹配全部/home目录及子目录下的文件。**用于匹配当前目录及其全部子目录 |
[xyz].txt | 匹配全部单个字符做为文件名,且单个字符只含“x”或“y”或“z”三种之一,且扩展名为.txt的文件。方括号[]用于指定一个集合 |
[a-c].txt | 匹配全部单个字符做为文件名,且单个字符只含“a”或“b”或“c”三种之一,且扩展名为.txt的文件。减号“-”用于指定一个范围,且只能用在方括号[]内 |
[!a].txt | 匹配全部单个字符做为文件名,且单个字符不能包含字母“a”,且扩展名为.txt的文件。叹号“!”用于否认 |
咱们能够调用Files类的walkFileTree方法,并传入一个FileVisitor接口类型的对象(还有更多方法在API里等你发现……)
1 /*传入一个FileVisitor子类的匿名对象*/ 2 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() 3 { 4 //walkFileTree回调此方法来遍历全部子孙 5 public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException 6 { 7 if(attrs.isDirectory()) //自定义的选择,属于业务代码,这和walkFileTree的宗旨(遍历全部子孙成员)无关 8 System.out.println(path); 9 return FileVisitResult.CONTINUE; 10 } 11 12 public FileVisitResult visitFileFailed(Path path, IOException exc) throws IOException 13 { 14 return FileVisitResult.CONTINUE; 15 } 16 });
Files.newDirectoryStream(Path dir) 遍历后返回一个可迭代的子孙文件集合;
Files.walkFileTree(Path dir, FileVisitor fv) 是一个遍历子孙目录和文件的过程;
由上文知道,Paths类会在默认的文件系统中查找路径,即在用户本地磁盘中的文件。
其实,咱们也能够有其余的文件系统,好比ZIP文件系统。
1 /*假设zipname是某个ZIP文件的名字*/ 2 FileSystem fs = FileSystems.newFileSystem(Paths.get(zipname), null);
上述代码将创建一个基于zipname的文件系统,它包含ZIP文档中的全部文件。
1)若是知道文件名(String类型),那么从这个ZIP文档中复制出这个文件就很容易:
1 Files.copy(fs.getPath(fileName), targetPath);
Q:fs.getPath是使用了ZIP文件系统来getPath,那么默认的文件系统能调用吗?
A:能。FileSystem类中有一个静态的getDefault()方法,返回一个默认的文件系统对象,一样能够由文件名getPath。
具体getPath(String name)是遍历仍是随机访问,有空再去看源码实现。
2)要列出ZIP文档中的全部文件,一样能够用walkFileTree遍历文件树
1 FileSystem fs = FileSystems.newFileSystem(Paths.get(fileName), null); 2 3 //walkFileTree须要传入一个要被遍历的目录Path,和一个FileVisitor对象 4 Files.walkFileTree(fs.getPath("/"), 5 newSimpleFileVisitor<Path>(){ 6 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws Exception{ 7 System.out.println(file); 8 return FileVisitResult.CONTINUE; 9 });