一、IO流的概述

  1. 什么是IO流

​ 存储和读取数据的解决方案

​ I : input O : ouput

​ 流 : 像水流一样传输数据

  1. IO流的作用?

​ 用于读写数据(本地文件,网络)

  1. IO流按照流向可以分类哪两种流?

​ 流出流 : 程序 ——> 文件

​ 输入流 : 文件 ——> 程序

  1. IO流按照操作文件的类型可以分类哪两种流?

​ 字节流 : 可以操作所有类型的文件

​ 字符流 : 只能操作纯文本文件

  1. 什么是纯文本文件?

​ 用Windows系统自带的记事本打开并且能读懂的文件(txt文件,md文件,xml文件,lrc文件等)

二、FileOutputStream的介绍

FileOutputStream书写细节

  1. 创建字节输出流对象
    细节1:参数是字符串表示的路径或者是FIle对象都可以
    细节2: 如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的。
    细节3: 如果文件已经存在,则会清空文件

  2. 写数据
    细节:write方法的参数是整数,但是实际上写到本地文件中的是整数的ASCII上对应的字符

  3. 释放资源
    每次使用完流之后都要释放资源

换行 和 续写

换行写:

再次写出一个换行符号就可以了
Windows: \r\n
早期的dos系统当中的回车是将光标放到这一行的开头,
换行才是真正的将光标移到下一行
Linux: \n
Mac: \r
细节:
在Windows系统当中,java对回车换行进行了优化。
虽然完整的是\r\n,但是我们写其中一个\r或者\n.
java也可以实现换行,因为java在底层会补全。
建议:
不要省略,还是写全了。

续写:
如果想要续写,打开续写开关即可
开关位置:创建对象的第二个参数
默认false:表示关闭续写,此时创建对象就会清空文件
手动传递true:表示打开续写,此时创建对象不会清空文件

总结

  1. FileOutputStream作用

    可以把程序中的数据写在本地文件上,是字节流的基本流

  2. 书写步骤

    创建对象,写出数据,释放资源

  3. 三步操作的细节

    1)创建对象:文件存在(会清空内容,打开续写开关就可以在后面append)、文件不存在(会创建文件、但是需要保证父级文件存在,否则会报错)、追加写入

    2)写出数据:写出整数(实际是ASCII表中对应的字符)、写出字节数组(相当于把字节数组的全部,或者一部分写入到本地文件当中)、换行写(写出一个换行符 windows : \r\n; Linux : \n; Mac : \r;)

    3)释放资源: 关闭通道

三、FileInputStream的介绍

FileInputStream书写细节

  1. 创建字节输入流对象

    细节1:如果文件不存在,就会直接报错。
    Java为什么会这么设计?
    输出流:不存在,创建
    把数据写到文件当中

     输入流:不存在,而是报错呢?
                         因为创建出来的文件是没有数据的,没有任何意义。
                         所以Java就没有设计这种无意义的逻辑,文件不存在直接报错。
    

    程序中最重要的是:数据。

  2. 读取数据

    细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字

    细节2:读到文件末尾了,read方法返回-1。

    tip:如果文件中最后一个是-1,会分两次读取,首先读取-号,然后读取1;

  3. 释放资源

    细节:每次使用完流之后都要释放资源

字节输入流循环读取

read : 表示读取数据,而且是读取一个数据就移动一次指针

四、字符集

ASCII、GBK

细节

1)计算机中最小存储单元是一个字节

2)计算机在存储英文时,只需要128个Byte就够了,而一个字节可以存储256个数据,因此一个字节就够了

3)假设现在要存储 ‘a’ ,其对应的ASCII为 97 , 97用二进制表示为110 0001,并不能直接将110 0001直接存储进去,而是需要编码{ASCII编码规则:前面补0,补齐8位},因此真实存储在计算机里面的时0110 0001,如果现在要读取出来,就会读取到0110 0001 ,然后解码{ASCII解码规则:直接转成十进制} 得到 97,查询ASCII 读取到英文’a’

4)GBK存储规则:英文用一个字节存储,汉字用两个字节存储(可以记录65535个字符)

规则1:汉字两个字节存储

规则2:高位字节二进制一定以1开头,转成十进制之后是一个复数

练习

问题一:10111010 10111010 01100001

一个汉字一个英文[汉 a]

问题二:01100001 01100010 01100011

三个英文[a b c]

问题三:10110000 10100010 11100111 11100010 10111010 11000011 11001011 10100111

四个汉字[阿 玮 好 帅]

总结
  1. 在计算书机中,任意数据都是以二进制的形式来存储的

  2. 计算机中最小的存储单元是一个字节

  3. ASCII字符集中,一个英文占一个字节

  4. 简体中文版Windows,默认使用GBK字符集

  5. GBK字符集完全兼容ASCII字符集

    一个英文占一个字节,二进制第一位是0

    一个英文占两个字节,二进制高位字节的第一位是1

