IO流--13--RandomAccessFile

news/2024/12/26 17:51:58/

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • RandomAccessFile
    • 1.简介
    • 2.RandomAccessFile的作用有哪些?
    • 3.RandomAccessFile类有两个构造器:
    • 3.常用API介绍
  • RandomAccessFile 案例
    • 环境搭建
    • 1.read方法
    • 2.skipBytes方法
    • 3.seek方法
    • 4.write方法
  • RandomAccessFile 实现多线程拷贝大文件
    • 1.多线程拷贝
    • 2.断点续传


RandomAccessFile

1.简介

  • RandomAccessFile是Java 输入/输出流体系中功能最丰富的文件内容访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。
  • 与普通的输入/输出流不同的是,RandomAccessFile支持"随机访问"的方式,程序可以直接跳转到文件的任意地方来读写数据。
    在这里插入图片描述

2.RandomAccessFile的作用有哪些?

在这里插入图片描述

3.RandomAccessFile类有两个构造器:

  • RandomAccessFile(String name, String mode):
  • RandomAccessFile(File file, String mode)

创建一个随机访问文件的流:

(1)构造器1中name会转换为构造器2中的file,RandomAccessFile(String name, String mode)等价于RandomAccessFile(new File(name), String mode)

(2)mode – 访问模式

在这里插入图片描述
在这里插入图片描述

3.常用API介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

RandomAccessFile 案例

环境搭建

在这里插入图片描述

1.read方法

java">    public static void main(String[] args) throws Exception {RandomAccessFile raf = new RandomAccessFile("./src/main/resources/data.txt", "r");// 读取一个字节,此时已经读取了 h,data.txt 还剩 ello world!没有读取raf.read();byte[] bytes = new byte[1024];// 将从data.txt中读取的数据转存到字节数组中int len = raf.read(bytes);// 由于在 raf.read(bytes) 之前执行过一个 raf.read() 方法了,所以此时字节数组中会直接跳过第一个字节的数组// 由于UTF-8编码英文一个字母占一个字节(中文占3个字节)所以最终结果回漏掉 data.txt 中的首字母System.out.println(new String(bytes, 0, len)); // ello world!}

备注:data.txt 中一个空格也算一个字节

2.skipBytes方法

java">    public static void main(String[] args) throws Exception {RandomAccessFile raf = new RandomAccessFile("./src/main/resources/data.txt", "r");// 读取一个字节,此时已经读取了 h,data.txt 还剩 ello world!没有读取raf.read();// 相对当前读取位置(也就是 ello world!)再跳过两个字节 e 和 l,此时 data.txt 还剩 lo world! 没有读取raf.skipBytes(2);byte[] bytes = new byte[1024];int len = raf.read(bytes);System.out.println(new String(bytes, 0, len)); // lo world!}

3.seek方法

java">    public static void main(String[] args) throws Exception {RandomAccessFile raf = new RandomAccessFile("./src/main/resources/data.txt", "r");// 读取一个字节,此时已经读取了 h,data.txt 还剩 ello world!没有读取raf.read();// 相对 data.txt 原始位置(也就是 hello world!)跳过2个字节,此时 data.txt 还剩 llo world! 没有读取raf.seek(2);byte[] bytes = new byte[1024];int len = raf.read(bytes);System.out.println(new String(bytes, 0, len)); // llo world!}

4.write方法

java">    public static void main(String[] args) throws Exception {RandomAccessFile raf = new RandomAccessFile("./src/main/resources/data.txt", "rw");// 此时直接调用write方法,是从文件的第一个字节开始写, data.txt 变成了 ghplo world!// 同时此时指针也会随着写操作来到了 lraf.write("ghp".getBytes(StandardCharsets.UTF_8));// raf.seek(0);byte[] bytes = new byte[1024];int len = raf.read(bytes);// 由于之前的写操作,导致指针来到了 l,所以读操作从 l 开始读,所以最终读取的结果是 lo world!// 并不会读取到之前写入的数据System.out.println(new String(bytes, 0, len)); // lo world!}
  • 备注:想要写入之后能够读取到 data.txt 中完整的数据,可以在执行完写操作之后,执行seek(0)将指针重置为初始位置,注意如果使用skipBytes(0)是没有效果的,因为它是相对位置

