美文网首页程序员技术开发
使用Path与Files操作文件

使用Path与Files操作文件

作者: 岛上码农 | 来源:发表于2020-08-13 22:02 被阅读0次

    Path

    Path用于表示目录名,也可以是一个文件。路径以根目录开始的为据对路径,否则就是相对路径。例如假设使用Linux系统:

    //绝对路径:/home/temp
    Path absolute = Paths.get("/home", "temp");
    Path absolute1 = Paths.get("/home/temp");
    //相对路径:document/work/test.txt
    Path relative = Paths.get("document", "work", "test.txt");
    Path relative1 = Paths.get("document/work/test.txt");
    

    Paths.get方法接收一个或多个字符串,并使用系统默认的路径分隔符(Linux为/,Windows为\)。Path类提供了一系列方法构建目录结构。

    • resolve(Path other), resolve(String other):基于this与other构建新的Path,如果other为绝对路径,则返回other,否则返回this+other的拼接的路径:
    //path:/home
    Path path = Paths.get("/home");
    //other:/home/work
    Path other = path.resolve("work");
    //other1:/home/document
    Path other1 = path.resolve("/home/document");
    //other2:/home/work
    Path other2 = path.resolve(Paths.get("work"));
    
    • resolveSibling(Path other)resolveSibling(String other):基于this构建相对同级路径。如果other是绝对路径,返回other,否则返回this上级路径+other。
    • relativize(Path other):返回other相对于this的相对路径
    • toAbsolutePath():转换为绝对路径。
    //path:/home
    Path path = Paths.get("/home/work");
    //other:/home/document
    Path other = path.resolveSibling("document");
    //other1:/user/document
    Path other1 = path.resolveSibling("/user/document");
    //other2:/home/document/user
    Path other2 = path.resolveSibling(Paths.get("document/user"));
    //relative: ../document/user
    Path relative = path.relativize(other2);
    //absolute:/Volumns/ServerDevelop/home/work,其中/Volumns/ServerDevelop为工作目录
    Path absolute = path.toAbsolutePath();
    
    • getParent():返回上级路径,若没有上级路径则返回null。
    • getFileName():获取路径的最好一个元素,有可能是目录或文件。
    • getRoot():获取根路径。
    • toFile():从该路径创建一个File对象,注意,不是创建一个文件

    Files

    Files类使得普通文件操作变得快捷。通过Path提供的文件路径,可以直接从文件读写行、字节、字符串内容,也可以使用流的方式处理文件读写。

    • Files.readAllLines(Path path):从path中读取全部行,返回List<String>;
    • Files.readAllLines(Path path):从path中读取全部字节,返回byte数组;
    • Files.newInputStream(Path path):以path构建输入流;
    • Files.newOutputStream(Path path, OpenOption options):以path构建输出流,其中输出的方式通过options配置。
    • Files.createDirectory(Path path):创建path目录,注意指挥创建当级目录,若创建失败(如目录已存在或上级路径不存在)则会抛出异常。
    • Files.createDirectories(Path path):如果路径中间目录不存在这会将中间路径也创建。
    • Files.exists(Path path):判断路径是否存在。
    • Files.copy(Path fromPath, Path toPath, CopyOption options):将fromPath复制到toPath且保留源文件,根据options决定toPath的行为是覆盖,复制文件属性还是原子性移动文件(ATOMIC_MOVE)。
    • Files.move(Path fromPath, Path toPath, CopyOption options):将fromPath复制到toPath,不保留源文件,根据options决定toPath的行为是覆盖,复制文件属性还是原子性移动文件(ATOMIC_MOVE)。
    • Files.delete(Path path):删除path文件目录或文件。文件不存在的情况会抛异常。
    • Files.deleteIfExists(Path path):删除path文件目录或文件,文件不存在不做任何操作。
    public class FilesBasic {
        public static void main(String[] args) throws IOException {
            Path path = Paths.get("./DesignPattern/src/practise/lios/demo/alice.txt");
            //按行读取全部内容
            List<String> lines = Files.readAllLines(path);
            lines.stream().limit(10).forEach(System.out::println);
    
            //按字节读取全部内容
            byte[] bytes = Files.readAllBytes(path);
            String bytesToString = new String(bytes, 0 , 100);
            System.out.println("bytes:\n" + bytesToString);
    
            //通过InputStream读取内容
            InputStream inputStream = Files.newInputStream(path);
            Scanner inputScanner = new Scanner(inputStream, StandardCharsets.UTF_8);
            final int maxLine = 10;
            int line = 0;
            System.out.println("Input Stream:");
            while(inputScanner.hasNextLine() && line < maxLine) {
                String contents = inputScanner.nextLine();
                line ++;
                System.out.println(contents);
            }
    
            //通过OutputStream写内容到文件
            Path outputDirectory = Paths.get("output");
            //目录不存在的话创建目录
            if (! Files.exists(outputDirectory)) {
                Files.createDirectory(outputDirectory);
            }
            //将输出文件放入到新加的目录下
            Path outPath = outputDirectory.resolve("output.txt");
            boolean fileExists = Files.exists(outPath);
            OutputStream outputStream;
            if (fileExists) {
                outputStream = Files.newOutputStream(outPath, StandardOpenOption.APPEND);
            } else {
                outputStream = Files.newOutputStream(outPath, StandardOpenOption.CREATE);
            }
            outputStream.write(bytes, 0, 100);
            
            //文件移动、复制及删除
            Path parentPath = outputDirectory.toAbsolutePath().getParent();
            if (parentPath != null) {
                Path destinationFilePath = parentPath.resolve("output.txt");
                //如果文件存在则替换掉
                Path copyPath = Files.copy(outPath, destinationFilePath, StandardCopyOption.REPLACE_EXISTING);
    
                Path movedFilePath = parentPath.resolve("output1.txt");
                Path movedPath = Files.move(outPath, movedFilePath, StandardCopyOption.REPLACE_EXISTING);
    
                Files.delete(outputDirectory);
            }
        }
    }
    

    Files获取文件的基本信息

    可以通过Files的方法获取文件的如下属性:

    • 是否存在、是否是常规文件、是否是链接、是否是目录;
    • 是否可读,是否可写,是否可执行;
    • 文件大小
    • 文件的创建、最近修改和最近访问时间
    public static void showFileInfo(Path path) throws IOException {
        boolean fileExists = Files.exists(path);
        System.out.println("File exists: " + fileExists);
        if (fileExists) {
            boolean isHidden = Files.isHidden(path);
            System.out.println("File is hidden: " + isHidden);
    
            boolean isReadable = Files.isReadable(path);
            System.out.println("File is readable: " + isReadable);
    
            boolean isWritable = Files.isWritable(path);
            System.out.println("File is writable: " + isWritable);
    
            boolean isExecutable = Files.isExecutable(path);
            System.out.println("File is executable: " + isExecutable);
    
            boolean isDirectory = Files.isDirectory(path);
            System.out.println("File is directory: " + isDirectory);
    
            boolean isSymbolicLink = Files.isSymbolicLink(path);
            System.out.println("File is symbolic link: " + isSymbolicLink);
    
            boolean isRegularFile = Files.isRegularFile(path);
            if (isRegularFile) {
                long fileSize = Files.size(path);
                System.out.printf("File size: %.1fkB \n",(float)fileSize / 1024);
                //BasicFileAttributes包含上述的全部属性封装
                BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class);
                System.out.println("File size: " + attributes.size() + "bytes");
                System.out.println("File created at: " + attributes.creationTime());
                System.out.println("File last modified at: " + attributes.lastModifiedTime());
                System.out.println("File last accessed at: " + attributes.lastAccessTime());
            }
        }
    }
    

    目录遍历

    Files.list(Path path)方法将遍历path下的目录(不包含下下级目录),返回Stream<Path>对象;Files.walk(Path path)返回path下的全部目录(包含全部子孙目录),返回Stream<Path>对象。

    try (Stream<Path> entries = Files.list(path)) {
        entries.forEach(p -> {
            if (Files.isDirectory(p)) {
                System.out.println("Directory: " + p);
            } else {
                System.out.println("File: " + p);
            }
        });
    }
    
    try (Stream<Path> entries = Files.walk(path)) {
        entries.forEach(p -> {
            if (Files.isDirectory(p)) {
                System.out.println("Directory: " + p);
            } else {
                System.out.println("File: " + p);
            }
        });
    }
    

    文件访问效率

    可以通过使用BufferedInputStream或FileChannel的map方法将文件映射到内存中,从而提高访问效率。其中FileChannel的map方法支持随机访问文件内容。

    public class MemoryMapFile {
        public static long checkSumWithInputStream(Path filename) throws IOException {
            try (InputStream inputStream = Files.newInputStream(filename)) {
                CRC32 crc32 = new CRC32();
    
                int c;
                while((c = inputStream.read()) != -1) {
                    crc32.update(c);
                }
    
                return crc32.getValue();
            }
        }
    
        public static long checkSumWithBufferedInputStream(Path filename) throws IOException {
            try (BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(filename))) {
                CRC32 crc32 = new CRC32();
    
                int c;
                while((c = inputStream.read()) != -1) {
                    crc32.update(c);
                }
    
                return crc32.getValue();
            }
        }
    
        public static long checkSumWithRandomAccessFile(Path filename) throws IOException {
            try (RandomAccessFile file = new RandomAccessFile(filename.toFile(), "r")) {
                long length = file.length();
    
                CRC32 crc32 = new CRC32();
                for (long i = 0; i < length; ++i) {
                    file.seek(i);
                    int c = file.readByte();
                    crc32.update(c);
                }
    
                return crc32.getValue();
            }
        }
    
        public static long checkSumWithMappedFile(Path filename) throws IOException {
            try (FileChannel fileChannel = FileChannel.open(filename)) {
                CRC32 crc32 = new CRC32();
                int length = (int)fileChannel.size();
                MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, length);
    
                for (int i = 0; i < length; ++i) {
                    int c = buffer.get(i);
                    crc32.update(c);
                }
    
                return crc32.getValue();
            }
        }
    
        public static void main(String[] args) throws IOException {
            Path path = Paths.get("./DesignPattern/src/practise/lios/demo/alice.txt");
    
            long start = System.currentTimeMillis();
            long crcValue = checkSumWithInputStream(path);
            long end = System.currentTimeMillis();
            System.out.println("CRC value: " + Long.toHexString(crcValue));
            System.out.println("InputStream Running time: " + (end - start) + " milliseconds");
    
            start = System.currentTimeMillis();
            crcValue = checkSumWithBufferedInputStream(path);
            end = System.currentTimeMillis();
            System.out.println("CRC value: " + Long.toHexString(crcValue));
            System.out.println("BufferedInputStream Running time: " + (end - start) + " milliseconds");
    
            start = System.currentTimeMillis();
            crcValue = checkSumWithRandomAccessFile(path);
            end = System.currentTimeMillis();
            System.out.println("CRC value: " + Long.toHexString(crcValue));
            System.out.println("RandomAccessFile Running time: " + (end - start) + " milliseconds");
    
            start = System.currentTimeMillis();
            crcValue = checkSumWithMappedFile(path);
            end = System.currentTimeMillis();
            System.out.println("CRC value: " + Long.toHexString(crcValue));
            System.out.println("MappedFile Running time: " + (end - start) + " milliseconds");
        }
    }
    

    运行结果如下:

    CRC value: 11891d02
    InputStream Running time: 185 milliseconds
    CRC value: 11891d02
    BufferedInputStream Running time: 9 milliseconds
    CRC value: 11891d02
    RandomAccessFile Running time: 221 milliseconds
    CRC value: 11891d02
    MappedFile Running time: 5 milliseconds
    

    可以看到文件访问的效率为:MappedFile > BufferInputStream > InputStream > RandomAccessFile

    相关文章

      网友评论

        本文标题:使用Path与Files操作文件

        本文链接:https://www.haomeiwen.com/subject/sozidktx.html