Java NIO Path接口和Files类配合操作文件的实例
path接口
1、path表示的是一个目录名序列,其后还可以跟着一个文件名,路径中第一个部件是根部件时就是绝对路径,例如 / 或 c:\ ,而允许访问的根部件取决于文件系统;
2、以根部件开始的路径是绝对路径,否则就是相对路径;
3、静态的paths.get方法接受一个或多个字符串,字符串之间自动使用默认文件系统的路径分隔符连接起来(unix是 /,windows是 \ ),这就解决了跨平台的问题,接着解析连接起来的结果,如果不是合法路径就抛出invalidpathexception异常,否则就返回一个path对象;
//假设是unix的文件系统 path absolute = paths.get("/home", "cat"); //绝对路径 path relative = pahts.get("ixenos", "config", "user.properties"); //相对路径
4、由string路径获取path对象
get还可以获取一整条路径(即多个部件构成的单个字符串),例如从配置文件中读取路径:
string basedir = properties.getproperty("base.dir"); //可能获得 /opt/ixenos 或者 c:\program files\ixenos path basepath = paths.get(basedir);
5、组合或解析路径
1) 调用 p.resolve(q) 将按下面的规则返回一个path:如果q是绝对路径,则返回q,否则追加路径返回 p/q 或者 p\q
path workrelative = paths.get("work"); path workpath = basepath.resolve(workrelative); //resolve也可以接受字符串形参 path workpath = basepath.resolve("work");
2) 调用 p.resolvesibling("q") 将解析指定路径 p 的父路径 o ,并产生兄弟路径 o/q
path temppath = workpath.resolvesibling("temp"); /* 如果workpath是 /opt/ixenos/work 那么将创建 /opt/ixenos/temp */
3) 调用 p.relativize(r) 将产生一个冗余路径q,对q进行解析将产生相对路径r,最终r不包含和p的交集路径
/* patha为 /home/misty pathb为 /home/ixenos/config 现已patha对pathb进行相对化操作,将产生冗余路径 */ path pathc = patha.relativize(pathb); //此时pathc为 ../ixenos/config /* normalize方法将移除冗余部件 */ path pathd = pathc.normalize(); //pathd为 /ixenos/config
4) toabsolutepath 将产生给定路径的绝对路径,从根部件开始
5) path类还有一些有用的断开和组合路径的方法,比如 getparent、getfilename、getroot//获得根目录
6) path有个tofile方法用来跟遗留类file类打交道,file类也有个topath方法
files工具类
1、读写文件
方法签名:
static path write(path path, byte[] bytes, openoption... options)
static path write(path path, iterable<? extends charsequence> lines, openoption... options)
这里只列举下面用到的方法,更多方法请看api文档...
其中openoption是个nio接口,standardopenoption是其枚举实现类,各枚举实例功能请查看api文档
/* files提供的简便方法适用于处理中等长度的文本文件 如果要处理的文件长度较大,或者二进制文件,那么还是应该使用经典的io流 */ //将文件所有内容读入byte数组中 byte[] bytes = files.readallbytes(path); //传入path对象 //之后可以根据字符集构建字符串 string content = new string(bytes, charset); //也可以直接当作行序列读入 list<string> lines = files.readalllines(path, charset); //相反,也可以写一个字符串到文件中,默认是覆盖 files.write(path, content.getbytes(charset)); //传入byte[] //追加内容,根据参数决定追加等功能 files.write(path, content.getbytes(charset), standardopenoption.append); //传入枚举对象,打开追加开关 //将一个行string的集合list写出到文件中 files.write(path, lines);
2、复制、剪切、删除
方法签名:
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)
这里只列举下面用到的方法,更多方法请看api文档...
其中copyoption是个nio接口,standardcopyoption是其枚举实现类,各枚举实例功能请查看api文档
其中有个atomic_move可以填入用来保证原子性操作,要么移动成功完成,要么源文件保持在原位置
//复制 files.copy(frompath, topath); //剪切 files.move(frompath, topath); /* 以上如果topath已存在,那么操作失败, 如果要覆盖,需传入参数replace_existing 还要复制文件属性,传入copy_attributes */ files.copy(frompath, topath, standardcopyoption.replace_existing, standardcopyoption.copy_attributes);
3、创建文件和目录
//创建新目录,除了最后一个部件,其他必须是已存在的 files.createdirectory(path); //创建路径中的中间目录,能创建不存在的中间部件 files.createdirectories(path); /* 创建一个空文件,检查文件存在,如果已存在则抛出异常 而检查文件存在是原子性的,因此在此过程中无法执行文件创建操作 */ files.createfile(path); //添加前/后缀创建临时文件或临时目录 path newpath = files.createtempfile(dir, prefix, suffix); path newpath = files.createtempdirectory(dir, prefix);
4、获取文件信息
略,具体看api文档,或者corejava page51
5、迭代目录中的文件
旧的file类有两个方法获取目录中所有文件构成的字符串数组,string[] list() 和string[] list(filefilter filter),但是当目录中包含大量文件时,这两方法性能会非常低。
原因分析:
1、//file类list所有文件 public string[] list() { securitymanager security = system.getsecuritymanager(); //文件系统权限获取 if (security != null) { security.checkread(path); } if (isinvalid()) { return null; } return fs.list(this); //底层调用filesystem的list } //filesystem抽象类的list //file类中定义fs是由defaultfilesystem静态生成的 private static final filesystem fs = defaultfilesystem.getfilesystem(); //因此我们来看一下defaultfilesystem类,发现是生成一个winntfilesystem对象 class defaultfilesystem { /** * return the filesystem object for windows platform. */ public static filesystem getfilesystem() { return new winntfilesystem(); } } //而winntfilesystem类继承于filesystem抽象类,这里我们主要观察它的list(file file)方法 @override public native string[] list(file f); /*我们可以看到这是个native方法,说明list的操作是由操作系统的文件系统控制的,当目录中包含大量的文件时,这个方法的性能将会非常低。 由此为了替代,nio的files类设计了newdirectorystream(path dir)及其重载方法,将生成iterable对象(可用foreach迭代)*///~ 2、//回调过滤 public string[] list(filenamefilter filter) { //采用接口回调 string names[] = list(); //调用list所有 if ((names == null) || (filter == null)) { return names; } list<string> v = new arraylist<>(); for (int i = 0 ; i < names.length ; i++) { if (filter.accept(this, names[i])) { //回调filenamefileter对象的accept方法 v.add(names[i]); } } return v.toarray(new string[v.size()]); }
这时候高科技来了——files获得可迭代的目录流,
传入一个目录path,遍历子孙目录返回一个目录path的stream,注意这里所有涉及的path都是目录而不是文件!
因此,files类设计了newdirectorystream(path dir)及其重载方法,将生成iterable对象(可用foreach迭代)
遍历目录得到一个可迭代的子孙文件集合
staticdirectorystream<path> |
newdirectorystream(path dir)
opens a directory, returning a
directorystream to iterate over all entries in the directory. |
staticdirectorystream<path> |
newdirectorystream(path dir, directorystream.filter<? super path> filter)
opens a directory, returning a
directorystream to iterate over the entries in the directory. |
staticdirectorystream<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.
示例:
try(directorystream<path> entries = files.newdirectorystream(dir)) { for(path entry : entries) { ... } }
可以传入glob参数,即使用glob模式来过滤文件(以取代list(filefilter filter)):
newdirectorystream(path dir, string glob) 注意是string类型
try(directorystream<path> entries = files.newdirectorystream(dir, "*.java")) // { ... }
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里等你发现……)
/*传入一个filevisitor子类的匿名对象*/ files.walkfiletree(dir, new simplefilevisitor<path>() { //walkfiletree回调此方法来遍历所有子孙 public filevisitresult visitfile(path path, basicfileattributes attrs) throws ioexception { if(attrs.isdirectory()) //自定义的选择,属于业务代码,这和walkfiletree的宗旨(遍历所有子孙成员)无关 system.out.println(path); return filevisitresult.continue; } public filevisitresult visitfilefailed(path path, ioexception exc) throws ioexception { return filevisitresult.continue; } });
咱们来总结一下,
files.newdirectorystream(path dir) 遍历后返回一个可迭代的子孙文件集合;
files.walkfiletree(path dir, filevisitor fv) 是一个遍历子孙目录和文件的过程;
zip文件系统
由上文知道,paths类会在默认的文件系统中查找路径,即在用户本地磁盘中的文件。
其实,我们也可以有其他的文件系统,比如zip文件系统。
/*假设zipname是某个zip文件的名字*/ filesystem fs = filesystems.newfilesystem(paths.get(zipname), null);
上述代码将建立一个基于zipname的文件系统,它包含zip文档中的所有文件。
1)如果知道文件名(string类型),那么从这个zip文档中复制出这个文件就很容易:
files.copy(fs.getpath(filename), targetpath);
q:fs.getpath是使用了zip文件系统来getpath,那么默认的文件系统能调用吗?
a:能。filesystem类中有一个静态的getdefault()方法,返回一个默认的文件系统对象,同样可以由文件名getpath。
具体getpath(string name)是遍历还是随机访问,有空再去看源码实现。
2)要列出zip文档中的所有文件,同样可以用walkfiletree遍历文件树
filesystem fs = filesystems.newfilesystem(paths.get(filename), null); //walkfiletree需要传入一个要被遍历的目录path,和一个filevisitor对象 files.walkfiletree(fs.getpath("/"), newsimplefilevisitor<path>(){ public filevisitresult visitfile(path file, basicfileattributes attrs) throws exception{ system.out.println(file); return filevisitresult.continue; });
以上这篇java nio path接口和files类配合操作文件的实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。