RandomAccessFile 实现多线程拷贝大文件

1.多线程拷贝

  • 比较 原始输入输出流单线程拷贝大文件 和 RandomAccessFile实现多线程拷贝大文件
java">package com.ghp.file.test;import java.io.*;
import java.util.concurrent.CountDownLatch;/*** @author ghp* @title* @description*/
public class Main {public static void main(String[] args) throws Exception {long startTime = System.currentTimeMillis();
//        copyFileBySingleThread(); // 单线程拷贝 476MB 的视频耗时 6903mscopyFileByMultiThread(); // 多线程拷贝 476MB 的视频耗时 3022mslong endTime = System.currentTimeMillis();System.out.println("文件拷贝耗时: " + (endTime - startTime) + "ms");}private static void copyFileBySingleThread() throws IOException {File file = new File("./src/main/resources/data.mp4");FileInputStream fis = new FileInputStream(file);FileOutputStream fos = new FileOutputStream("./src/main/resources/data-bak1.mp4");byte[] bytes = new byte[1024];int len = -1;while ((len = fis.read(bytes)) != -1) {fos.write(bytes, 0, len);}}private static void copyFileByMultiThread() throws Exception {File file = new File("./src/main/resources/data.mp4");int threadNum = 5;// 计算每个线程需要读取的字节大小int part = (int) Math.ceil(file.length() / threadNum);// 创建线程计数器对象,用于阻塞主线程CountDownLatch latch = new CountDownLatch(threadNum);for (int i = 0; i < threadNum; i++) {final int k = i;new Thread(() -> {try {RandomAccessFile fis = new RandomAccessFile(file, "r");RandomAccessFile fos = new RandomAccessFile("./src/main/resources/data-bak.mp4", "rw");// 设置读和写的位置fis.seek(k * part);fos.seek(k * part);byte[] bytes = new byte[1024];int sum = 0;while (true) {int len = fis.read(bytes);if (len == -1){// 文件已经读完了break;}sum += len;fos.write(bytes, 0, len);if (sum >= part){// 当前线程需要读取的字节已经读完了break;}}} catch (Exception e) {throw new RuntimeException(e);}finally {// 子线程执行完毕,线程计数器减一latch.countDown();}}).start();}// 阻塞主线程,只有线程计数器归0,主线程才会继续执行latch.await();}}

注意点:

