IO流操作

news2024/11/17 16:41:51

在这里插入图片描述

文章目录

  • 一、字符集
    • 常见字符集
    • 编码、解码操作
  • 二、IO流
    • FileInputStream
    • FileOutputStream
    • FileReader
    • FileWriter
    • 常见问题

一、字符集

常见字符集

我们的计算机底层是不可以直接存储字符的,计算机中底层只能存储二进制(0、1),同时二进制是可以转换成十进制的。
结论:计算机底层是可以表示十进制编号。计算机可以给人类字符进行编号存储,这套编号规则就是字符集。

ASCll字符集: ASCll(American Standard Code for Information Interchange),美国信息交换标准代码,包括了数字、英文、符号。
ASCll使用1个字节存储一个字符,一个字节是8位,一共可以表示128个字符信息,对于英文,数字是够用的。
在这里插入图片描述
GBK:windows系统默认的码表。兼容ASCll码表,也包含了几万个汉字,并支持繁体汉字以及部分日韩文字。
注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。

Unicode码表:unicode(统一码,万国码)是计算机科学领域里的一项业界字符编码标准,容纳世界上大多数国家的所有常见的文字和符号。
由于Unicode会先通过UTF-8、UTF-16、以及UTF-32编码成二进制在存储到计算机中,最常见的是UTF-8.

注意:
1.Unicode是万国码,以UTF-8编码后一个中文一般以三个字节的形式存储。
2.UTF-8也兼容ASCll编码表
3.我们正常情况下应该使用UTF-8的字符集编码
4.编码前和编码后的字符集需要保持一致,否则会出现乱码
5.英文和数字在任何编码中都不会乱码。
在这里插入图片描述
这里可能有的同学会问了,编码的时候又是怎么知道你是汉字去读取三个字节,知道你是字符去读一个字节。
因为我们中文的第一个字节和英文的第一个字节是有一定的存储规律的,比如我们中文的第一个字节表示为负数,英文的第一个字节表示为正数。

编码、解码操作

String编码:

方法作用
byte[] getBytes()使用平台默认字符集将String编码为字节,存储到数组中
byte[] getBytes(String charsetName)使用指定字符集将String编码为字节,存储到字节数组中

String解码:

方法作用
String(byte[] bytes)使用平台默认编码来解码字节数组来构造String
String(byte[] bytes,String charsetName)通过指定字符集解码指定字节数组来构造String
public static void main(String[] args) {
        //1.编码: 把字符串转为字节(使用默认的编码)
        String name = "abc进大厂";
        byte[] bytes = name.getBytes(); //使用默认字符集编码(UTF-8)
        System.out.println(bytes.length);
        System.out.println(Arrays.toString(bytes));
    }

在这里插入图片描述
我们可以发现英文的都是正的,汉字是负的。

public static void main(String[] args) throws UnsupportedEncodingException {
        //1.编码: 把字符串转为字节(使用指定的编码)
        String name = "abc进大厂";
        byte[] bytes = name.getBytes("GBK"); //使用指定字符集编码(GBK)
        System.out.println(bytes.length);
        System.out.println(Arrays.toString(bytes));
    }

在这里插入图片描述
由于我们GBK中一个汉字占两个字节,所以我们编码后的数组长度为9.

public static void main(String[] args) throws UnsupportedEncodingException {
        //1.编码: 把字符串转为字节(使用指定的编码)
        String name = "abc进大厂";
        byte[] bytes = name.getBytes("GBK"); //使用指定字符集编码(GBK)
        System.out.println(bytes.length);
        System.out.println(Arrays.toString(bytes));

        //2.解码: 把字节转换为对应的字符形式(使用默认的编码)
        String res = new String(bytes);
        System.out.println(res);
    }

在这里插入图片描述
由于我们编码使用的是GBK,解码使用的是UTF-8,所以我们这里的中文乱码了。
在这里插入图片描述
当我们统一了编码和解码方式之后,乱码的情况消失了。

