java 文件操作(二)---Files和Path

     自从java 7以来,引入了FIles类和Path接口。他们两封装了用户对文件的所有可能的操作,相比于java 1的File类来说,使用起来方便很多。但是其实一些本质的操作还是很类似的。主要需要知道的是,Path表示路径可以使文件的路径也可以是目录的路径,Files中所有成员都是静态方法,通过路径实现了对文件的基本操作。下面我们首先看看Path接口。

一、Path接口
      和Path接口相关的还有一个类Paths,这个类非常简单,只有两个方法加一个私有构造方法。

public final class Paths {
    private Paths() { }
    public static Path get(String first, String... more) {
        return FileSystems.getDefault().getPath(first, more);
    }
    public static Path get(URI uri) {/*这个方法我们暂时不研究*/}
}

     大家可以看到,通过Paths的get静态方法,我们可以获得一个Path对象,而实际上我们通常都是通过Paths的这个get方法来获取Path对象。(至于怎么调用这个方法返回的对象,主要是通过调用文件系统的抽象方法来实现的),获得了Path对象,我们就可以调用其内部封装的所有的方法,由于方法比较多,我们一点一点看。

boolean isAbsolute();
Path getFileName();
Path getParent();
int getNameCount();
Path getName(int index);
Path subpath(int beginIndex, int endIndex);
Path resolve(Path other);
Path resolveSibling(Path other);
Path relativize(Path other);
Path toAbsolutePath();
File toFile();

     主要的方法就这么多,其中有些方法的功能乃至具体的实现代码和上篇介绍的File类类似,这里就不在说明了。getFileName这个方法类似于File类的getName方法,返回路径的文件名称(目录名或者文件名),两者实现原理基本一样。

Path p = Paths.get("a","b","c","d","e");
System.out.println(p.getNameCount());
/*执行代码可以看到输出结果:5*/

Path p = Paths.get("a/t","b","c","d","e");
System.out.println(p.getNameCount());
/*执行代码输出结果:6*/

     可以看到,getNameCount方法并不是直接数构建Path对象时传入了几个字符串。当我们调用Path.get方法传入可变字符串作为路径时,它将每个独立的字符串当成一个目录名,使用默认路径分隔符连接这些路径名形成Path路径,而调用getNameCount方法是根据默认路径分隔符的个数来统计返回的。
     getName方法要求传入一个int型索引,在构建路径时,从根路径开始每一层都被编号了,根目录为0,子目录依次加一所以getName方法可以获取任意层次目录的名称。

Path p = Paths.get("a","b","c","d","e");
System.out.println(p.getName(1));
/*输出:b*/

     subpath方法和我们String中的substring类似,给定开始和结束位置的索引值,获取他们之间的路径字符串。

Path p = Paths.get("a","b","c","d","e");
System.out.println(p.subpath(1,3));
/*输出结果:b/c*/

     需要注意一个细节,截取范围[startIndex,endIndex),也就是endIndex位置的值是不会被截取的。

      resolve方法是一个很有意思的方法,按照我的理解,这个方法实现的是对路径的组合的操作。p.resolve(q),如果q是绝对路径,返回结果为q,如果q是相对路径返回结果是p+q,实现了拼接组合。

Path p = Paths.get("a","b","c","d","e");
Path q = Paths.get("c:/users");
System.out.println(p.resolve(q));
/*输出:c:/users*/

Path p = Paths.get("a","b","c","d","e");
Path q = Paths.get("users");
System.out.println(p.resolve(q));
/*输出结果:a\b\c\d\e\users*/

     resolveSibling方法是通过解析当前路径的父目录,产生兄弟路径。

Path p = Paths.get("a","b","c","d","e");
Path q = Paths.get("users");
System.out.println(p.resolveSibling(q));
/*输出结果:a\b\c\d\users*/

     替换了e为users,因为e作为当前目录,而此方法就是在当前目录下生成一个和他同级的兄弟目录。通常可以用来修改当前目录的目录名。(在生成磁盘文件之前)

     接下来说说这个relativize方法,它是一个用来生成一个相对路径的方法。需要额外传入一个Path对象。

Path p = Paths.get("a","b","c","d","e");
Path q = Paths.get("users");
System.out.println(p.relativize(q));
/*输出结果:..\..\..\..\..\users*/

Path p = Paths.get("a","b","c","d","e");
Path q = Paths.get("a","b","c","d","e","f");
System.out.println(p.relativize(q));
/*输出结果:f*/

     通过比较可以发现,所谓生成相对路径,实际上就是,q相对于p。从第二中情况我们可以看出来,整个p可以作为q的父目录,于是相对路径就是当前目录f。对于第一种情况,因为没有找到公共目录,所以整个p作为q的父目录,当然相对于q来说,相对路径就是这样了。
     最后还想啰嗦一句,在Path中有一个方法toFile,这个方法对应于FIle中的toPath,为什么要实现这么两个方法,其实还是为了兼容旧的File类,方便一些旧的系统成功的跨度到新的java标准中来。

二、Files类
     上面说过,整个FIles类中,都是静态方法,没有一个实例域。(足以见得,这个类就是为了实现对文件的各种操作)首先看看对文件的读写操作。

public static InputStream newInputStream(Path path, OpenOption... options)

public static OutputStream newOutputStream(Path path, OpenOption... options)

public static BufferedReader newBufferedReader(Path path, Charset cs)

