文件编程

作者: 我可能是个假开发 | 来源:发表于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);

相关文章

  • 文件编程

    对目录的操作 mkdir函数创建一个目录 删除一个目录 修改目录名 查询操作 打开一个目录 读取目录中的内容 关闭...

  • Python学习_IO文件操作

    在编程工作中,时常需要对各种文件进行操作。读写文件是最常见的IO编程,Python中内置了读写文件的函数。读写文件...

  • 文件编程、文件下载

    目录操作 文件操作创建目录结构 1)mkDir(路径名字):创建成功返回true,失败返回false 删除目录 1...

  • Google Java Style

    目录前言源文件基础源文件结构格式命名约定编程实践Javadoc后记 前言这份文档是Google Java编程风格规...

  • 多文件编程

    随着程序的慢慢变大,单一文件必然不能满足我们的需求,一个普遍的做法是多文件编程,这样做的目的也是为了项目组织结构更...

  • Linux文件编程

    文件概述 文件:存储在外部介质上的数据的集合,是操作系统数据管理的单位 文件分类按逻辑结构分记录文件:由具有一定结...

  • 文件编程指导

    文件访问是一个很重要的性能瓶颈。 本文主要从以下几方面来提高文件访问的性能: 文件编程建议 OX文件系统介绍 文件...

  • 分文件编程

    防止循环引用头文件 demo a.h 引用b.hb.h 引用a.ha.h a.c b.h b.c 让c代码可以在c...

  • 系统编程

    系统编程里面文件的基本类型包括,普通文件,目录文件,设备文件,管道文件,套接字文件,链接文件等。学会这些文件的基本...

  • 自定义封装清除缓存与删除子文件夹路径的两个工具类

    FileTool.h 文件 FileTool.m 文件 愿编程让这个世界更美好

网友评论

    本文标题:文件编程

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