二、IO流

IO流也称为输入,输出流,就是用来读写数据的。
I:表示input,数据从硬盘文件读入到内存的过程,称之输入(读)
O: 表示output,数据从内存写入硬盘文件的过程,称之输出(写)
在这里插入图片描述
我们java标准库的流对象,从类型上,可以分为两大类:
在这里插入图片描述
但是我们这里类都是抽象类,是不能直接用的,于是我们提供了操作文件的实现类
在这里插入图片描述
虽然这里的类很多,但是具有非常强的规律性,核心就是四个操作:
1.打开文件(构造对象)
2.关闭文件(close)
3.读文件(read),只针对 InputStream / Reader
4.写文件(write),只针对OutputStream / Writer

FileInputStream

方法作用
FileInputStream(File file)利用 File 构造文件输入流
FileInputStream(String name)利用文件路径构造文件输入流
public static void main(String[] args) throws FileNotFoundException {
        //创建一个文件字节输入流管道与源文件接通
        InputStream inputStream = new FileInputStream("./data.txt");
    }

在这里我们可以直接传路径,因为会帮我们去new 一个 File对象
在这里插入图片描述
在这里插入图片描述
我们有三种读取数据的方法:
无参数:一次读取一个字节
一个参数:我们把读取到的内容填充到字节数组当中(“输出型参数”)
三个参数:指定范围,只往字节数组填充一部分

在这里插入图片描述
在这里我们准备一个data文件。

public static void main(String[] args) throws IOException {
        //创建一个文件字节输入流管道与源文件接通
        InputStream inputStream = new FileInputStream("./data.txt");
        //按一次读一个字节的方式读取
        int a1 = inputStream.read();
        System.out.println((char)a1);
        int a2 = inputStream.read();
        System.out.println((char)a2);
        int a3 = inputStream.read();
        System.out.println((char)a3);
        int a4 = inputStream.read();
        System.out.println(a4);
    }

在这里插入图片描述
这里我们可以发现,当我们文件的数据被读完之后,再去读就会返回-1,我们来看看方法。
在这里插入图片描述
我们这里read一次读取的是一个字节,按理说用byte接收就可以了,但实际上是用int,因为byte除了要表示0 -> 255(-128 -> 127),还有一个特殊情况,就是表示文件读取结束要返回-1.

但很明显,我们这样的读效率不高,因为我们不知道文件有多少数据,所以我们采用循环的方式来读。

public static void main(String[] args) throws IOException {
        //创建一个文件字节输入流管道与源文件接通
        InputStream inputStream = new FileInputStream("./data.txt");
        int a;
        while ((a = inputStream.read()) != -1) {
            System.out.print((char)a);
        }
    }

在这里插入图片描述
在这里插入图片描述
当我们文件中有中文时,我们用字节输入流就会出现乱码,因为中文在UTF-8是按照3个字节存储的,而我们这里是一个一个字节读取的。

我们一次读一个字节可能出现什么问题?
1.性能较慢
2.读取中文字符输出无法避免乱码问题

于是我们有更搞笑的读取方法,每次读取一个字节数组的数据。我们输入管道,我们read()方法就相当于一次取一滴,read(byte[] byte)相当于我们用桶接,而且我们可以指定桶的大小。

public static void main(String[] args) throws IOException {
        //创建一个文件字节输入流管道与源文件接通
        InputStream inputStream = new FileInputStream("./data.txt");
        //定义一个字节数组读取数据
        byte[] buffer = new byte[1024]; //1KB
        }

我们的字节数组一般指定为1024大小,也就是1KB,但是为了更好的带大家学习,我这里先不指定为1024.
在这里插入图片描述
文件数据

