【文件IO】文件内容操作

news2024/9/21 4:33:50

读文件、写文件,都是操作系统提供了 API,在 Java 中也进行了封装,叫“文件流”/“IO流

Stream
流,形象比喻,水流/气流


水流的特点:我要通过水龙头,接 1000ml 水

  1. 直接一口气,把 1000ml 接完
  2. 一次接 500ml,分两次接完
  3. 一次接 100ml,分十次接完

IO 流的特点:我要从文件读取 100 字节文件

  1. 直接一口气,把 100 字节读完
  2. 一次读 50 字节,分两次读
  3. 一次读 10 字节,分十次

操作系统本身提供的文件读写 API 就是流式
Java 实现 IO 流,类有很多,主要分为两个大类:

字节流和字符流

  • 字节流:二进制文件使用
    • 读写数据的基本单位,就是字节
    • 一次读的 bit 不可少于 8 个,因为一个字节 8 个 bit,至少得读一个字节

表示字节流的类

  • InputStream,用来输入的

  • OutputStream,用来输出的

  • 字符流:文本文件使用

    • 一个字符不确定有几个字节,取决于实际的编码方式(GBK—一个汉字两个字节、UTF 8—一个汉字三个字节,一个字母一个字节
    • 内部做的工作更多,会自动的查询码表,把二进制数据转换成对应字符

表示字符流的类

  • Reader,输入
  • Writer,输出

比如,就像读取某个文件中的前 10 个汉字
使用字符流就可以非常方方便的实现

  • 直接读取 10 个字符
  • 字符流自动判定文件是哪种编码方式,再将字节分割好
  • 再读取对数量字节就得到 10 个汉字了

理解清楚“输入/输出”的方向(人为定义的)
把内存中的数据,放到硬盘上,视为输入还是输出呢?

  • 如果站在内存视角,就是输出
  • 如果站在硬盘视角,就是输入

后面但凡谈到输入输出,都是以 CPU 的视角来谈的,内存离 CPU 比硬盘离 CPU 更近

  • 数据远离 CPU,就是输出,将内存中的数据写到硬盘里
  • 数据靠近 CPU,就是输入,硬盘文件中的数据拿到内存里

上面四个输入输出的类,都是“抽象类
实际上真正干活的,并非这四个类
另外,Java 中,提供了很多很多类,实现上述的这四个抽象类
因为类太多了,就使得我们对于 IO 流的理解就非常费劲

  • 但虽然种类多,但其实大家的用法都差不多
  • 但凡类的名字是以“Read/Writer”结尾的,就是实现了 ReadWriter 的字符流对象
  • 但凡类的名字是以“InputStream/OutputStream”结尾的,就是实现了 InputStreamOutputStream 的字节流对象
import java.io.FileInputStream;  
import java.io.IOException;  
import java.io.InputStream;  
  
public class Demo8 {  
    //这个异常是IOException的子类,是其特殊的情况,可以直接写成 IOException    public static void main(String[] args) throws IOException {  
        //因为他是一个抽象类,所以不能直接new  
        //只能new一个实现了它的子类  
        InputStream inputStream = new FileInputStream("./text.txt");  
        //可以指定绝对路径,也可以指定相对路径,也可以指定 File 对象  
        inputStream.close();  
    }
}
  • FileNotFoundException 这个异常是 IOException 的子类,是他的一种特殊情况,可以就 throws 这个父类异常
  • 抽象类不能直接被 new,只能 new 一个实现了它的子类
    • 在这里还隐藏了一个操作,“打开文件”,针对文件进行读写,务必需要先打开(操作系统的基本要求)
  • 指定路径的时候,可以指定绝对路径,也可以指定相对路径,也可以指定 File 对象
  • 这个代码中,虽然要求文件使用完毕之后要关闭,但是局限于本代码,不写 close 也行。因为 close 之后,紧接着就是进程结束了
    • close 是释放“文件描述符表”里的元素,进程结束,意味着 PCB 就销毁了,PCB 上面的文件描述符表就整个释放了

文件资源泄露

