IO系列之File

Posted 大佛拈花-GoSaint

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IO系列之File相关的知识,希望对你有一定的参考价值。

1 File类

1.1 目录列表器

在这里我主要是参考Think in Java的内容从而做的一些总结以及扩展。Java中的IO流的设计应该说是Java中最经典的,最学院式的设计,包括它的整体架构设计以及代码的构造。 
现在让我们开始进行文件过滤吧。说到这里,说一下这个File类,从类名上看,它表示文件的意思,但是这个意思却不是准确的表达它的含义,FilePath应该来说更加的符合,它表示的就是文件的路径。好了,让我们看看当前的文件目录吧。

这个就是当前文件所在的目录。 
好了,让我先列出所有的文件列表吧。

package cn.czg.io;

import java.io.File;

/**
 * 目录列表器
 */
public class DirListDemo {
    public static void main(String[] args) {
        /** 文件表示当前目录*/
        File filePath=new File(".");
        /** 当前目录的文件名称组成一个String[] 数组*/
        String[] list = filePath.list();
        for (String listDir:list) {
            System.out.println(listDir);
            /**
             * .idea
             * pom.xml
             * spring40parent.iml
             * src
             * target
             * 测试.java
             */
        }
    }
}

上面的输出结果和文件的目录是一致的。不带参数的list()方法的功能就是返回一个该目录下的文件名的String数组。

public String[] list() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkRead(path);
    }
    if (isInvalid()) {
        return null;
    }
    return fs.list(this);
}

security是一个安全管理器对象,第一个判断是判断安全管理器是否为空,这个目的是为了分配文件的操作权限,比如读写的权限,第二个判断则是判断文件的路径时候合理或者存在。关于安全管理器我希望在有时间的时候作为单独的一个系列来研究下它的源码。在这里我希望不要深究。

1.2 文件过滤器

在大多的时候我们还是希望得到我们想要的文件,比如后缀是.java结尾的文件,在jdk中,存在着专门的一个接口,就是FilenameFilter.它表示的就是文件过滤器。

@FunctionalInterface
public interface FilenameFilter {

    boolean accept(File dir, String name);
}

当且仅当该文件名称包含在文件列表中,则返回true,其他的返回false.好了,让我们实践一把吧。 
在操作之前,我们来看看file.list()的api
 

大家可以清楚的看到,下面的list()方法带有参数,参数是一个filter,即文件过滤器。 
请看如下的代码:

/**
 * 目录列表器
 */
public class DirListDemo {
    public static void main(String[] args) {
        /** 文件表示当前目录*/
        File filePath=new File(".");
        /** 当前目录的文件名称组成一个String[] 数组*/
        //String[] list = filePath.list();
        // list方法包含了所有的文件的名称
        // 如果要调用含有参数的list方法,此时就需要一个FilenameFilter的实现类,并且实现正则匹配
        MyFilenameFilter filter=new MyFilenameFilter(".java");
        String[] list = filePath.list(filter);
        for (String dirList:list) {
            System.out.println(dirList);
        }
    }
}
class MyFilenameFilter implements FilenameFilter{
    private String regex;
    public MyFilenameFilter(String regex) {
        this.regex=regex;
    }

    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(regex);
    }
}

打印结果毫无疑问的将后缀为Java的文件帅选出来,好了,接着我们可以对刚才的代码进行优化,让它看起来比较完美。 
优化一 匿名内部类:

public class DirListDemo3 {
    public static void main(String[] args) {
        /** 文件表示当前目录*/
        File filePath=new File(".");
        String[] list = filePath.list(filter(".java"));

        for (String dirList:list) {
            System.out.println(dirList);
        }
    }
    public static FilenameFilter filter(String regex){
        return new FilenameFilter() {
            private String regexs=regex;
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(regexs);
            }
        };
    }
}

大家可以看到,上述的FilenameFilte作为方法的返回值进行了传递,此时FilenameFilte掺杂在DirListDemo3中,所以我们直接将FilenameFilte作为方法的参数直接传递也是可以的,这样也是降低了耦合: 
优化二:FilenameFilte作为参数传递:

public class DirListDemo {
    public static void main(String[] args) {
        /** 文件表示当前目录*/
        File filePath=new File(".");
        String regex=".java";
        String[] list = filePath.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(regex);
            }
        });
        for (String dirList:list) {
            System.out.println(dirList);
        }
    }
}

1.3 目录实用工具

