NIO之FileChannel解读

news2024/9/28 9:22:13

目录

基本概述

打开 FileChannel

从 FileChannel 读取数据 

向 FileChannel 写数据

关闭 FileChannel

FileChannel 的 position 方法 

FileChannel 的 size 方法

FileChannel 的 truncate 方法 

FileChannel 的 force 方法

FileChannel 的 transferTo 和 transferFrom 方法

Scatter/Gather

Scattering Reads

Gathering Writes 


基本概述

FileChannel 类可以实现常用的 read,write 以及 scatter/gather 操作,同时它也提 供了很多专用于文件的新方法。这些方法中的许多都是我们所熟悉的文件操作。

下面是一个使用 FileChannel 读取数据到 Buffer 中的示例 :

RandomAccessFile既可以读取文件内容,也可以向文件输出数据。同时RandomAccessFile支持“随机访问”的方式,程序快可以直接跳转到文件的任意地方来读写数据。

public class FileChannelDemo {

    public static void main(String[] args) throws IOException {
        RandomAccessFile aFile = new
                RandomAccessFile("d:\\atguigu\\01.txt", "rw");
        FileChannel inChannel = aFile.getChannel();
        ByteBuffer buf = ByteBuffer.allocate(48);
        int bytesRead = inChannel.read(buf);
        while (bytesRead != -1) {
            System.out.println("读取: " + bytesRead);
            buf.flip();
            while (buf.hasRemaining()) {
                System.out.print((char) buf.get());
            }
            buf.clear();
            bytesRead = inChannel.read(buf);
        }
        aFile.close();
        System.out.println("操作结束");
    }
}

Buffer 通常的操作

将数据写入缓冲区

调用 buffer.flip() 反转读写模式

从缓冲区读取数据

调用 buffer.clear() 或 buffer.compact() 清除缓冲区内容 

打开 FileChannel

在使用 FileChannel 之前,必须先打开它。但是,我们无法直接打开一个 FileChannel,需要通过使用一个 InputStream、OutputStream 或 RandomAccessFile 来获取一个 FileChannel 实例。下面是通过 RandomAccessFile 打开 FileChannel 的示例:

RandomAccessFile aFile = new RandomAccessFile("d:\\atguigu\\01.txt", "rw");
FileChannel inChannel = aFile.getChannel();

从 FileChannel 读取数据 

调用多个 read()方法之一从 FileChannel 中读取数据。如:

ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buf);

首先,分配一个 Buffer。从 FileChannel 中读取的数据将被读到 Buffer 中。然后,调 用 FileChannel.read()方法。该方法将数据从 FileChannel 读取到 Buffer 中。read() 方法返回的 int 值表示了有多少字节被读到了 Buffer 中。如果返回-1,表示到了文件 末尾。

向 FileChannel 写数据

使用 FileChannel.write()方法向 FileChannel 写数据,该方法的参数是一个 Buffer。

public class FileChannelDemo {
    public static void main(String[] args) throws IOException {
        RandomAccessFile aFile = new
                RandomAccessFile("d:\\atguigu\\01.txt", "rw");
        FileChannel inChannel = aFile.getChannel();
        String newData = "New String to write to file..." +
                System.currentTimeMillis();
        ByteBuffer buf1 = ByteBuffer.allocate(48);
        buf1.clear();
        buf1.put(newData.getBytes());
        buf1.flip();

        while (buf1.hasRemaining()) {
            inChannel.write(buf1);
        }
        inChannel.close();
    }
}

 注意 FileChannel.write()是在 while 循环中调用的。因为无法保证 write()方法一次能向 FileChannel 写入多少字节,因此需要重复调用 write()方法,直到 Buffer 中已经没 有尚未写入通道的字节。

关闭 FileChannel

用完 FileChannel 后必须将其关闭。如:

inChannel.close();

FileChannel 的 position 方法 

有时可能需要在 FileChannel 的某个特定位置进行数据的读/写操作。可以通过调用 position()方法获取 FileChannel 的当前位置。也可以通过调用 position(long pos)方 法设置 FileChannel 的当前位置。