Unicode:万国码

细节

1)Unicode编码:

UTF(Unicode Transfer Format)

UTF-16编码规则:用2~4个字节保存

a —> 97 —> 00000000 01100001

UTF-32编码规则:固定使用四个字节保存

a —> 97 —>00000000 00000000 00000000 01100001

UTF-8编码规则:用1~4个字节保存

UTF-8编码方式(二进制)

0xxxxxxxx (ASCII码)

110xxxxx 10xxxxxx (两个字节)

1110xxxx 10xxxxxx 10xxxxxx (三个字节)

11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (四个字节)

a —> 97 —> 110 0001(英文采用一个字节) —> (0110 0001)

汉 —> 27721 —> 01101100 01001001(汉字采用三个字节) —>

11100110 10110001 10001001

英文:1个字节 中文:3个字节,第一个字节的首位是1

练习
  1. UTF-8是一个字符集吗?

    不是,是Unicode字符集的一种编码方式

  2. 以下为Unicode字符集,利用UTF-8编码之后的二进制数据,请说出有几个中文,几个英文?

    问题一:01001010 01100001 01110110 01100001

    三个英文,零个汉字[j a v a]

    问题二:01100001 01101001 11100100 10111101 10100000 11100101 10010011 10011111

    两个英文,两个汉字[a i 你 哟]

    总结
    1. Unicode字符集的UTF-8编码格式

      一个英文占一个字节,二进制第一位是0,转成十进制是正数

      一个中文占三个字节,二进制第一位是1,第一个字节转成十进制是负数

乱码

为什么会产生乱码?

原因1:读取数据时未读完整个汉字

原因2:编码和解码时的方式不统一

如何不产生乱码?
  1. 不要使用字节流读取文本文件
  2. 编码解码时使用同一个码表,同一个编码方式
扩展

疑问:字节流读取中文会乱码,但是为什么拷贝不会乱码呢?

当使用字节流(如InputStream)读取中文字符时可能会出现乱码的情况,这是因为字节流是以字节为单位进行读取的,而中文字符通常需要多个字节来表示(例如UTF-8编码中的中文字符通常需要3个字节)。

如果你使用字节流直接读取,可能会把一个中文字符的一部分作为一个字节读取,导致解码时出现乱码。

然而,当你进行拷贝时,实际上是将原始的字节流进行了复制,不涉及字符编码和解码的过程。所以即使原始数据中存在中文字符,拷贝的过程中并不会对中文字符进行处理,因此不会出现乱码问题。

如果你希望在处理中文字符时避免乱码,可以考虑使用字符流(如Reader)和相应的字符编码(如UTF-8)来进行读取和写入,这样可以确保正确地处理中文字符。字符流会以字符为单位进行读取,而不是字节,从而避免了中文字符被拆分的问题。

五、FileReader 的介绍

FileReader书写细节

  1. 创建字符输入流对象

    public FileReader(File file) 创建字符输入流关联本地文件
    public FileReader(String pathname) 创建字符输入流关联本地文件

    细节1:如果文件不存在,就直接报错。

  2. 读取数据

    public int read() 读取数据,读到末尾返回-1

    public int read(char[] buffer) 读取多个数据,读到末尾返回-1

    细节1:按字节进行读取,遇到中文,一次读多个字节,读取后解码,返回一个整数

    细节2:读到文件末尾了,read方法返回-1。

  3. 释放资源

    public int close() 释放资源/关流

六、FileWriter 的介绍

FileWriter书写细节

  1. 创建字符输出流对象

    细节1:参数是字符串表示的路径或者File对象都是可以的

    细节2:如果文件不存在会创建一个新的文件,但要保证父级路径是存在的

    细节3:如果文件已经存在,则会清空文件,如果不想清空可以打开续写开关

  2. 写数据

    细节:如果writer方法额参数是整数,但实际上写到本地文件中的是整数在字符集上对应的字符

  3. 释放资源

    细节:每次使用完流之后都要释放资源

字符流原理解析

FileReader
  1. 创建字符输入流对象

    底层:关联文件,并创建缓冲区(长度为8192的字节数组)

  2. 读取数据

    底层:1.判断缓冲区是否有数据可以读取

    ​ 2.缓冲区没有数据:就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区

    ​ 如果文件中野没有数据了,返回-1

    ​ 3.缓冲区有数据:就从缓冲区读取。

    ​ 空参的read方法:一次读取一个字节,遇到中文一次读多个字节,并把字节源码转成十进制返回

    ​ 有参的read方法:把读取字节,解码,强转三步合并了,强转之后的字符放到数组中

FileWriter