打开文件之后,还需要关闭文件

打开文件,其实是在该进程的文件描述符表中,创建了一个新的表项

  • 进程 => PCB(进程控制块)=> 文件描述表
  • 这个表描述了该进程都需要操作哪些文件
  • 可以认为它是一个数组,数组的每个元素就是一个 struct file 对象(Linux 内核)
  • 每个结构体就描述了对应操作的文件的信息
  • 数组的下标,就称为“文件描述符

每次打开一个文件,就相当于在数组上占用一个位置,而在系统内核中,文件描述附表数组是固定长度&不可扩容的。除非主动调用 close 关闭文件,此时才会释放空间。如果代码里一直打开,不去关闭,就会使这里的资源越来越少,把数组填满了,后续再打开文件就会打开失败

这样的问题,不容易被发现,泄露不是一瞬间就泄露完耳朵,这是一个持续的过程。整个问题直到所有的资源泄露完毕,这一刻才会集中的爆发出来

import java.io.FileInputStream;  
import java.io.IOException;  
import java.io.InputStream;  
  
public class Demo8 {  
    public static void main(String[] args) {  
        try(InputStream inputStream = new FileInputStream("./text.txt")){  
                    }catch(IOException e){  
            e.printStackTrace();  
        }    
    }
}
  • close 的时候,问了防止因为一些特殊原因代码执行不到 close,有一种特殊的 try 方法——try with sources
  • 这里() 中的创建的资源可以是多个
  • try{}执行完毕,最终都会自动执行这里的 close
    • 不过想在() 里面写,必须是实现了 Closable 接口的类
      `

字节流

1. 读文件

在文件打开之后,就需要读文件了

image.png

import java.io.FileInputStream;  
import java.io.IOException;  
import java.io.InputStream;  
  
public class Demo8 {  
    public static void main(String[] args) {  
        try(InputStream inputStream = new FileInputStream("./text")){  
            while (true) {  
                int b = inputStream.read();  
                if(b == -1){  
                    //读取完毕  
                    break;  
                }                
                System.out.printf("0x%x\n",b);  
            }        
        }catch(IOException e){  
            e.printStackTrace();  
        }    
    }
}
//运行结果(text文件内容:hello)
0x68
0x65
0x6c
0x6c
0x6f
//(text文件内容:你好)
0xe4
0xbd
0xa0
0xe5
0xa5
0xbd
  • 当读到最后一个字节,就返回 -1
  • 打印字节的时候,一般都用十六进制进行表示,方便随时换算成二进制
  • hello,可在 ASCII 码表中找到对应单词;“你好”因为是六个字节,所以可以确定是 UTF8 编码方式,就可以在 UTF8 码表中对应打印出的内容拼出“你好”

频繁读取多次硬盘,当前硬盘的 IO 就耗时比较大,希望能减少 IO 的次数

byte[] buffer = new byte[1024];  
int n = inputStream.read(buffer);
  • 这个操作就会把硬盘中读取到的对应的数据,填充到 buffer 内存的字节数组中,并且尽可能填满(只需要一次 IO
    • 此处是把 buffer 形参当成了“输出型参数”
    • 平时写代码,方法的参数一般是“输入型参数”,使用返回值表示输出结果
  • 虽然是一次读的内容多了,但也比一次读 1 个字节,分很多次读效率高不少
  • 返回的 n 代表实际读到的字节数

  • 这个过程也非常类似于“去食堂打饭”
    • 拿空盘递给阿姨打饭(空 bufferread
    • 阿姨打满后,再把盘给你(read 把读完的内容装进 buffer

import java.io.FileInputStream;  
import java.io.IOException;  
import java.io.InputStream;  
  
public class Demo9 {  
    public static void main(String[] args) {  
        try(InputStream inputStream = new FileInputStream("./text")){  
            byte[] buffer = new byte[1024];  
            int n = inputStream.read(buffer);  
            for (int i = 0; i < n; i++) {  
                System.out.printf("0x%x\n",buffer[i]);  
            }        
        }catch (IOException e){  
            e.printStackTrace();  
        }    
    }
}

和 Scanner 结合:

import java.io.FileInputStream;  
import java.io.IOException;  
import java.io.InputStream;  
import java.util.Scanner;  
  
public class Demo14 {  
    public static void main(String[] args) throws IOException{  
        try(InputStream inputStream = new FileInputStream("./text")){  
            Scanner scanner = new Scanner(inputStream);  
            while(scanner.hasNextInt()){  
                System.out.println(scanner.nextInt());  
            }        
        }catch (IOException e){  
            e.printStackTrace();  
        }    
    }
}
  • 这样也可以完成文件内容的读取

2. 写文件

image.png|449

在文件中写入“你好

import java.io.*;  
  
public class Demo10 {  
    public static void main(String[] args) throws IOException {  
        try(OutputStream outputStream = new FileOutputStream("./text");){  
            outputStream.write(0xe4);  
			outputStream.write(0xbd);  
			outputStream.write(0xa0);  
			outputStream.write(0xe5);  
			outputStream.write(0xa5);  
			outputStream.write(0xbd);
        }catch (IOException e){  
            e.printStackTrace();  
        }    
    }
}
  • 这里是按照一个字节一个字节的方式进行写入的
  • 每次执行写操作的时候,都会先把之前的内容清空
    • 只要使用 OunputStream 打开文件,文件里面的内容就没了
    • 这样的操作,可能就把文件内容搞没了,并且找不回来了

还有一种“追加写”的方式,保持原内容不变,在末尾写入新内容

try(OutputStream outputStream = new FileOutputStream("./text",true);)
  • 在最后加上一个参数 true,代表开启“追加写”的方式

一次把整个字节数组都写入:

import java.io.*;  
  
public class Demo10 {  
    public static void main(String[] args) throws IOException {  
        try(OutputStream outputStream = new FileOutputStream("./text",true);){  
            byte[] buffer = new byte[] {(byte)0xe4,(byte)0xbd,(byte)0xa0,(byte)0xe5,(byte)0xa5,(byte)0xbd};  
            outputStream.write(buffer);  
        }catch (IOException e){  
            e.printStackTrace();  
        }  
    }  
}

InputStream / OutputStream 读写数据就是按照字节来操作的。如果要读写字符的话(中文),此时就绪要靠程序员手动来区分出哪几个字节是一个字符,再确保把这几个字节作为整体来写入

字符流

1. 读文件

为了方便处理字符,引入字符流
image.png|518

一次读一个字符:

import java.io.FileReader;  
import java.io.IOException;  
import java.io.Reader;  
  
public class Demo11 {  
    public static void main(String[] args) throws IOException {  
        try(Reader reader = new FileReader("./text")){  
            while (true) {  
                int c = reader.read();  
                if (c == -1) return;  
                char ch = (char) c;  
                System.out.println(ch);  
            }        }catch (IOException e){  
            e.printStackTrace();  
        }    
    }
}
  • 每次 read 读到的就是一个汉字
  • 最初按照字节来读的时候,是每个汉字三个字节,但在 Java 中一个 char 是两个字节,怎么用两个字节表示出了一个汉字?
    • 当使用 char 表示这里的汉字的时候,不再使用 UTF8,而是使用 unicode 编码方式
    • unicode 中,一个汉字就是两个字节
  • 使用字符流读取数据的过程,Java 标准库内部就自动针对数据的编码进行转码了

用字符数组一次读若干字符:

import java.io.FileReader;  
import java.io.IOException;  
import java.io.Reader;  
  
public class Demo12 {  
    public static void main(String[] args) throws IOException {  
        try(Reader reader = new FileReader("./text")){  
            char[] buffer = new char[1024];  
            int n = reader.read(buffer);  
            System.out.println(n);  
            for (int i = 0; i < n; i++) {  
                System.out.println(buffer[i]);  
            }        
        }catch (IOException e){  
            e.printStackTrace();  
        }    
    }
}

2. 写文件

import java.io.FileWriter;  
import java.io.IOException;  
import java.io.Writer;  
import java.nio.channels.WritableByteChannel;  
  
public class Demo13 {  
    public static void main(String[] args) throws IOException {  
        try(Writer writer = new FileWriter("./text")){  
            writer.write("你好世界");  
        }catch (IOException e){  
            e.printStackTrace();  
        }    
    }
}
  • 直接写入一个 String 到文件中

小结:
当前设计的这八个类,虽然数目不少,但用法都很相似
基本流程:打开 —> 读写 —> 关闭

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

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

相关文章

µC/OS-III

第一章 μCOS 简介 1.1 初识 μCOS 实际上&#xff0c;一个 CPU 核心在某一时刻只能运行一个任务&#xff0c;由于切换处理任务的速度非常快&#xff0c;因此给人造成了一种同一时刻有多个任务同时运行的错觉。 操作系统的分类方式可以由任务调度器的工作方式决定&am…

RCE漏洞基础初了解

目录 一、简介 二、php的命令执行函数 2.1 exec 2.2 passthru 2.3 shell_exec 2.4 popen 三、代码执行 3.1 php的回调后门 3.1.1 回调后门的老祖宗 3.1.2 数组造成单参数回调后门 3.1.3 绕过安全狗 ​编辑 四、来看看php中webshell奇淫技巧 4.1eval长度限制突破方法…

problem with running OpenAI Cookbook‘s chatbot

题意&#xff1a;运行 OpenAI Cookbook 的聊天机器人时遇到问题 问题背景&#xff1a; Im having trouble running the chatbot app in the OpenAI Cookbook repository. 我在运行 OpenAI Cookbook 仓库中的聊天机器人应用程序时遇到了问题。 What I tried 我尝试的内…

240810-Gradio通过HTML组件打开本地文件+防止网页跳转到about:blank

A. 最终效果 B. 可通过鼠标点击打开文件&#xff0c;但会跳转到about:blank import gradio as gr import subprocessdef open_pptx():pptx_path /Users/liuguokai/Downloads/240528-工业大模型1.pptxtry:subprocess.Popen([open, pptx_path])return "PPTX file opened s…

七、3 AD单通道(代码)

1、步骤 &#xff08;1&#xff09;开启GPIO和ADC的时钟、配置ADCCLK的分频器 &#xff08;2&#xff09;配置GPIO &#xff08;3&#xff09;配置多路开关&#xff08;把左边的通道接入到右边的规则组中&#xff09; &#xff08;4&#xff09;配置ADC转换器 &#xff08;…

HarmonyOs编写一个案例实现一个照片选择(阶段进阶 四种需求 逐一完善)

需求1. .实现照片选择 并将选择好的照片展示出来 import { GoodItem } from ../06/modules;Entry Component struct PhotoPage {State message: string 实现一个相册;State List: GoodItem[] [{goods_name: dsfjlsjkfsf,goods_price: 100,goods_img: https://img1.baidu.com…

Springboot3 配置sql打印到控制台

一、pom.xml <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId><version>3.1.2</version></dependency> 二、application.yml com.lingyang.system # log4j2配…

《密码编码学与网络安全原理与实践》第十一章、第十二章、第十三章 数据完整性算法

数据完整性算法 概念消息加密实现认证对称加密提供认证公钥加密提供认证 Hash函数要求满足特点哈希函数安全性要求密码分析基于分组密码链接的Hash函数生日攻击&#xff1a; MD5SHA-256算法步骤SHA-3散列函数实现消息认证散列函数实现消息认证方法一散列函数实现消息认证方法二…

HttpClient在ASP.NET Core中的最佳实践:实现高效的HTTP请求

引言 在现代Web开发中&#xff0c;HTTP请求的高效性和可靠性对于应用的整体性能至关重要。ASP.NET Core提供了HttpClient类&#xff0c;它是一个强大且灵活的工具&#xff0c;可以用来发送HTTP请求并处理响应。然而&#xff0c;如何在ASP.NET Core中实现高效的HTTP请求&#x…

Java基础篇/IO流的介绍和了解

一、java的IO是基于流&#xff08;stream&#xff09;概念的&#xff0c;什么是流: 在Java中&#xff0c;流&#xff08;Stream&#xff09;是一种抽象的数据传输方式&#xff0c;它代表了数据的序列。流可以用于表示来自各种源的数据输入&#xff0c;以及向各种目的地发送数据…

商品信息采集技巧大公开:五种高效采集方法分享

摘要&#xff1a; 面对日益激烈的电商竞争&#xff0c;高效采集淘宝商品信息成为商家致胜的关键。本文将揭秘2024年最实用的五种淘宝商品信息采集技巧&#xff0c;助您在大数据时代抢占先机&#xff0c;提升市场竞争力。 一、为何淘宝商品信息采集如此重要&#xff1f; 在电…

新闻稿件管理系统

TOC springboot109新闻稿件管理系统 系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理新…

社区团购独立源码最新版,包含小程序前端、管理后台、数据库

介绍&#xff1a; 社区拼团&#xff0c;社区电商等形式的新型社交电商平台源码。独创双模式&#xff1a;以团长为中心&#xff0c;以小区为中心&#xff0c;首款支持社区团购加社群团购模式的团购小程序&#xff0c;线上线下双模式&#xff0c;后台自由配置配送方式。 更新说…

五分钟学会辨别葡萄酒的优劣

不论是买什么商品&#xff0c;人们都十分关心品质。比如&#xff0c;要买葡萄酒&#xff0c;你首先要确认的就是这款酒好不好。那么&#xff0c;如何才能做到快速识别葡萄酒的好坏呢&#xff1f; 一、观察外观 首先&#xff0c;观察干红葡萄酒的外观是初步判断其品质的第一步。…

IP基础(通俗易懂版)

IP 位于 TCP/IP 参考模型的第三层&#xff0c;也就是⽹络层。 ⽹络层的主要作⽤是&#xff1a;实现主机与主机之间的通信&#xff0c;也叫点对点通信。 1 、网络层&#xff08; IP) 与数据链路层 (MAC) 有什么关系呢&#xff1f; MAC 的作用&#xff1a; 实现【直连】的两个…

【乐吾乐大屏可视化组态编辑器】条件修改属性

条件修改属性 在线使用&#xff1a;https://v.le5le.com/ 如图所示&#xff0c;右边文本图元数值一直在发生变化&#xff0c;当数值大于等于600、或者小于等于200的时候&#xff0c;左侧正方形图元背景颜色变成红色&#xff0c;产生告警效果&#xff1b;当数值在200到600之间…

海康相机二次开发学习笔记1-环境配置

因为最近可以用一段时间海康加密狗,Visionpro二次开发暂时停更一段时间,开始记录一下海康相机二次开发的学习笔记. 环境配置 1. 创建项目 打开Visual Studio,新建.NetFramework项目,选择WindowsForms,点击下一步,选择项目名称,点击下一步,点击确定打开项目属性,点击生成选项…

系统编程-认识Linux及常用指令

1 认识Linux及常用指令 主要学习函数 一、认识linux Linux 是由 UNIX 发展而来的&#xff0c;UNIX 是由程序员设计&#xff0c;它的主要服务对象也是程序员。Linux 继承了 UNIX 的设计目标。 1、linux系统的主要特性 多用户多任务开源 免费 大家都可以用庞大的社区群支持多…

Linux os下制作deb包

在 linux os下制作 .deb 包涉及以下几个主要步骤。假设已经有一个应用程序或软件项目&#xff0c;并且希望将它打包为一个 .deb 包。 1. 准备源代码和构建环境 首先&#xff0c;需要准备好应用程序的源代码&#xff0c;并确保它可以在开发环境中成功编译和运行。 2. 创建目录…

Istio学习整理

一、Service Mesh Service Mesh 的中文译为 “服务网格” &#xff0c;是一个用于处理服务和服务之间通信的基础设施层&#xff0c;它负责为构建复杂的云原生应用传递可靠的网络请求&#xff0c;并为服务通信实现了微服务所需的基本组件功能&#xff0c;例如服务发现、负载均衡…