文件编程

作者: 我可能是个假开发 | 来源:发表于2023-12-26 11:41 被阅读0次

    一、FileChannel

    FileChannel 工作模式:阻塞模式

    不能直接打开 FileChannel,必须通过 FileInputStream、FileOutputStream 或者 RandomAccessFile 来获取 FileChannel,它们都有 getChannel 方法

    • 通过 FileInputStream 获取的 channel 只能读
    • 通过 FileOutputStream 获取的 channel 只能写
    • 通过 RandomAccessFile 是否能读写根据构造 RandomAccessFile 时的读写模式决定

    1.读取

    int readBytes = channel.read(buffer);
    会从 channel 读取数据填充 ByteBuffer,返回值表示读到了多少字节,-1 表示到达了文件的末尾

    2.写入

    SocketChannel(有写能力上限):

    ByteBuffer buffer = ...;
    buffer.put(...); // 存入数据
    buffer.flip();   // 切换读模式
    
    while(buffer.hasRemaining()) {
        channel.write(buffer);
    }
    

    在 while 中调用 channel.write 是因为 write 方法并不能保证一次将 buffer 中的内容全部写入 channel

    3.关闭

    channel 必须关闭,不过调用了 FileInputStream、FileOutputStream 或者 RandomAccessFile 的 close 方法会间接地调用 channel 的 close 方法

    4.位置

    • 获取当前位置:long pos = channel.position();
    • 设置当前位置:
    long newPos = ...;
    channel.position(newPos);
    

    设置当前位置时,如果设置为文件的末尾

    • 这时读取会返回 -1
    • 这时写入,会追加内容,但要注意如果 position 超过了文件末尾,再写入时在新内容和原末尾之间会有空洞

    5.大小

    使用 size 方法获取文件的大小

    6.强制写入

    操作系统出于性能的考虑,会将数据缓存,不是立刻写入磁盘。可以调用 force(true) 方法将文件内容和元数据(文件的权限等信息)立刻写入磁盘

    二、两个 Channel 传输数据

    public class TestFileChannelTransferTo {
        public static void main(String[] args) {
            try (
                    FileChannel from = new FileInputStream("data.txt").getChannel();
                    FileChannel to = new FileOutputStream("to.txt").getChannel();
            ) {
                    // 效率高,底层会利用操作系统的零拷贝进行优化, 2g 数据
                    from.transferTo(0,from.size(), to);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    限制2G数据优化:

    public class TestFileChannelTransferTo {
        public static void main(String[] args) {
            try (
                    FileChannel from = new FileInputStream("data.txt").getChannel();
                    FileChannel to = new FileOutputStream("to.txt").getChannel();
            ) {
                // 效率高,底层会利用操作系统的零拷贝进行优化, 2g 数据
                long size = from.size();
                // left 变量代表还剩余多少字节
                for (long left = size; left > 0; ) {
                    System.out.println("position:" + (size - left) + " left:" + left);
                    left -= from.transferTo((size - left), left, to);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    三、Path

    jdk7 引入了 Path 和 Paths 类

    • Path 用来表示文件路径
    • Paths 是工具类,用来获取 Path 实例
    Path source = Paths.get("1.txt"); // 相对路径 使用 user.dir 环境变量来定位 1.txt
    
    Path source = Paths.get("d:\\1.txt"); // 绝对路径 代表了  d:\1.txt
    
    Path source = Paths.get("d:/1.txt"); // 绝对路径 同样代表了  d:\1.txt
    
    Path projects = Paths.get("d:\\data", "projects"); // 代表了  d:\data\projects
    
    • . 代表了当前路径
    • .. 代表了上一级路径

    demo:

    d:
        |- data
            |- projects
                |- a
                |- b
    
    Path path = Paths.get("d:\\data\\projects\\a\\..\\b");
    System.out.println(path);
    System.out.println(path.normalize()); // 正常化路径
    
    d:\data\projects\a\..\b
    d:\data\projects\b
    

    四、Files

    1.检查文件是否存在

    Path path = Paths.get("helloword/data.txt");
    System.out.println(Files.exists(path));
    

    2.创建目录

    创建一级目录

    Path path = Paths.get("helloword/d1");
    Files.createDirectory(path);
    
    • 如果目录已存在,会抛异常 FileAlreadyExistsException
    • 不能一次创建多级目录,否则会抛异常 NoSuchFileException

    创建多级目录用

    Path path = Paths.get("helloword/d1/d2");
    Files.createDirectories(path);
    

    3.拷贝文件

    Path source = Paths.get("helloword/data.txt");
    Path target = Paths.get("helloword/target.txt");
    
    Files.copy(source, target);
    
    • 如果文件已存在,会抛异常 FileAlreadyExistsException

    如果希望用 source 覆盖掉 target,需要用 StandardCopyOption 来控制

    Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
    

    4.移动文件

    Path source = Paths.get("helloword/data.txt");
    Path target = Paths.get("helloword/data.txt");
    
    Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
    
    • StandardCopyOption.ATOMIC_MOVE 保证文件移动的原子性

    5.删除文件

    Path target = Paths.get("helloword/target.txt");
    Files.delete(target);
    

    如果文件不存在,会抛异常 NoSuchFileException

    6.删除目录

    Path target = Paths.get("helloword/d1");
    
    Files.delete(target);
    

    如果目录还有内容,会抛异常 DirectoryNotEmptyException

    7.遍历目录文件

    public static void main(String[] args) throws IOException {
        Path path = Paths.get("C:\\Program Files\\Java\\jdk1.8.0_91");
        AtomicInteger dirCount = new AtomicInteger();
        AtomicInteger fileCount = new AtomicInteger();
        Files.walkFileTree(path, new SimpleFileVisitor<Path>(){
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                System.out.println(dir);
                dirCount.incrementAndGet();
                return super.preVisitDirectory(dir, attrs);
            }
    
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                System.out.println(file);
                fileCount.incrementAndGet();
                return super.visitFile(file, attrs);
            }
        });
        System.out.println(dirCount);
        System.out.println(fileCount);
    }
    

    统计 jar 的数目:

    Path path = Paths.get("C:\\Program Files\\Java\\jdk1.8.0_91");
    AtomicInteger fileCount = new AtomicInteger();
    Files.walkFileTree(path, new SimpleFileVisitor<Path>(){
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
            throws IOException {
            if (file.toFile().getName().endsWith(".jar")) {
                fileCount.incrementAndGet();
            }
            return super.visitFile(file, attrs);
        }
    });
    System.out.println(fileCount); 
    

    删除多级目录:

    Path path = Paths.get("d:\\a");
    Files.walkFileTree(path, new SimpleFileVisitor<Path>(){
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            Files.delete(file);
            return super.visitFile(file, attrs);
        }
    
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            Files.delete(dir);
            return super.postVisitDirectory(dir, exc);
        }
    });
    

    拷贝多级目录:

    long start = System.currentTimeMillis();
    String source = "D:\\Snipaste-1.16.2-x64";
    String target = "D:\\Snipaste-1.16.2-x64aaa";
    
    Files.walk(Paths.get(source)).forEach(path -> {
        try {
            String targetName = path.toString().replace(source, target);
            // 是目录
            if (Files.isDirectory(path)) {
                Files.createDirectory(Paths.get(targetName));
            }
            // 是普通文件
            else if (Files.isRegularFile(path)) {
                Files.copy(path, Paths.get(targetName));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    });
    long end = System.currentTimeMillis();
    System.out.println(end - start);
    

    相关文章

      网友评论

        本文标题:文件编程

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