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

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

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏AI研习社

嘀~正则表达式快速上手指南(下篇)

上面的代码中用 for 循环去遍历 contents 这样我们就可以一个一个处理每封邮件。我们创建一个字典, emails_dict,这将保存每个电子邮件的所有...

561
来自专栏刺客博客

使用sed命令在两行匹配之间插入一行新内容

在这两个之间插入一行liming。 解决办法: 查了一下sed的用法貌似可以试一下,看到命令n可以读入下一行到pattern space,就可一匹配完前一行...

792
来自专栏Java帮帮-微信公众号-技术文章全总结

shell编程基础入门

shell编程基础入门 文章最后有下载shell学习指南电子书链接。 1.shell格式:例 shell脚本开发习惯 1.指定解释器 #!/bi...

3184
来自专栏noteless

[二十二]JavaIO之LineNumberReader

LineNumberReader本身并没有太多的东西,他是BufferedReader的子类

722
来自专栏编程

使用dict和set

Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。-...

18910
来自专栏Python小屋

Python编程常见出错信息及原因分析(2)

(1)缩进错误 演示代码: >>> if 5>3: print('5>3') SyntaxError: expected an indented block >...

3567
来自专栏阮一峰的网络日志

awk 入门教程

它依次处理文件的每一行,并读取里面的每一个字段。对于日志、CSV 那样的每行格式相同的文本文件,awk可能是最方便的工具。

662
来自专栏抠抠空间

模板语言

常用语法 只需要记两种特殊符号: {{  }}和 {% %} 变量相关的用{{}},逻辑相关的用{%%}。 变量 {{ 变量名 }} 变量名由字母数字和下划线组...

3258
来自专栏xingoo, 一个梦想做发明家的程序员

【AngularJS】—— 7 模块化

AngularJS有几大特性,比如:   1 MVC 2 模块化   3 指令系统   4 双向数据绑定 那么本篇就来看看AngularJS的模块...

1885
来自专栏技术/开源

从C#到TypeScript - 装饰器

从C#到TypeScript - 装饰器 在C#里面如果想要不直接修改类或方法,但给类或方法添加一些额外的信息或功能,可以想到用Attribute,这是一个十分...

20310

扫码关注云+社区