  • 文件的切分必须是向上取整,否则回存在数据遗漏。向上取整能够保证即使多读了数据也会被覆盖避免数据遗漏,不会发生数据堆叠,而如果是向下取整就会导致数据遗漏
  • 当前线程是否已经将自己那部分字节读取完毕的判断操作要在写操作之后,这样能够防止数据遗漏,判断操作放在写操作的后面,即使多写了数据,会被覆盖掉

2.断点续传

java">/*** 断点续传** @param src       源文件(需要拷贝的文件)* @param target    目标文件(拷贝后的文件)* @param threadNum 线程数*/private static void breakpointContinuation(File src, File target, int threadNum) throws Exception {// 每一个线程平均需要读取的字节数final int part = (int) Math.ceil(src.length() / threadNum);// 创建应该HashMap,用于记录每一个线程已读取的位置final Map<Integer, Integer> map = new ConcurrentHashMap<>();// 读取日志文件中的数据String[] logDatas = null;String logName = target.getCanonicalPath() + ".log";File logFile = new File(logName);if (logFile.exists()) {// 日志文件存在,则从上一次读取的位置开始读try (BufferedReader reader = new BufferedReader(new FileReader(logFile))) {String data = reader.readLine();logDatas = data.split(",");} catch (IOException e) {e.printStackTrace();}}final String[] logData = logDatas;CountDownLatch latch = new CountDownLatch(threadNum);for (int i = 0; i < threadNum; i++) {final int k = i;new Thread(() -> {try (RandomAccessFile in = new RandomAccessFile(src, "r");RandomAccessFile out = new RandomAccessFile(target, "rw");RandomAccessFile log = new RandomAccessFile(logName, "rw")) {// 从指定位置读int start = logData == null ? k * part : Integer.parseInt(logData[k]);in.seek(start);out.seek(start);byte[] bytes = new byte[1024 * 2];int sum = 0;while (true) {int len = in.read(bytes);if (len == -1) {// 文件所有字节已读完,结束读取break;}sum += len;// 记录当前线程已读取的位置map.put(k, sum + start);// 将读取到的数据、进行写入out.write(bytes, 0, len);// 将 map 中的数据持久化log.seek(0);StringJoiner joiner = new StringJoiner(",");map.forEach((key, val) -> joiner.add(String.valueOf(val)));log.write(joiner.toString().getBytes(StandardCharsets.UTF_8));if (sum + (start) >= (1 + k) * part) {// 当前线程读取的字节数量已经够了,结束读取break;}}} catch (Exception e) {e.printStackTrace();} finally {latch.countDown();}}).start();}latch.await();// 读取完成后、将日志文件删除即可new File(logName).delete();}

http://www.ppmy.cn/news/1543759.html

相关文章

计算机网络:网络层 —— IP数据报的发送和转发过程

文章目录 IP数据报的发送和转发过程主机发送IP数据报路由器转发IP数据报示例 IP数据报的发送和转发过程 IP 数据报的发送和转发过程包含以下两个过程&#xff1a; 主机发送IP数据报路由器转发IP数据报 直接交付&#xff1a;源主机与目的主机在同一网络中间接交付&#xff1a;…

奇瑞不客气智驾 晚不晚?

文/孔文清 一直很好奇&#xff1a; 尹同跃董事长的金句“智驾不客气”&#xff0c;应该怎么翻译成英语&#xff1f; 谷俊丽的演讲PPT给了我答案&#xff1a; All in Ai Cars ——全力以赴、全情投入智能化汽车。 谷俊丽是奇瑞全球创新大会上最兴奋的人之一&#xff0c;有一种闭…

Flink CDC系列之:学习理解核心概念——Data Sink

Flink CDC系列之&#xff1a;学习理解核心概念——Data Sink sink参数示例 sink 数据接收器用于应用架构更改并将更改数据写入外部系统。 数据接收器可以同时写入多个表。 参数 为了描述数据接收器&#xff0c;需要以下内容&#xff1a; 参数含义可选/必需typesink的类型&…

【计算机网络】零碎知识点(易忘 / 易错)总结回顾

一、计算机网络的发展背景 1、网络的定义 网络是指将多个计算机或设备通过通信线路、传输协议和网络设备连接起来&#xff0c;形成一个相互通信和共享资源的系统。 2、局域网 LAN 相对于广域网 WAN 而言&#xff0c;局域网 LAN 主要是指在相对较小的范围内的计算机互联网络 …

kali——tcpdump的使用

目录 前言 使用方法 监听指定网卡 将抓取的数据包保存到指定文件 读取数据包 前言 定义&#xff1a;tcpdump 是 Linux 系统下的一个强大的命令行式数据包嗅探工具&#xff0c;它能够实时捕获网络接口上的数据包&#xff0c;并将这些数据包的头部信息或完整内容显示出来或保…

CSS3新增盒子属性(三)

1、CSS3新增盒子属性 1.1 box-sizing 设置盒子的大小。 content-box&#xff1a;设置内容区的大小&#xff1b;border-box&#xff1a;设置盒子的总大小。 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><t…

《向量数据库指南》——解锁GenAI生态系统新纪元

哈喽,大家好!我是你们的老朋友,大禹智库的向量数据库高级研究员王帅旭,也是那本备受好评的《向量数据库指南》的作者。今天啊,咱们来聊聊生成式人工智能(GenAI)生态系统,特别是向量数据库在其中扮演的关键角色。这话题可有意思了,保证让你大开眼界! 自从20个月前Cha…

2024年10月28日练习(双指针算法)

一.11. 盛最多水的容器 - 力扣&#xff08;LeetCode&#xff09; 1.题目描述&#xff1a; 这个题目代表的意思就是数组上每个对应的值就相当于每条垂直线的高度&#xff0c;就相当于短板效应&#xff0c;两 个高度的线会取最短的长度因为那样水才不会漏。而两条线的数组的下标…