`
QING____
  • 浏览: 2234127 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JAVA NIO2文件操作

    博客分类:
  • JAVA
 
阅读更多

    JAVA7+对NIO进行了增强,主要在对文件操作部分做了大量的改进,体现在将File操作进行分离、封装、改进最终形成Path(Paths)、Files、FileSystem(FileSystems)三个主要类,其中Paths、Files中提供了大量便捷的静态操作方法;此外NIO2还提供了有关文件权限(属性)操作、软连接、文件查找等高级API,使得NIO2具有更全面的文件系统操作接口。JAVA IO与NIO中有关文件操作的API迁移对比表,请参考“IO与NIO文件API对比

 

Path

   文件系统都是Tree或者层级结构来组织文件的,任何一个节点可以是一个目录或者一个文件,在NIO2中称为Path,这和原来的File有很多相似之处,只是Path具有更多的表述语义。

    1、基本属性

Path path = Paths.get("/data/logs/web.log");
//属性
//获取路径中的文件名或者最后一个节点元素
System.out.printf("FileName:%s%n", path.getFileName());
//路径节点元素的格式
System.out.printf("NameCount:%s%n", path.getNameCount());
//遍历路径节点方法1
Iterator<Path> names = path.iterator();
int i = 0;
while (names.hasNext()) {
    Path name = names.next();
    System.out.printf("Name %s:%s%n",i,name.toString());
    i++;
}
//方法2
for(int j = 0; j < path.getNameCount(); j++) {
    System.out.printf("Name %s:%s%n",j,path.getName(j));
}
//父路径
System.out.printf("Parent:%s%n",path.getParent());
//跟路径,比如"/"、"C:";如果是相对路径,则返回null。
System.out.printf("Root:%s%n",path.getRoot());
//子路径,结果中不包含root,前开后闭
System.out.printf("Subpath[0,2]:%s%n",path.subpath(0,2));

 

 

FileName:web.log
NameCount:3
Name 0:data
Name 1:logs
Name 2:web.log
Name 0:data
Name 1:logs
Name 2:web.log
Parent:/data/logs
Root:/
Subpath[0,2]:data/logs

 

    2、路径转换

    1)比如“/data/logs/./web.log”、“/data/logs/../db”这种包含“冗余”路径的,有时候我们需要转换为正常路径语句,则使用“Path.normalize()”方法。

Path path = Paths.get("/data/logs/../web.log");
//输出结果:/data/web.log
System.out.printf("%s%n",path.normalize());

  

    2)如果文件需要被外部资源访问(resource),你可以通过Path.toUri()来转换,path对应的文件或者目录可以不存在,此方法不会check:

Path path = Paths.get("/data/logs/web.log");
//表示本地文件的uri,输出结果:file:///data/logs/web.log
System.out.printf("%s%n",path.toUri());

 

    3)toAbsolutePath():如果路径为相对路径,则转换为绝对路径,对于JAVA程序而言,起始路径为classpath。此方法不会检测文件是否真的存在或者有权限。

    4)其中toRealPath()是比较重要的方法,不过它会对文件是否存在、访问权限进行检测,需要捕获异常。首先检测文件是否存在、是否有权限;如果path为相对路径,则将会转换为绝对路径,同“3)”;如果是“符号连接”文件(软连接),则获取其实际target路径(除非指定了NO_FOLLOW_LINKS);如果路径中包含“冗余”,则移除,同1)。这个方法,通常用于对“用户输入的path”进行校验和转换,使用比较多。

 

    5)resolve():路径合并,当前path与参数进行路径合并,append。

    6)relativize():获取相对路径,“/data”与“/data/logs/p1”的相对路径为“logs/p1”,反之为“../../”。

 

Files与文件操作

    Files类中提供了大量静态方法,用于实现文件(目录)的创建、复制、迁移、删除以及访问文件数据等操作。这部分操作以前在File类中,对于NIO2我们通过Files即可完成大部分常规操作。

    1、检测文件或目录:Files.exists(Path)和notExists(Path)两个方法,这两个方法都会实际检测文件或者目录是否存在、以及是否有访问权限。注意:!exist() 与notExists()并不完全相等,exist可能有三种状态:如果不存在或者安全校验不通过则返回false,如果返回true则表示文件确实存在且有权限。notExists()检测类似,对于没有通过安全校验的也会返回false;当exists与notExists同时返回false时,说明文件不可以验证(即无权限),所以通常这两个方法需要同时使用。

    文件(目录)具有读、写、执行的权限,可以通过如下方法:

 

Path path = Paths.get("data/logs/web.log");
boolean isRegularExecutableFile = Files.isRegularFile(path) &
        Files.isReadable(path) & Files.isExecutable(path);
 

 

 

    有时候,两个不同的path,会指向同一个文件,比如当一个path是软连接时,此时可以使用Files.isSameFile(p1,p2)来检测,当然你可以通过Path + LinkOption相关组合获取target实际path来比较。

 

    2、删除:delete和deleteIfExists两个方法均可删除文件,前者尝试删除的文件如果不存在则会抛出异常。如果文件是软连接,则只删除连接文件而不会删除target文件,如果path为目录,则目录需要为空,否则删除失败(IOException)。在删除操作之前,最后做一些常规的检测,比如文件是否存在(有权限)、目录是否为空等。稍后我们再介绍“递归删除目录树和文件”。

 

    3、文件(目录)复制:copy(Path,Path,CopyOption...)方法可以复制文件,不过需要注意CopyOption的相关选项。当copy一个软连接文件时,默认将会复制target文件,如果只想复制软连接文件而不是target内容,可以指定NOFOLLOW_LINKS选项。CopyOption的实现类为StandardCopyOption,此外CopyOption也扩展了LinkOption,即包含NOFOLLOW_LINKS选项。如下为CopyOption选项列表:

    1)REPLACE_EXISTING:如果目标文件已经存在,则直接覆盖;如果目标文件是个软连接,则软连接文件本身被覆盖(而非连接文件的target文件);如果复制的是目录,且目标目录不为空时,则会抛出异常(DirectoryNotEmptyException),稍后介绍“递归复制目录树和文件”。此参数通常必选。复制目录时,目标目录会自动创建,源目录中如果有文件,则不会复制文件,只会创建空的目标目录。source和target,要么同时是目录、要么同时是文件。

    2)COPY_ATTRIBUTES:复制文件时,也同时复制目标文件的属性(metadata),对于文件属性(FileAttribute)的支持依赖于文件系统(和平台),不过lastModifiedTime通常会被复制。

    3)NOFOLLOW_LINKS:继承自LinkOption,表示如果文件是软连接,则不followed,即只复制连接文件,不复制其target实际文件内容。

    4)ATOMIC_MOVE:只支持move操作,copy不支持。

 

Path source = Paths.get("/data/logs/web.log");
Path target = Paths.get("/data/logs/web.log.copy");
Files.copy(source,target,REPLACE_EXISTING,COPY_ATTRIBUTES,NOFOLLOW_LINKS);
  

 

    4、迁移:move(Path,Path,CopyIOption),基本原则同copy。需要注意,如果是目录,目录中包含文件时也可以移动的(这可能依赖于平台),子目录也一起移动,但是目标目录必须为空(DirectoryNotEmptyException)。基本语义同“mv -rf”,目标目录不需要提前创建,move结束后,源目录将不存在。支持两种选项:

    1)REPLACE_EXISTING:如果目标文件已存在,则覆盖;如果目标文件是软连接,则连接文件被覆盖但是其指向不会受影响。

    2)ATOMIC_MOVE:原子复制,需要平台的文件系统支持(不支持则抛出异常),指定此参数时其他选项将被忽略;如果文件不能被原子复制(或者替换),则会抛出AtomicMoveNotSupportedException。

 

    5、打开文件

    Files类中提供了多个静态的方法,用于直接读写文件。如下为文件打开的几个选项参数(StandardOpenOptions):

    1)WRITE: 打开文件用于write访问。

    2)APPEND:在文件尾部追加数据,伴随用于WRITE或CREATE选项。

    3)TRUNCATE_EXISTING:将文件truncate为空,伴随用于WRITE选项。比如,文件存在时,将文件数据清空并重新写入。

    4)CREATE_NEW:创建新文件,如果文件已存在则抛出异常。

    5)CREATE:如果文件已存在则直接打开,否则创建文件。

    6)DELETE_ON_CLOSE:当文件操作关闭时则删除文件(close方法或者JVM关闭时),此选项适用于临时文件(临时文件不应该被其他进程并发访问)。

    7)SPARSE:创建一个“稀疏”文件,伴随使用CREATE_NEW,适用于某些特殊的文件系统比如NTFS,这些大文件允许出现“gaps”(空洞)在某些情况下可以提高性能且这些gaps不消耗磁盘空间。

    8)SYNC:对文件内容(data)或者metadata的修改,都会同步到底层存储。

    9)DSYNC:对文件内容的修改,会同步到底层存储。

    

    对于一些小文件(M级别),通常我们希望一次全部读取所有内容,而不再使用传统的方式迭代读取。

 

//全部读取小文件中的数据
//Files.readAllBytes(Paths.get("/data/web.log"));
List<String> lines = Files.readAllLines(Paths.get("/data/web.log"),Charset.forName("utf-8"));

//将准备好的数据,直接全部写入文件。(打开、写入)
Files.write(Paths.get("/data/web-1.log"),lines,Charset.forName("utf-8"),
        StandardOpenOption.APPEND,
        StandardOpenOption.CREATE));

//传统操作
try (BufferedReader reader = Files.newBufferedReader(Paths.get("/data/web.log"))) {
    while (true) {
        String line = reader.readLine();
        if (line == null) {
            break;
        }
        System.out.println(line);
    }
} catch (IOException e) {
    //
}

//传统操作
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get("/data/web.log"),Charset.forName("utf-8"),StandardOpenOption.APPEND,
        StandardOpenOption.CREATE)) {
    for(String line : lines) {
        writer.write(line);
    }
} catch (IOException e) {
    //
}

 

    此外Files中还提供了基于buffer的channel操作,返回类型为SeekableByteChannel,这种操作通常适用于读或者写,以及数据可以基于buffer进行拆封包,其他特性类似“随机访问文件”。(Files.newByteChannel(),功能同File.open())
Path path = Paths.get("/data/web.log");
//创建一个普通文件,如果已存在则抛出异常,文件属性为默认。
Files.createFile(path);

//创建临时文件,临时文件的目录和前缀可以为null,后缀如果为null时默认使用".tmp";
Files.createTempFile(null,null,".tmp");
//创建一个软连接文件
Files.createSymbolicLink(Paths.get("/data/link.log"),Paths.get("/data/web.log"));
 
 Metadata管理

    BasicFileAttributes基本接口,提供了一些基本的文件metadata,比如lastAccessTime、lastModifiedTime等,它的实现类因平台而已有:DosFileAttributes、PosixFileAttribute、UnixFileAttribute等;不同平台所能支持的属性有所不同。(在跨平台场景下,你可能需要使用FileStore来判断当前文件系统是否支持相应的FileAttributeView)

Path path = Paths.get("/data/logs/web.log");
BasicFileAttributes attributes = Files.readAttributes(path,BasicFileAttributes.class);
System.out.println("regular file:" + attributes.isRegularFile());
System.out.println("directory:" + attributes.isDirectory());
System.out.println("symbolic link:" + attributes.isSymbolicLink());
System.out.println("modified time:" + attributes.lastModifiedTime().toMillis());

//修改系统更新属性
Files.setLastModifiedTime(path,FileTime.fromMillis(System.currentTimeMillis()));

//修改其他属性
Files.setAttribute(path,"dos:hidden",true);

    属性名格式为“view-name:attribute-name”,比如“dos:hidden”;其中合法的view-name目前有“basic”、“posix”、“unix”、“owner”(所有者信息,权限),属性的列表需要根据自己的平台对应相应的Attributes类,否则会导致设置异常。

 

Path path = Paths.get("/data/logs/web.log");
PosixFileAttributes attributes = Files.readAttributes(path,PosixFileAttributes.class);

//用户组和权限
UserPrincipal userPrincipal = attributes.owner();
System.out.println(userPrincipal.toString());

GroupPrincipal groupPrincipal =  attributes.group();
System.out.println(groupPrincipal.toString());

Set<PosixFilePermission> permissions = attributes.permissions();
//将权限转换为文件属性,用于创建新的文件,目前文件权限也是一种属性
FileAttribute<Set<PosixFilePermission>> fileAttribute = PosixFilePermissions.asFileAttribute(permissions);

Files.createFile(Paths.get("/data/test.log"),fileAttribute);

//修改文件权限,可以在permissions中增减权限列表,枚举
Files.setPosixFilePermissions(path,permissions);

 

   从权限字符串中,构建权限列表

Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("-rw-r--r--");
Files.setPosixFilePermissions(path, permissions);

   权限字符串规则简述,请参考:Linux权限字符串

 

    修改文件(目录)的所有者或者所在组:

Path path = Paths.get("/data/logs/web.log");
//首先找到系统中的其他用户,根据用户名
UserPrincipal userPrincipal = path.getFileSystem().getUserPrincipalLookupService().lookupPrincipalByName("userName");
Files.setOwner(path,userPrincipal);
//或者
Files.getFileAttributeView(path,FileOwnerAttributeView.class).setOwner(userPrincipal);

//修改group
GroupPrincipal groupPrincipal = path.getFileSystem().getUserPrincipalLookupService().lookupPrincipalByGroupName("groupName");
Files.getFileAttributeView(path,PosixFileAttributeView.class).setGroup(groupPrincipal);

 

    用户自定义文件属性(即扩展文件属性),以及属性值的长度,这取决于底层文件系统或者平台是否支持。目前,主流的平台和文件系统都支持扩展文件属性,比如FreeBSD(ZFS)、Linux(ext3、ext4、ZFS)、MacOS等。默认情况下,此操作是开启的,如果已关闭,可以通过“sudo mount -o remount,user_xattr {你的文件系统挂载路径}”,否则也会抛出UnsupportedOperationException。

Path path = Paths.get("/data/logs/web.log");
UserDefinedFileAttributeView view = Files.getFileAttributeView(path,UserDefinedFileAttributeView.class);
String name = "user.mimetype";
int size = view.size(name);//我个人认为JDK应该直接支持获取属性值,而不是再周折一番
ByteBuffer buffer = ByteBuffer.allocate(size);
view.read(name,buffer);
buffer.flip();
String value = Charset.defaultCharset().decode(buffer).toString();
System.out.println(value);
//其他操作,比如list获取所有属性的列表等。

//写入或者跟新自定义属性
view.write(name,Charset.defaultCharset().encode("text/html"));

 

    FileStore是新增的API,用于描述底层存储系统,一个平台有多个FileStore,我们可以通过FileSystem获取FileStore的列表,以及每个store的存储状态、文件列表等。

Path path = Paths.get("/data/logs/web.log");
path.getFileSystem().getFileStores();//获取文件所属的文件系统的所有的存储器。

//当前文件系统所能支持的FileAttributeView,此后可以对文件使用相应的view获取或者修改属性
Set<String> viewNames = FileSystems.getDefault().supportedFileAttributeViews();
System.out.println(viewNames);//basic,unix,posix,owner,dos等

//或者,全局
//遍历所有的磁盘存储
Iterable<FileStore> fileStores = FileSystems.getDefault().getFileStores();//获取默认文件系统的所有存储器
for(FileStore store : fileStores) {
    System.out.println("----------------------------");
    System.out.println("className:" + store.getClass().getName());
    System.out.println("name:" + store.name());//磁盘名称
    System.out.println("type:" + store.type());//类型
    System.out.println("readOnly:" + store.isReadOnly());//是否为只读
    System.out.println("usableSpace:" + store.getUsableSpace() + "/" + store.getTotalSpace());
    boolean supported = store.supportsFileAttributeView(BasicFileAttributeView.class);
    //或者
    //boolean supported =  store.supportsFileAttributeView("basic");
    //fileStore的属性,不同于FileAttributes,这些属性应该与FileStore的各个实现类对应。
    Long totalSpace = (Long)store.getAttribute("totalSpace");
    System.out.println(totalSpace);

 

    可以获取每个FileStore支持的FileAttributeView,此后即可通过Files.getFileAttributeView()来获取相应的视图类。每个view都有简写名称,比如BasicFileAttributeView.name()返回其简写名称为“basic”。目前JDK支持很多FileAttributeView,比如BasicFileAttributeView、UnixFileAttributeView等等,以及允许自定义的UserDefinedFileAttributeView等。JDK将获取文件属性、修改文件属性的操作全部基于对应的FileAttributeView类来实现。

PosixFileAttributeView view = Files.getFileAttributeView(path,PosixFileAttributeView.class);
PosixFileAttributes fileAttributes = view.readAttributes();

 

    此外,根据FileStore的底层存储不同,有多种实现,可以参看FileStore的实现类,比如UnixFileStore、BsfFileStore(Mac)等,每个FileStore所能支持的attribute也不太相同,需要根据其对应的实现类获取,也可以使用FileStore.getAttribute()来获取,但是属性名需要与类中支持的属性名对应。

 

 

目录操作

    JAVA中目录也用Path表示,其基本属性与File一样。

    1、列举跟目录

//遍历文件系统的所有根目录
Iterable<Path> roots = FileSystems.getDefault().getRootDirectories();
for(Path root : roots) {
    System.out.print(root);
}

 

    2、创建、删除

Path dir = Paths.get("/data/xyz");
Files.createDirectories(dir);
Files.createDirectory(dir);

    其中createDirectory()方法是一个“严格校验”的方法,如果父路径不存在则会抛出异常,如果路径已经存在或者同名文件存在则会抛出异常,简单来说此方法只能创建最后一级目录(且此前不存在)。对于createDirectories()方法,比较兼容,会逐层校验并创建父路径,如果不存在则创建。

 

    创建目录时,也可以像文件那样,指定文件属性(包括权限)

Path dir = Paths.get("/data/xyz/12x/123x");
Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("-rw-rw----");
FileAttribute<Set<PosixFilePermission>> fileAttribute = PosixFilePermissions.asFileAttribute(permissions);
Files.createDirectories(dir,fileAttribute);

 

    3、遍历目录

Path dir = Paths.get("/data");
DirectoryStream<Path> stream = Files.newDirectoryStream(dir);
for (Path path : stream) {
    System.out.println(path);
}
stream.close();

 

    stream方式会遍历指定目录下的所有文件,包括文件。目录。连接、隐藏文件或者目录等。

 

    此外,JDK还支持基于filter模式,在遍历时过滤“非必要”的文件或者目录。

Path dir = Paths.get("/data");
DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
    @Override
    public boolean accept(Path entry) throws IOException {
        return Files.isRegularFile(entry);
    }
};
DirectoryStream<Path> stream = Files.newDirectoryStream(dir,filter);
for (Path path : stream) {
    System.out.println(path);
}
stream.close();

 

   NIO2中新增支持了基于Glob的文件过滤器,一种类似于正则表达式的匹配语法;glob是来自unlix(shell指令)用于文件匹配的表达式,很多主流语言和平台(dos、window)都支持,不过不同语言对glob的支持程度不同(需要注意)。

    1、*:匹配任意多个任意字符,包括空字符(none)。比如“/data/*.log”匹配“data”目录下所有以“.log”结尾的文件。

    2、**:类似于*,匹配任意多个字符,不过它可以越过目录分割符,此语法通常用于匹配全路径。比如:“/data/**”

    3、?:只匹配一个字符。

    4、\:转义符,用于转义特殊字符,比如“\”、“-”、“{”、“}”、“[”等等。比如需要匹配“{”那么其字面表达式为“\{”,“\\”用于匹配单斜杠。

    5、!:非,不包含,通常配合[]使用。(貌似不支持^)

    6、{}:指定一组子规则,比如:

         1){sum,moon,stars}:匹配“sun”或者“moon”或者“starts”(其一即可),子规则使用“,”分割。

         2){temp*,tmp*}:匹配以temp或者tmp开头的所有字符串。

    7、[]:匹配一组字符串中的单个字符,如果字符串集中包含“-”则匹配区间中单个字符。比如[abc]匹配“a”或者“b”或者“c”,[a-z]匹配a到z小写字符中的一个,[0-9]匹配0~9任意一个数字。可以混合使用,比如[abce-g]匹配“a”、“b”、“c”、“e”、“f”、“g”,[!a-c]匹配除a、b、c之外的任意一个字符。复合子规则使用“,”分割,比如[a-z,0-9]匹配a~z或者0~9任意一个字符。

     在[]中,“*”、“?”、“\”只匹配其自己(字面),如果“-”在[]内且是第一个字符或者在!之后,也匹配自己。

    8、文件中的前导字符、“.”将作为普通字符对待。比如“*”可以匹配“.tmp”这个文件,FIles.isHidden()可以用来检测此文件为隐藏文件。

    9、其他所有字符匹配其自己(取决于因平台而已的实现方式,包括路径分隔符等)。

 

    示例:

    1、*.html匹配任意已“.html”结尾的文件。

    2、???:匹配任意三个字符(包括数字字符)

    3、*[0-9]*:匹配包含一个数字的任意多个字符。

    4、*.{htm,html},匹配任意以“html”或者“htm”结尾的字符串。

 

    GLobbing表达式,一种比较便捷的过滤策略,对于一些简单的操作(主要是只根据文件或者路径名特性匹配时),可以不使用Filter的情况下完成,当然glob的内部实现仍然是基于封装的Filter来实现(PathMatcher);但是glob语法相对简单,JDK NIO2有关文件过滤表达式,可以同时支持glob和正则表达式。稍后介绍如何使用PathMatcher来遍历文件或者目录树。

Path dir = Paths.get("/data");
//内部,默认会对glob表达式增加前缀,glob,为了兼容PathMatcher
DirectoryStream<Path> stream = Files.newDirectoryStream(dir,"*.txt");
for (Path path : stream) {
    System.out.println(path);
}
stream.close();

 

链接文件

    硬连接(或者连接):

    1)文件有相同的 inode 及 data block;

    2)只能对已存在的文件进行创建;

    3)不能交叉文件系统进行硬链接的创建;

    4)不能对目录进行创建,只可对文件创建;

    5)删除一个硬链接文件并不影响其他有相同 inode 号的文件。

 

    软连接(符号连接):软链接与硬链接不同,若文件用户数据块中存放的内容是另一文件的路径名的指向,则该文件就是软连接。软链接就是一个普通文件,只是数据块内容有点特殊。软链接有着自己的 inode 号以及用户数据块。因此软链接的创建与使用没有类似硬链接的诸多限制:

    1)软链接有自己的文件属性及权限等;

    2)可对不存在的文件或目录创建软链接;

    3)软链接可交叉文件系统;

    4)软链接可对文件或目录创建;

    5)创建软链接时,链接计数 i_nlink 不会增加;

    6)删除软链接并不影响被指向的文件,但若被指向的原文件被删除,则相关软连接被称为死链接(即 dangling link,若被指向路径文件被重新创建,死链接可恢复为正常的软链接)。

 

    有关links的介绍,可以参考:Linux连接

    

##创建连接
ln 目标 连接名
## 创建软连接
ln -s 目标 连接名


##查看软连接目标指向,对于硬连接是不显示。
ls -l 软连接文件

##通过stat指令可以查看软硬连接的inode和block信息
##发现硬连接与目标文件的信息完全一致。
##软连接文件有单独的inode和block。

 

    创建连接

Path target = Paths.get("/data/test.log");
//target必须存在
Files.createLink(Paths.get("/data/hard-link.log"),target);
Files.createSymbolicLink(Paths.get("/data/soft-link.log"),target);

//检测文件是否为软连接问题
boolean isSymbolicLink = Files.isSymbolicLink(Paths.get("/data/soft-link.log"));

Path target2 = Files.readSymbolicLink(Paths.get("/data/soft-link.log"));
//检测是否为同一个文件
System.out.println(Files.isSameFile(target,target2));//true
System.out.println(Files.isSameFile(target,Paths.get("/data/hard-link.log")));//true
System.out.println(Files.isSameFile(target,Paths.get("/data/soft-link.log")));//true

 

    通过Files.isSameFile()比较,我们会发现,无论是软连接、硬链接,都与目标文件是同一个文件。

 

查找文件

     前文中介绍了有关PathMatcher,在JAVA NIO2中用于匹配文件的表达式,可以支持glob和正则表达式(regex)两种方式。其中glob的语法更接近linux shell,regex是更广泛、更丰富的一种方式。比如文件:/data/test.log

Path path = Paths.get("/data/test.log");
PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:**.log");

System.out.println(pathMatcher.matches(path));

//基于正则
pathMatcher = FileSystems.getDefault().getPathMatcher("regex:.*\\.log");
System.out.println(pathMatcher.matches(path));

 

    表达式规则:syntax:pattern,其中合法的syntax为“glob”和“regex”;需要注意这两种表达式的区别。内部实现也比较简单,对于glob字符串将会转化为正则表达式字符串,然后统一使用正则匹配。

 

递归遍历目录树

    曾经,使用JAVA遍历文件数是一件比较繁琐的事情,在NIO2中增加了原生提供了此操作。主要API为FileVisitor,其简单实现类为SimpleFileVisitor:

    1、preVisitDirectory:在浏览目录之前。前置操作。比如遍历并复制文件时,可以在进入目录之前,创建迁移的目标新目录。

    2、postVisitDirectory:在浏览目录中所有文件之后(浏览其他目录之前)。后置操作。

    3、visitFile:浏览文件,Path和BaseFileAttributes会传递入方法。

    4、visitFileFailed:浏览文件失败时调用,比如文件属性无法获取、目录无法打开等异常时,调用此方法,同时传入Path和Exception。

 

    简单的遍历(查找、筛选匹配)

Path dir = Paths.get("/data/redis");
Stream<Path> stream = Files.walk(dir);
stream.forEach(path -> {
    System.out.println(path);
});
stream.close();

 

    复杂遍历(遍历查找、文件迁移校验)

 

public static void main(String[] args) throws Exception{
    Path dir = Paths.get("/data/redis");
    Files.walkFileTree(dir,new Finder());
}


public static class Finder implements FileVisitor<Path> {

    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
        System.out.println("preVisitDirectory:" + dir);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        System.out.println("visitFile:" + file);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
        System.out.println("visitFileFailed:" + file + ",exception:" + (exc != null ? exc.getMessage() : "-"));
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
        System.out.println("postVisitDirectory:" + dir);
        return FileVisitResult.CONTINUE;
    }
}

 

    FileVisitResult用于表示执行状态:

    1)CONTINUE:表示继续执行(继续下一步操作)

    2)TERMINATE:终止递归遍历,其他的后续方法不会被执行,尚未浏览的文件也将不会被访问。

    3)SKIP_SUBTREE:跳过子树,即当前目录以及其子目录都将被跳过。适用在preVisitDirectory(),其他方法返回此值则等效于CONTINUE。

    4)SKIP_SIBLINGS:跳过此文件(或者目录)的同级文件或者文件,适用在postVisitDirectory(),如果preVisitDirectory返回此值,则当前目录也会跳过,且postVisitDirectory()不会被执行。

 

    其他有关walk例子,请参考:Find(匹配与查找文件)Copy(迭代复制文件树)

 

参考文档:https://docs.oracle.com/javase/tutorial/essential/io/fileio.html

分享到:
评论

相关推荐

    java NIO文件操作(中文版pdf)

    java NIO文件操作(中文版pdf),希望对大家有帮助,(转载)

    JavaNIO内存文件系统ParallelGit.zip

    ParallelGit 可以帮助你操作本地 Git仓库,允许你通过 Java 7 nio 文件系统接口来访问 Git 仓库,达到跟原生文件系统一样的体验。示例Create Repositorypublic Repository createProjectRepository() {  File ...

    自己写的Java NIO 同步不阻塞IO操作

    用nio想的一个不阻塞NIOSocket例子.。。希望对阁下有用

    java_nio.doc

    用java.nio.*进行网络编程

    Java视频教程 Java游戏服务器端开发 Netty NIO AIO Mina视频教程

    [第4节] JavaNIO流-通道1.flv [第5节] Java NIO流-通道2.flv [第6节] Java NIO流-socket通道操作.flv [第7节] Java NIO流-文件通道操作.flv [第8节] Java NIO流-选择器 .flv [第9节] Java NIO流-选择器操作.flv...

    优雅的操作文件:java.nio.file 库介绍.pdf

    但 Java 在后期版本中引入了 java.nio.file 库来提高 Java 对文件操作的能力。还增加的流的功能,似乎使得文件变成更好用了。所以本章,我们就来主要介绍 java.nio.file 中常用的类和模块,大致如下: Path 路径:...

    Java NIO原理和使用

    Java NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,...

    Java使用BIO和NIO进行文件操作对比代码示例

    主要介绍了Java使用BIO和NIO进行文件操作对比代码示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    Java NIO 网络编程初探

    NIO同样拥有文件读写,网络通信等IO操作,今天我们来看看NIO中的TCP网络通信的使用方法。 2. Java NIO 三大核心 Java NIO 有三大核心要素:Channel、Buffer和Selector。Java IO 的操作都是基于输入输出流的,而NIO则...

    nio-samples:Java NIO库使用示例

    带有Java NIO库的样本 简单的TCP / IP服务器,它使用来自客户端的数据并将其写入套接字相应的消息。 相同的回显服务器,但使用Netty框架。 相同的回显服务器,但使用NIO2异步通道。 创建10000个客户端连接的类,...

    java.nio新功能的优点

    java 1.4中推出的java.nio(新输入/输出)软件包允许像在其他低级语言——如C语言——中一样进行输入/输出。许多开发者认为这些新功能只能进行非阻塞操作;但是,这些新功能提供许多其他新鲜有趣的特性,它们包括:...

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

    下面小编就为大家分享一篇Java NIO Path接口和Files类配合操作文件的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

    javasnmp源码-nio-learn:JavaNIO使用示例,NIO的使用,TCP,UDP的简单示例

    NIO将以更加高效的方式进行文件的读写操作。 Java NIO与普通IO的主要区别 io nio 面向流 面向缓冲区(buffer,channel) 堵塞io 非堵塞io - 选择器 java nio主要的核心组件 缓冲区 buffer 通道 Channels 选择器 ...

    FileWatcher:使用 Java7 NIO 的文件系统观察者服务。 任何文件创建、删除或修改的警报

    java.nio.file 包提供了一个文件更改通知 API,称为 Watch Service API。 此 API 使您能够向监视服务注册一个(或多个)目录。 注册时,您告诉服务您对哪些类型的事件感兴趣:文件创建、文件删除或文件修改。 当...

    mina:Java Nio Apache Mina Java Nio

    在以往的java的IO操作的过程中都是面向字节流操作,并且读写操作是单向的操作,而在java1.4添加nio包新的io不再像以前的标准的io操作一样面向流操作,而NIO是面向块的操作,并且有通道、缓冲区等重要的部分组成。...

    Java流NIO

    NIO于原来的IO有相同的功能,但是他们之间的使用方式是完全不同的,NIO是面向缓冲区,面向通道的的IO操作,NIO拥有更加高效的进行文件读写。 另外NIO在网络编程可以是一个无阻塞的IO交互,可以大大提升Socket交互的...

    nio2:java.nio.file 包周围的 clojure 包装器

    此外,NIO2 的引入使开发人员可以创建自己的文件系统实现(例如 zip 文件系统或内存文件系统)。 当然,这为改进围绕 IO 代码的测试打开了大门。 不幸的是,由于大量的可变参数方法,Java 开发人员的改进 API 使 ...

    详解JavaNIO

    文件的抽象化表示,字节流以及字符流的文件操作等属于传统IO的相关内容,我们已经在前面的文章进行了较为深刻的学习了。但是传统的IO流还是有很多缺陷的,尤其它的阻塞性加上磁盘读写本来就慢,会导致CPU使用效率...

    java常用代码

    6.NioFile.java nio操作文件读写 7.ImageThumbnail.java 生成图片缩略图 8.JsonObjs.java json简单操作 9.HttpProxy.java 代理设置 10.CaptureScreen.java 截取桌面 11.XmlReaer.java 读写xml文件 12.ArrayConvers...

    java操作CSV文件类文件

    import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import com.csvreader.CsvReader; public class Test { /** * @param args */ public static void main(String[] ...

Global site tag (gtag.js) - Google Analytics