这里有两个例子:
long pos = channel.position();
channel.position(pos +123);

 如果将位置设置在文件结束符之后,然后试图从文件通道中读取数据,读方法将返回- 1 (文件结束标志)。

如果将位置设置在文件结束符之后,然后向通道中写数据,文件将撑大到当前位置并 写入数据。这可能导致“文件空洞”,磁盘上物理文件中写入的数据间有空隙。

FileChannel 的 size 方法

FileChannel 实例的 size()方法将返回该实例所关联文件的大小。如:

long fileSize = channel.size();

FileChannel 的 truncate 方法 

可以使用 FileChannel.truncate()方法截取一个文件。截取文件时,文件将中指定长度 后面的部分将被删除。如:

channel.truncate(1024);

这个例子截取文件的前 1024 个字节。

FileChannel 的 force 方法

FileChannel.force()方法将通道里尚未写入磁盘的数据强制写到磁盘上。出于性能方面的考虑,操作系统会将数据缓存在内存中,所以无法保证写入到 FileChannel 里的 数据一定会即时写到磁盘上。要保证这一点,需要调用 force()方法。

force()方法有一个 boolean 类型的参数,指明是否同时将文件元数据(权限信息等) 写到磁盘上。

FileChannel 的 transferTo 和 transferFrom 方法

通道之间的数据传输:

如果两个通道中有一个是 FileChannel,那你可以直接将数据从一个 channel 传输到 另外一个 channel。

(1)transferFrom()方法

FileChannel 的 transferFrom()方法可以将数据从源通道传输到 FileChannel 中(译 者注:这个方法在 JDK 文档中的解释为将字节从给定的可读取字节通道传输到此通道 的文件中)。下面是一个 FileChannel 完成文件间的复制的例子:

public class FileChannelWrite {
    public static void main(String args[]) throws Exception {
        RandomAccessFile aFile = new
                RandomAccessFile("d:\\atguigu\\01.txt", "rw");
        FileChannel fromChannel = aFile.getChannel();
        RandomAccessFile bFile = new
                RandomAccessFile("d:\\atguigu\\02.txt", "rw");
        FileChannel toChannel = bFile.getChannel();
        long position = 0;
        long count = fromChannel.size();
        toChannel.transferFrom(fromChannel, position, count);
        aFile.close();
        bFile.close();
        System.out.println("over!");
    }
}

方法的输入参数 position 表示从 position 处开始向目标文件写入数据,count 表示最 多传输的字节数。如果源通道的剩余空间小于 count 个字节,则所传输的字节数要小 于请求的字节数。此外要注意,在 SoketChannel 的实现中,SocketChannel 只会传 输此刻准备好的数据(可能不足 count 字节)。因此,SocketChannel 可能不会将请 求的所有数据(count 个字节)全部传输到FileChannel 中。 

(2)transferTo()方法

transferTo()方法将数据从 FileChannel 传输到其他的 channel 中。

Scatter/Gather

Java NIO 开始支持 scatter/gather,scatter/gather 用于描述从 Channel 中读取或 者写入到 Channel 的操作。

分散(scatter)从 Channel 中读取是指在读操作时将读取的数据写入多个 buffer 中。 因此,Channel 将从 Channel 中读取的数据“分散(scatter)”到多个 Buffer 中。

聚集(gather)写入 Channel 是指在写操作时将多个 buffer 的数据写入同一个 Channel,因此,Channel 将多个 Buffer 中的数据“聚集(gather)”后发送到 Channel。

scatter / gather 经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头 和消息体组成的消息,你可能会将消息体和消息头分散到不同的 buffer 中,这样你可 以方便的处理消息头和消息体。

Scattering Reads

Scattering Reads 是指数据从一个 channel 读取到多个 buffer 中。如下图描述:

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.read(bufferArray);

注意 buffer 首先被插入到数组,然后再将数组作为 channel.read() 的输入参数。 read()方法按照 buffer 在数组中的顺序将从 channel 中读取的数据写入到 buffer,当 一个 buffer 被写满后,channel 紧接着向另一个 buffer 中写。