public static BufferedReader newBufferedReader(Path path)

public static BufferedWriter newBufferedWriter

public static byte[] readAllBytes(Path path)

public static List<String> readAllLines(Path path)

public static Path write(Path path, byte[] bytes, OpenOption... options)

public static long copy(InputStream in, Path target, CopyOption... options)

public static long copy(Path source, OutputStream out)

     方法很多,我们慢慢看,首先有两个方法可以根据Path路径返回InputStream/OutputStream字节流对象,这两个方法为我们下面的一些方法提供了一定的便利,也有两个方法通过Path对象返回BufferedReader/BufferedWriter对象。这些都是对于我们其他的方法是有帮助的。
     readAllBytes内部通过创建InputStream对象来读取所有的字节到给定的字节数组中并返回。readAllLines内部通过创建List数组,使用BufferedReader创建字符缓冲流,一行一行的读取。最后返回List集合。写的操作基本都是读的逆操作,这里不再赘述。
     copy这个方法有多个重载,分别是:

private static long copy(InputStream source, OutputStream sink)

public static long copy(InputStream in, Path target, CopyOption... options)

public static long copy(Path source, OutputStream out)

public static Path copy(Path source, Path target, CopyOption... options)

      第一个重载是一个私有的方法,是被被人调用的工具方法。主要的功能是:从一个source流中读取所有的字节并写入sink流中,返回实际读入或写入的字节数。第二个重载是选择将Path对象通过方法newOutputStream,构建了一个OutputStream对象,然后调用第一个重载方法实现copy。完成的功能是:从一个InputStream流中读取所有的字节并写入一个指定的文件中。第三个重载方法主要是:从一个Path文件中读取所有的字节并写入一个OutputStream对象流中。操作流程类似,不在赘述。最后一个重载方法实现的是从一个Path对象复制到另一个Path对象。

//根目录下只有hello.txt文件,没有world文件
Path p = Paths.get("hello.txt");
Path q = Paths.get("world.txt");
Files.copy(p,q);
/*world文件被创建并且hello中的内容被复制到此*/

     对于这个操作,需要注意的几点:如果q在磁盘为位置的文件已经存在将不能完成复制操作,如果p在磁盘位置上没有对应文件此操作依然失败,如果p是一个目录文件,结果会复制一个名为world的目录文件,如果q是一个目录文件则会创建一个无类型的文件(hello中的内容已经被复制进去)。

     说完了有关文件的读写操作,下面说说文件或目录的创建和获取文件的基本信息。

public static Path createFile(Path path, FileAttribute<?>... attrs)

public static Path createDirectory(Path dir, FileAttribute<?>... attrs)

public static Path createDirectories(Path dir, FileAttribute<?>... attrs)

public static Path createTempFile

     因为Path路径中存放的可以是文件类型,也可以是目录类型。那么在创建的时候就需要进行区分了。createFile根据指定路径创建一个指定类型的文件,createDirectory和createDirectories的区别在,如果Path路径上存在着没有被创建的目录,后者会将他们全部都创建。对于创建临时文件,由于用的不多,就不说了。
     对于文件信息的获取主要有以下些方法:

public static boolean isSameFile(Path path, Path path2)
public static boolean isHidden(Path path)
public static String probeContentType(Path path)
public static boolean isDirectory(Path path, LinkOption... options)
public static boolean isRegularFile(Path path, LinkOption... options)
public static long size(Path path)
public static boolean exists(Path path, LinkOption... options)
public static boolean isReadable(Path path)
public static boolean isWritable(Path path)
/*这些方法名字就是注释,相信大家一眼就能识别他们各自的功能*/

     最后谈谈迭代和过滤器,在上篇文章的最后,我们说了FIle的过滤和迭代,因为在File类中,通常都是一次性返回一个File数组或者String数组,这往往是低效的。在Files类中,设计了一个方法newDirectoryStream,返回了一个目录流,可以显著提高效率。

public static DirectoryStream<Path> newDirectoryStream(Path dir)

public static DirectoryStream<Path> newDirectoryStream(Path dir, String glob)

 public static DirectoryStream<Path> newDirectoryStream(Path dir,
                                                           DirectoryStream.Filter<? super Path> filter)

     这是三个目录流的重载方法,第一个只需要提供一个Path路径即可,第二个方法提供了一个Path对象和一个glob字符串。glob模式:


这里写图片描述

第三个方法还外部指定了一个过滤器。具体怎么使用,下面看代码。

DirectoryStream<Path> d = Files.newDirectoryStream(Paths.get("f:/360"));
for (Path p : d){
  System.out.println(p.getFileName());
}
//输出结果:
360defender
360sdSetup.exe
360zip
//这是我f盘360文件下的所有文件
DirectoryStream<Path> d = Files.newDirectoryStream(Paths.get("f:/360"),"*.exe");
for (Path p : d){
   System.out.println(p.getFileName());
}
//输出结果:
360sdSetup.exe
DirectoryStream<Path> d = Files.newDirectoryStream(Paths.get("f:/360"),new DirectoryStream.Filter<Path>(){
    @Override
    public boolean accept(Path entry)  {
       return Files.isDirectory(entry)?true:false;
            }
   });
   for (Path p : d){
       System.out.println(p.getFileName());
    }
    //输出结果:
    360defender
    360zip

     第三种方法通过显式传入一个过滤器来实现获取子目录中所有的目录。

     如果本文有错误,欢迎大家指出!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容