现在我的需求就是列出当前文件下的所有的文件或者文件夹 
看到这里我们很容易想到递归的操作方式,确实如此,本例也是使用的递归的算法来完成

 

public class Directory2 {
    public static void main(String[] args) {
        File file=new File(".");
        list(file);
    }
    public static void list(File file){
        if(file.isDirectory()){
            File[] files = file.listFiles();
            if(files!=null){
                for (int i=0;i<files.length;i++){
                    // 此时files[i]表示的是一个文件或者文件目录
                    //采用递归算法,直接调用list()方法,将files[i]作为参数进行传递
                    list(files[i]);
                }
            }
        }
        System.out.println(file);
    }
}

 

在Think in java中,封装了一个工具类来完成相同的功能。但是功能要比我上述的写的完全要多。我做了相应的修改,使得代码变得可读性较好

package cn.czg.io;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Directory {
    /**
     * 该方法返回一个File[]数组,和我们之前的目录列表器是相同的,
     * 通过特定的形式返回特定的文件
     * @param dir
     * @param regex
     * @return
     */
    public static File[] local(File dir,final String regex){
        return dir.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(regex);
            }
        });
    }

    /**
     * 该方法和上述的local()方法是方法的重载,只不过此时传递的参数均为字符串,
     * 第一个参数为文件路径的目录的字符串表达形式,第二个参数相同
     * 最后调用第一个local()方法
     * @param path
     * @param regex
     * @return
     */
    public static File[] local(String path,final String regex){
        return local(new File("path"),regex);
    }

    /**
     * 定义一个静态内部类TreeInfo,表示的是文件目录树,实现Iterable,实现此接口的类就可以
     * 成为forech的目标
     */
    public static class TreeInfo implements Iterable<File>{
        /**
         * 定义两个list集合,分别是files(文件list)以及dirs(目录list)
         */
        public List<File> files=new ArrayList<>();
        public List<File> dirs=new ArrayList<>();

        /**
         * 覆盖iterator()方法,返回一个File的比较器
         * @return
         */
        @Override
        public Iterator<File> iterator() {
            return files.iterator();
        }
        void addAll(TreeInfo other){
            /**
             * 将集合files和集合dirs全部添加到对应的list当中
             */

            files.addAll(other.files);
            dirs.addAll(other.dirs);
        }

        @Override
        public String toString() {
            return "TreeInfo{" +
                    "files=" + files +
                    ", dirs=" + dirs +
                    \'}\';
        }
    }

    /**
     * 在这里分析下,TreeInfo表示的就是目录树对象,有两个字段
     * files和dirs,这两个集合分别存储文件和目录,而recurseDirs()方法返回的就是TreeInfo对象
     * 这个对象保存了文件和目录,walk()的作用就是执行。
     * @param start
     * @param regex
     * @return
     */
    public static TreeInfo walk(String start,String regex){
        return recurseDirs(new File(start),regex);
    }
    public static TreeInfo walk(File start,String regex){
        return recurseDirs(start,regex);
    }
    public static TreeInfo walk(File start){
        return recurseDirs(start,".*");
    }
    public static TreeInfo walk(String start){
        return recurseDirs(new File(start),".*");
    }

    /**
     *
     * @param startDirs
     * @param regex
     * @return
     */
    static TreeInfo recurseDirs(File startDirs, String regex) {
        TreeInfo result=new TreeInfo();
        for (File item:startDirs.listFiles()) {
            if(item.isDirectory()){
                result.dirs.add(item);
                /**
                 * 递归方法的出口,如果该文件下存在子文件或者子文件夹,
                 * 继续调用recurseDirs()方法,返回的TreeInfo目录树对象添加到
                 * list集合当中
                 */
                result.addAll(recurseDirs(item,regex));
            }else {
                /**
                 * 否则,直接帅选
                 */
                if(item.getName().endsWith(regex))
                    result.files.add(item);

            }
        }
        return result;
    }

    public static void main(String[] args) {
        File[] local = local(new File("."), ".java");
        for (File fs:local) {
            System.out.println(fs);
        }
    }
}

 

以上是关于IO系列之File的主要内容,如果未能解决你的问题,请参考以下文章

Lucene系列:LuceneUtils之CRUD

Lucene系列:LuceneUtils之同步分页

系列情节 - Geopandas

Java之文件IO流详解首卷

Java之IO输入输出

java之io之file类的常用操作