Scattering Reads 在移动下一个 buffer 前,必须填满当前的 buffer,这也意味着它 不适用于动态消息(译者注:消息大小不固定)。换句话说,如果存在消息头和消息体, 消息头必须完成填充(例如 128byte),Scattering Reads 才能正常工作。

Gathering Writes 

Gathering Writes 是指数据从多个 buffer 写入到同一个 channel。如下图描述:

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
//write data into buffers
ByteBuffer[] bufferArray = { header, body };
channel.write(bufferArray);

buffers 数组是 write()方法的入参,write()方法会按照 buffer 在数组中的顺序,将数 据写入到 channel,注意只有 position 和 limit 之间的数据才会被写入。因此,如果 一个 buffer 的容量为 128byte,但是仅仅包含 58byte 的数据,那么这 58byte 的数 据将被写入到 channel 中。因此与 Scattering Reads 相反,Gathering Writes 能较 好的处理动态消息。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/587101.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为OD机试真题B卷 Java 实现【寻找峰值】,附详细解题思路

一、题目描述 给定一个长度为n的数组nums,请你找到峰值并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个所在位置即可。 1.峰值元素是指其值严格大于左右相邻值的元素。严格大于即不能有等于; 2.假设 nums[-1] n…

齿轮齿条平动模组的制作

1. 运动功能说明 齿轮齿条平动模组的主要运动方式为直流电机带动2个齿轮沿着齿条平行方向前进、后退。 2. 结构说明 本模组主要是由直流电机、齿轮、齿条、光轴、滑块、机架等组成。 3. 电子硬件 在这个示例中,我们采用了以下硬件,请大家参考&#xff1…

Fiddler抓不到包Fiddler chrome Edge无法抓包原因排查Fiddler死活抓不了包

一、问题描述 我这电脑上的Fiddler莫名其妙的死活就是无法抓包,换了几个版本的Fiddler都没有解决,这里参考了一些网上的教程,最终解决了,该文章算是比较详细的一篇介绍Fiddler无法抓包的教程。无法抓包主要由以下原因导致的&#…

1726_使用Python从dbc文件中提取simulink建模数据定义

全部学习汇总: GreyZhang/python_basic: My learning notes about python. (github.com) 使用dbc文件建模完成CAN通讯是一种比较高效的开发模式,不过在建模的过程中dbc文件中描述的数据需要自己去定义。使用文本编辑工具打开dbc文件可以看到&#xff0c…

中国生物科技公司【Advanced Biomed】申请纳斯达克IPO上市