public static void main(String[] args) throws IOException {
        //创建一个文件字节输入流管道与源文件接通
        InputStream inputStream = new FileInputStream("./data.txt");
        //定义一个字节数组读取数据
        byte[] buffer = new byte[3]; //1KB
        int len = inputStream.read(buffer);//返回读取了几个字节
        System.out.println("读取了 " +len +"字节");
        String rs = new String(buffer);
        System.out.println(rs);

        len = inputStream.read(buffer);//返回读取了几个字节
        System.out.println("读取了 " +len +"字节");
        rs = new String(buffer);
        System.out.println(rs);

        len = inputStream.read(buffer);//返回读取了几个字节
        System.out.println("读取了 " +len +"字节");
        rs = new String(buffer);
        System.out.println(rs);
    }

在这里插入图片描述
我们大家思考,为什么我们第三桶水,只读了2个字节,单打印出来了三个。
当我们读取第二桶水的时候,buffer = {a,b,c}。
当我们读取第三桶水的时候,只读了两个字节,于是只替换掉了buffer的前两个数据,buffer = {b,c,c}。
在这里插入图片描述
当我们再去读取的时候,就会返回-1,这时候我们不能去打印桶,因为桶里面仍然有数据,那我们该怎样去避免这样的情况呢?

方法作用
String(Byte[] byte,int offset,int length

我们在new String的时候,可以使用它的这个构造方法。
在这里插入图片描述
这样我们就不会打印桶中的无关数据了,但是我们这样效率还是太低,我们改进为循环的方式。

public static void main(String[] args) throws IOException {
        //创建一个文件字节输入流管道与源文件接通
        InputStream inputStream = new FileInputStream("./data.txt");
        //定义一个字节数组读取数据
        byte[] buffer = new byte[3]; //1KB
        int len; //记录读取的字节数
        while((len = inputStream.read(buffer)) != -1) {
            System.out.print(new String(buffer,0,len));
        }
    }

在这里插入图片描述
我们一次读一个字节数组可能出现什么问题?
1.性能得到了提升
2.读取中文字符输出无法避免乱码问题

那我们如何使用字节输入流解决中文乱码问题?
定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。但是如果文件过大,字节数组可能会引起内存溢出。我们官方也有提供API。

方法作用
readAllBytes()一次读取文件的全部字节

FileOutputStream

我们刚刚介绍了从硬盘读取到内存的文件字节输入流,现在介绍从内存写入到硬盘上的字节输出流。

方法作用
FileOutputStream(File file)创建字节输出流与源文件对象接通
FileOutputStream(File file,boolean append)创建字节输出流与源文件对象接通,可追加数据
FileOutputStream(String filepath)创建字节输出流与源文件路径接通
FileOutputStream((String filepath),boolean append)创建字节输出流与源文件路径接通,可追加数据

我们在每次打开文件进行写数据的时候,默认是清空文件然后写,我们传入true之后可以在文件里追加数据写。
在这里插入图片描述
同样为我们提供了三个写数据的方法:
无参数:一次写一个字节
一个参数:写一个字节数组
三个参数:写字节数组的一部分

方法作用
flush()刷新流,还可以继续写数据
close()关闭流,释放资源,再关闭之前先刷新流。一旦关闭,就不能写数据

我们在写数据时,可能不能实时写到硬盘,为了避免这种情况发生,我们在每次写完数据都刷新一下。
在这里插入图片描述

public static void main(String[] args) throws IOException {
        //创建一个文件字节输出流管道与源文件接通
        OutputStream outputStream = new FileOutputStream("./data.txt");
        //一次写一个字节
        outputStream.write('a');
        outputStream.write('b');
        outputStream.write('c');
        outputStream.close();
    }

在这里插入图片描述

public static void main(String[] args) throws IOException {
        //创建一个文件字节输出流管道与源文件接通
        OutputStream outputStream = new FileOutputStream("./data.txt");
        //一次写一个字节数组
        byte[] bytes = {'a',98,99,100};
        outputStream.write(bytes);
        outputStream.close();
    }

在这里插入图片描述
那我们如何写中文到硬盘呢?

public static void main(String[] args) throws IOException {
        //创建一个文件字节输出流管道与源文件接通
        OutputStream outputStream = new FileOutputStream("./data.txt");
        //一次写一个字节数组
        byte[] bytes = "我爱你中国".getBytes();
        outputStream.write(bytes);
        outputStream.close();
    }

在这里插入图片描述
我们可以先将字符串转为字节数组再写。
有同学可以发现,我们写数据都是写在一行了太紧凑了,那么如何换行呢?

//写换行
outputStream.write("\r\n".getBytes());

肯定有同学疑惑,不是\n就可以实现换行,为什么还要加回车\r,\n确实在windows可以达到换行操作,但是在linux可能就不太行了,使用\r\n比较稳妥。

我们现在每次写都会清空文件之前的数据,让我们来实现一下可以直接在文件后面追加写的。
在这里插入图片描述
我们只需要在创建流对象的时候,第二个参数传true即可。

FileReader

字符输入流:我们刚才所介绍的字节流,对于操作中文数据不太友好,我们现在来介绍一个非常针对中文操作的流对象|

方法作用
FileReader(File file)创建字符输人流与源文件对象接通
FileReader(File file,Charset charset)创建字符输人流与源文件对象接通,指定字符集
FileReader(String fileName)创建字符输人流与源文件路径接通
FileReader(String fileName,Charset charset)创建字符输人流与源文件路径接通,指定字符集

在这里插入图片描述

同样为我们一些读数据的方法,但是是针对字符来操作的。
在这里插入图片描述
我们准备好数据

 public static void main(String[] args) throws IOException {
        //创建一个文件字符流管道与文件接通
        Reader reader = new FileReader("./data.txt");
        int len;
        while((len = reader.read()) != -1) {
            System.out.print((char)len);
        }
    }

在这里插入图片描述
如果文件数据过多时我们一个字符一个字符读会很慢,应该一次读一个字符数组。

public static void main(String[] args) throws IOException {
        //创建一个文件字符流管道与文件接通
        Reader reader = new FileReader("./data.txt");
        char[] buffer = new char[1024];
        int len;
        while ((len = reader.read(buffer)) != -1) {
            String str = new String(buffer,0,len);
            System.out.print(str);
        }
    }

在这里插入图片描述

FileWriter

文件字符输出流

方法作用
FileWriter(File file)创建字符输出流管道与源文件对象接通
FileWriter(File file,boolean append)创建字符输出流管道与源文件接通,可追加数据
FileWriter(String filepath)创建字符输出流管道与源文件路径接通
FileWriter(String filepath,boolean append)创建字符输出流管道与源文件路径接通,可追加数据

在这里插入图片描述

public static void main(String[] args) throws IOException {
        //创建一个文件字符流管道与文件接通
        Writer writer = new FileWriter("./data.txt");
        writer.write(97);//写一个字符
        writer.write("\r\n");//换行
        writer.write("中国人");//写一个字符串
        writer.write("\r\n");//换行
        writer.write("China".toCharArray());//写一个字符数组
        writer.write("\r\n");//换行
        writer.write("陕西老铁666",0,4);//写字符串的一部分
        writer.write("\r\n");//换行
        writer.write("油泼面嘹咋咧".toCharArray(),0,3);//写字符数组的一部分
        writer.write("\r\n");//换行
        writer.close();

    }

在这里插入图片描述
如果想实现追加数据,我们可以在构造方法的第二个参数传true.

常见问题

close方法代表关闭文件,这也是一个重要的方法。
进程,在操作系统内核,使用PCB这样的数据结构来标识进程,一个线程对应一个PCB,一个进程可以对应一个PCB,也可以对应多个,我们PCB里有一个和文件息息相关的属性:文件描述符(用来记录该进程打开了那些文件。
在这里插入图片描述
我们每次打开一个文件就会占一个文件描述符表的一个位置,当我们关闭文件时,就会把文件描述符表的该位置释放。
如果我们没有close操作,会造成什么后果?
首先我们文件描述符表是有大小的,存在上限,虽然我们java有GC,但是GC操作可能不太及时,那么我们不手动close就可能将文件描述符表吃满,导致后面我们打开文件时,就会打开失败。
但是如果我们的程序中从始至终都在使用文件对象,那么我们不关闭也没事,因为当我们进程结束,PCB就会销毁,伴随着文件描述符也会销毁,对应的资源也会被操作系统自动回收。
给大家介绍一种关闭资源的方式,我们称为try with resources
在这里插入图片描述
我们将资源定义到try()括号里,这里虽然没有显示的写close,但会自动的执行close。
但不是说任何对象放到try()里都会自动释放
在这里插入图片描述
必须实现Closeable接口,该接口提供了close方法。

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

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

相关文章

Fluent UDF编译环境配置 VS2019

Fluent UDF编译环境配置 VS2019环境配置问题记录继续记录调试过程仅用一个host仅用一个node两个都放进去换个电脑继续报错记录错误环境配置 生成PATH文件的,有的没有权限在当前文件夹,可以用这个命令,还是原来的代码,就是改一下 …

趣味三角——前言和序言

目录 1. 前言 2. 序言 2.1 抄写员Ahmes,公元前1650年 2.2 古埃及的趣味数学 1. 前言 There is perhaps nothing which so occupies the middle position of mathematics as trigonometry. (也许,没有什么东西像三角学一样占据数学的中心位置…

离散数学-图论-欧拉图、哈密顿图、二部图、平面图(14)

欧拉图、哈密顿图、二部图、平面图 1 欧拉图 无向图G是欧拉图⇔\Leftrightarrow⇔G连通,且无奇度点。无向图G是半欧拉图⇔\Leftrightarrow⇔G连通,且仅有两个奇度点。有向图G是欧拉图⇔\Leftrightarrow⇔G强连通,且所有顶点的入度出度。有向图G是半欧拉图⇔\Leftrightarrow⇔…

登录时“自动填充”和“验证码”的实现

自动填充和验证码的实现需求1. 基础登录功能1.1 持久层pojo实体类:代理接口:1.2 业务层1.3 表现层login.jsp(登陆界面):LoginServlet:selectAllServlet:brand.jsp(登陆成功&#xff…

30.Isaac教程--Costmap规划器

Costmap规划器 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 文章目录Costmap规划器组件消息入门自定义图使用自定义地图改变规划器将 Costmap 添加到视线中将通道添加到配置Isaac SDK 中的标准导航规划器指示机器人在避开障碍物的同时采用最短…

赛意SMOM和金蝶云星空单据接口对接

赛意SMOM和金蝶云星空单据接口对接数据源系统:金蝶云星空金蝶K/3Cloud在总结百万家客户管理最佳实践的基础上,提供了标准的管理模式;通过标准的业务架构:多会计准则、多币别、多地点、多组织、多税制应用框架等,有效支持企业的运营…

数据结构进阶 unordered系列的效率对比

作者:小萌新 专栏:数据结构进阶 作者简介:大二学生 希望能和大家一起进步! 本篇博客简介:对比map set和unordered系列map和set的效率 unordered系列的效率对比map/set与unordered_map/unordered_set的区别map/set与uno…

HTTP简史

今天一起来研究Http协议的一些事情,通过本文你将了解到以下内容:Http协议各版本的对比和优缺点Http2.0协议相关的SPDY协议、二进制分帧协议、多路复用、首部压缩、服务推送等基本原理HTTP3.0和QUIC协议乘风破浪前往知识的海洋吧,要开船了&…

Linux常用命令——telnet命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) telnet 登录远程主机和管理(测试ip端口是否连通) 补充说明 telnet命令用于登录远程主机,对远程主机进行管理。telnet因为采用明文传送报文,安全性不好,很多Linux服务器都不开…

【Java IO流】字符流详解

文章目录1. 前言2. 字符输入流3. 字符输出流4. 字符流底层原理解析4.1 字符输入流4.2 字符输出流1. 前言 在上一篇字符集详解中我们说到了产生乱码的原因,要么是读取数据时未读完整个汉字,要么是因为编码和解码的方式不统一,对于后者我们只需…

PromQL之函数

Prometheus 版本 2.41.0 平台统一监控的介绍和调研直观感受PromQL及其数据类型PromQL之选择器和运算符PromQL之函数 PromQL 聚合函数 PromQL 的聚合函数只能用于瞬时向量,支持的聚合函数有: sum 求和min 最小值max 最大值avg 平均值group 分组&#xf…

Nginx与LUA(5)

您好,我是湘王,这是我的CSDN博客,欢迎您来,欢迎您再来~Nginx诞生以来就获赞无数,反向代理、负载均衡、流量限制与流量扩展都是它的拿手好戏。基本上是互联网应用的主流入口,和计算、即时通讯、存…

python批量下载modis数据(可筛选日期、范围、数据类型)

找了一圈下modis数据的,有的不能空间筛选有的不能下初级产品(也可能没找到),不甚满意,自己搞了个 0 前言 用到的主要依赖是selenium,下载网站是https://ladsweb.modaps.eosdis.nasa.gov,环境是…

ensp实现不同VLAN之间的通信

1.VLAN的基本配置 一个简单的VLAN实验&#xff0c;基本配置如图。 其中交换机为S5700。 在只对四台PC配置IP地址&#xff0c;交换机不用配置后&#xff0c;四台PC可以互相ping通。 下面是对交换机配置VLAN的方法 <Huawei>sys #进入系统视图 [Huawei…

钉钉搭和金蝶云星空接口打通对接实战

数据源系统:阿里宜搭宜搭是阿里巴巴自研的基于钉钉和阿里云的低代码平台&#xff0c;可让用户通过简单的拖拽、配置即可完成应用搭建&#xff0c;为每个组织提供低门槛、高效率的数字化业务应用生产新模式。有效缩减企业应用开发时间&#xff0c;助力企业数字化升级。对接目标系…

第四层:友元与函数成员别样定义

文章目录前情回顾友元友元的概念友元的目的友元的关键字友元的两种种用法全局函数做友元类做友元函数成员的别样定义有缘人学友元&#xff0c;急速突破第四层本章知识点&#xff08;图片形式&#xff09;&#x1f389;welcome&#x1f389; ✒️博主介绍&#xff1a;一名大一的…

Python怎么获取节假日信息?

“holidays” 是一个 Python 第三方库&#xff0c;它可以用来解析和处理节假日信息。 该库提供了一系列函数&#xff0c;可以用来检查某一天是否是节假日、获取某一年中所有节假日的列表等。它支持多种国家和地区的节假日&#xff0c;例如美国、加拿大、澳大利亚、英国等。 “…

【软件架构思想系列】分层架构

- 分层架构 -今天谈下架构设计中的分层思想和分层模型。架构思维概述对于架构思维本身仍然是类似系统思维&#xff0c;结构化思维&#xff0c;编程思维等诸多思维模式的一个合集。由于架构的核心作用是在业务现实世界和抽象的IT实现之间建立起一道桥梁&#xff0c;因此…

CUDA编程笔记(4)

文章目录前言1.CUDA的计时程序2.CUDA程序的计时影响GPU加速的关键总结前言 怎么才能看出使用cuda编程&#xff0c;提高了程序的性能&#xff0c;一般都是通过比较程序运行的时间来验证。所以熟悉程序的运行时间的计时&#xff0c;可以查看优化的性能效果。 1.CUDA的计时程序 …

每日亿题(面试题)

每日亿题(面试题) new Fn和new Fn()有什么区别&#xff1f; 1.首先如果是为传参数的情况下结果是相同的 2.如果是获取属性new Fn() 正常执行&#xff0c;而 new Fn 报错 3.不带括号不能给构造函数传参 分析比较 opacity: 0、visibility: hidden、display: none 优劣和适用…