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类配合操作文件的实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • JAVA-NIO之Socket/ServerSocket Channel(详解)

    一.ServerSocketChannel Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 就像标准IO中的ServerSocket一样.ServerSocketChannel类在 java.nio.channels包中. 打开 ServerSocketChannel 通过调用 ServerSocketChannel.open() 方法来打开ServerSocketChannel. 关闭 ServerSocketChannel 通过调用Se

  • JDK1.7 之java.nio.file.Files 读取文件仅需一行代码实现

    JDK1.7中引入了新的文件操作类java.nio.file这个包,其中有个Files类它包含了很多有用的方法来操作文件,比如检查文件是否为隐藏文件,或者是检查文件是否为只读文件.开发者还可以使用Files.readAllBytes(Path)方法把整个文件读入内存,此方法返回一个字节数组,还可以把结果传递给String的构造器,以便创建字符串输出.此方法确保了当读入文件的所有字节内容时,无论是否出现IO异常或其它的未检查异常,资源都会关闭.这意味着在读文件到最后的块内容后,无需关闭文件.要注意

  • Java使用NioSocket手动实现HTTP服务器

    NioSocket简单复习 重要概念 NioSocket里面的三个重要概念:Buffer.Channel.Selector Buffer为要传输的数据 Channel为传输数据的通道 Selector为通道的分配调度者 使用步骤 使用NioSocket实现通信大概如以下步骤: ServerSocketChannel可以通过configureBlocking方法来设置是否采用阻塞模式,设置为false后就可以调用register注册Selector,阻塞模式下不可以用Selector. 注册后,S

  • Java NIO:浅析IO模型_动力节点Java学院整理

    也许很多朋友在学习NIO的时候都会感觉有点吃力,对里面的很多概念都感觉不是那么明朗.在进入Java NIO编程之前,我们今天先来讨论一些比较基础的知识:I/O模型.下面本文先从同步和异步的概念 说起,然后接着阐述了阻塞和非阻塞的区别,接着介绍了阻塞IO和非阻塞IO的区别,然后介绍了同步IO和异步IO的区别,接下来介绍了5种IO模型,最后介绍了两种和高性能IO设计相关的设计模式(Reactor和Proactor). 以下是本文的目录大纲: 一.什么是同步?什么是异步? 二.什么是阻塞?什么是非阻塞

  • java的NIO管道用法代码分享

    Java的NIO中的管道,就类似于实际中的管道,有两端,一段作为输入,一段作为输出.也就是说,在创建了一个管道后,既可以对管道进行写,也可以对管道进行读,不过这两种操作要分别在两端进行.有点类似于队列的方式. 这里是Pipe原理的图示: 创建管道 通过Pipe.open()方法打开管道.例如: Pipe pipe = Pipe.open(); 向管道写数据 要向管道写数据,需要访问sink通道.像这样: Pipe.SinkChannel sinkChannel = pipe.sink(); 通过

  • JAVA-4NIO之Channel之间的数据传输方法

    在Java NIO中,如果两个通道中有一个是FileChannel,那你可以直接将数据从一个channel(译者注:channel中文常译作通道)传输到另外一个channel. transferFrom():被动接收 FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中(译者注:这个方法在JDK文档中的解释为将字节从给定的可读取字节通道传输到此通道的文件中). 方法的输入参数position表示从position处开始向目标文件写入数据,cou

  • Java中网络IO的实现方式(BIO、NIO、AIO)介绍

    在网络编程中,接触到最多的就是利用Socket进行网络通信开发.在Java中主要是以下三种实现方式BIO.NIO.AIO. 关于这三个概念的辨析以前一直都是好像懂,但是表达的不是很清楚,下面做个总结完全辨析清楚. 1. BIO方式 首先我用一个较为通俗的语言来说明: BIO 就是阻塞IO,每个TCP连接进来服务端都需要创建一个线程来建立连接并进行消息的处理.如果中间发生了阻塞(比如建立连接.读数据.写数据时发生阻碍),线程也会发生阻塞,并发情况下,N个连接需要N个线程来处理. 这种方式的缺点就是

  • Java NIO深入分析

    以下我们系统通过原理,过程等方便给大家深入的简介了Java NIO的函数机制以及用法等,学习下吧. 前言 本篇主要讲解Java中的IO机制 分为两块: 第一块讲解多线程下的IO机制 第二块讲解如何在IO机制下优化CPU资源的浪费(New IO) Echo服务器 单线程下的socket机制就不用我介绍了,不懂得可以去查阅下资料 那么多线程下,如果进行套接字的使用呢? 我们使用最简单的echo服务器来帮助大家理解 首先,来看下多线程下服务端和客户端的工作流程图: 可以看到,多个客户端同时向服务端发送

  • Java NIO实例UDP发送接收数据代码分享

    Java的NIO包中,有一个专门用于发送UDP数据包的类:DatagramChannel,UDP是一种无连接的网络协议, 一般用于发送一些准确度要求不太高的数据等. 完整的服务端程序如下: public class StatisticsServer { //每次发送接收的数据包大小 private final int MAX_BUFF_SIZE = 1024 * 10; //服务端监听端口,客户端也通过该端口发送数据 private int port; private DatagramChann

  • Java NIO Path接口和Files类配合操作文件的实例

    Path接口 1.Path表示的是一个目录名序列,其后还可以跟着一个文件名,路径中第一个部件是根部件时就是绝对路径,例如 / 或 C:\ ,而允许访问的根部件取决于文件系统: 2.以根部件开始的路径是绝对路径,否则就是相对路径: 3.静态的Paths.get方法接受一个或多个字符串,字符串之间自动使用默认文件系统的路径分隔符连接起来(Unix是 /,Windows是 \ ),这就解决了跨平台的问题,接着解析连接起来的结果,如果不是合法路径就抛出InvalidPathException异常,否则就

  • java中List接口与实现类介绍

    目录 List接口介绍-ArrayList ArrayList源码结论 ArrayList源码分析 总结 List接口介绍-ArrayList 有序.可重复 线程不安全,因为没有synchronized修饰 ArrayList源码结论 ArrayList中维护了一个Object类型的数组elementData. transient Object[] elementData; // transient 表示该属性不会被序列化 当创建ArrayList对象时,如果使用的是无参构造器,则初始eleme

  • 详解Java枚举与接口常量和类常量的区别

    目录 一个简单的需求 接口常量 类常量 枚举 什么是枚举 枚举常量 限制输入的类型 枚举可以使用==来比较吗 枚举实现单例 一个简单的需求 在我们实际开发java项目过程中,突然有一天"领导老王"给了个任务, 公司系统需要支持商品管理的需求 比如水果有:苹果,香蕉,葡萄等等,电子产品有:电脑,手机,摄像机等等 我们一般新建商品类Goods: public class Goods { /** * 商品名称 */ private String name; /** * 商品类型 */ pri

  • Java数组高级算法与Arrays类常见操作小结【排序、查找】

    本文实例讲述了Java数组高级算法与Arrays类常见操作.分享给大家供大家参考,具体如下: 冒泡排序 冒泡排序原理 冒泡排序代码: package cn.itcast_01; /* * 数组排序之冒泡排序: * 相邻元素两两比较,大的往后放,第一次完毕,最大值出现在了最大索引处 */ public class ArrayDemo { public static void main(String[] args) { // 定义一个数组 int[] arr = { 24, 69, 80, 57,

  • C#使用StreamReader和StreamWriter类读写操作文件

    StreamReader 类 (System.IO) | Microsoft 官方文档 StreamWriter 类 (System.IO) | Microsoft 官方文档 一.文本读写类: TextReader/TextWriter:文本读写,抽象类 1.TextReader文本读,其派生类: StreamReader:以一种特定的编码从字节流中读取字符. StringReader:从字符串读取. 2.TextWriter文本写,其派生类: StreamWriter:以一种特定的编码向流中写

  • java反射获取包下所有类的操作

    我就废话不多说了,大家还是直接看代码吧~ public static void main(String[] args) { try { // 获取包名下所有类 Set<Class<?>> classes = getClasses("com"); for(Class c:classes){ // 打印有RestController 的类 if(c.isAnnotationPresent(RestController.class)){ System.out.prin

  • php利用ZipArchive类操作文件的实例

    ZipArchive类是专门用于文件的压缩与解压操作的类,通过压缩文件可以达到节省磁盘空间的目的,并且压缩文件体积更小,便于网络传输. 在ZipArchive类中我们主要使用如下方法: 1:open(打开一个压缩包文件) $zip = new \ZipArchive; $zip->open('test_new.zip', \ZipArchive::CREATE) 参数说明: 第一个参数:要打开的压缩包文件 第二个参数: ZIPARCHIVE::OVERWRITE总是创建一个新的文件,如果指定的z

  • 使用Java7的Files工具类和Path接口来访问文件的方法

    在日期的java版本中,都是通过File类来访问文件,但是File类的操作有限,不能利用特定文件系统的特性,性能也不高.在访问出错时,很多都是直接返回false,并没有给出异常信息.因此Java7新增了Files和Paths两个工具类来操作文件.Files类中包含了大量的静态方法来操作文件.Java7还引入了一个Path接口,它代表的是与平台无关的平台路径,关于它的用法,下面会做详细的介绍. Paths提供了两个方法用来获取Path对象, 1.get(URL url) 2.get(String

  • Java NIO.2 使用Path接口来监听文件、文件夹变化

    Java7对NIO进行了大的改进,新增了许多功能: •对文件系统的访问提供了全面的支持 •提供了基于异步Channel的IO 这些新增的IO功能简称为 NIO.2,依然在java.nio包下. 早期的Java只提供了File类来操作文件.文件夹本身,功能有限,性能也不高. NIO.2为解决这种缺陷,提供了Path接口,并提供了Paths.Files2个工具类,这2个工具类包含的方法都是静态方法,Files类提供了大量的静态方法来操作文件.文件夹. Path接口.Paths工具类使用示例: //获

随机推荐