来源:猛兽财经 作者:猛兽财经 猛兽财经获悉,总部位于台湾台南的生物科技公司【Advanced Biomed】近期已向美国证券交易委员会(SEC)提交招股书,申请在纳斯达克IPO上市,股票代码为(AD…

git的本地分支如何关联远程分支,比如github,gitlab,码云等

文章目录 1. 文章引言2. 本地分支如何关联远程分支2.1 远程有分支2.2 远程无分支 3. 总结 1. 文章引言 今天发布某版本的项目,准备创建个v0point1分支,后期如果修改该版本,直接在该分支上修改即可。 首先,使用git branch v0point…

SeaFormer实战:使用SeaFormer实现图像分类任务(一)

文章目录 摘要安装包安装timm安装mmcv安装 grad-cam 数据增强Cutout和MixupEMA项目结构计算mean和std生成数据集 摘要 论文翻译:https://blog.csdn.net/m0_47867638/article/details/130437649?spm1001.2014.3001.5501 官方源码:https://github.com/fu…

Vue2 创建 Vite 项目,新手教学

关于vite Vite是一种快速的现代化构建工具,可以显著提高Web应用程序的开发效率和性能。 以下是一些Vite的好处: 快速的冷启动:Vite使用原生ES模块解析器,在冷启动时会非常快速,不需要像Webpack一样构建整个应用程序。…

Linux输入输出重定向

目录 Linux输入输出重定向 Linux中的默认设备 输入输出重定向定义 输入输出重定向操作符 实用形式 标准输入、标准输出、标准错误 输出重定向案例 案例1 --- 输出重定向(覆盖) 案例2 --- 输出重定向(追加) 案例3 --- 错误…

chatgpt赋能python:Python中向上取整函数详解

Python中向上取整函数详解 对于Python中的向上取整运算,大家一定不会感到陌生。在FPython中,我们通常使用math.ceil()函数来对数值进行向上取整。本文将为大家详细介绍Python中的向上取整函数,以及如何在实践中应用。 什么是向上取整&#…

被黑客攻击了?无所谓,我会拔网线。。。

「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」:对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 最近老是有粉丝问我,被黑客攻击了,一定要拔网线吗?还有…

C/S客户端核服务端-简单收发

一、程序 首先上程序 client端的程序 #include <arpa/inet.h> #include <netinet/in.h> #include <netinet/ip.h> #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <sys/socket.h> #include <sys/type…

keep-alive 是 Vue 内置的一个组件,被用来缓存组件实例。

文章目录 简介注意点使用 keep-alive 有以下优缺点优点缺点 简介 keep-alive 是 Vue 内置的一个组件&#xff0c;被用来缓存组件实例。 使用 keep-alive 包裹动态组件时&#xff0c;被包裹的组件实例将会被缓存起来&#xff0c;而不会被销毁&#xff0c;直到 keep-alive 组件…

LSM零知识学习一、概念与框架机制

本文内容参考&#xff1a; LSM(Linux Security Modules)框架原理解析_lsm框架_pwl999的博客-CSDN博客 LSM相关知识及理解-布布扣-bubuko.com 一文了解Linux安全模块&#xff08;LSM&#xff09; - 嵌入式技术 - 电子发烧友网 在此特别致谢&#xff01; 一、什么是LSM LSM全…

HiFB 与Linux Framebuffer的对比

引言 HiFB和Linux Framebuffer是两种不同的图形缓冲区技术&#xff0c;它们在处理计算机图形显示方面有着重要的作用。以下是对这两种技术的简短定义&#xff1a; HiFB&#xff08;High-performance Intelligent FrameBuffer&#xff09;&#xff1a;HiFB是华为推出的一种高性…

Socket(五)

文章目录 1. 日志2. 如何记录日志 1. 日志 服务器要在无人看管的情况下运行很长时间&#xff0c;通常需要在很久以后对服务器中发生的情况进行调试&#xff0c;这很重要。由于这个原因&#xff0c;建议在存储服务器日志&#xff0c;至少要存储一段时间的日志。日志中通常希望记…

ARM微架构与程序编写

目录 1.流水线 2.指令流水线 3. 多核处理器​编辑 4. 工程搭建 4.1为Keil软件配置编译工具链 5.程序编写 5.1 数据处理指令 5.2 带标志位的加法ADC ADDS 5.3 跳转指令B\BL 5.4 单寄存器内存访问 5.5 批量寄存器内存访问 5.6 满减操作 1.流水线 2.指令流水线 3.…

算法基础学习笔记——⑭欧拉函数\快速幂\扩展欧几里得算法\中国剩余定理

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨欧拉函数 &#x1f353;求欧拉函数 : &#x1f353;筛法求欧拉函数 : ✨快速幂 ✨扩展欧几里得算法 ✨中国剩余定理 前言&#xff1a;算法学习笔记记录日常分享&#xff0c;需要的看哈O(∩_∩)O&#…

chatgpt赋能python:Python中的倒序输出方法

Python中的倒序输出方法 在Python中&#xff0c;倒序输出是一个经常用到的操作。倒序输出可以用于字符串、列表、元组等数据类型&#xff0c;帮助我们更方便地处理数据。 字符串的倒序输出 对于字符串&#xff0c;我们可以使用字符串切片的方法倒序输出。例如&#xff0c;我…

十二、Vben之Vue3+vite跨域代理地址实现

在vue2中使用proxy进行跨域的原理是:将域名发送给本地的服务器(启动vue项目的服务,loclahost:8080),再由本地的服务器去请求真正的服务器。 代码如下: 1.在proxy中设置要访问的地址,并重写/api为空的字符串,这里如果不重写,会相当于在代理的地址上默认加了/api,所以…