IO流
一、IO流的概述
- 什么是IO流
存储和读取数据的解决方案
I : input O : ouput
流 : 像水流一样传输数据
- IO流的作用?
用于读写数据(本地文件,网络)
- IO流按照流向可以分类哪两种流?
流出流 : 程序 ——> 文件
输入流 : 文件 ——> 程序
- IO流按照操作文件的类型可以分类哪两种流?
字节流 : 可以操作所有类型的文件
字符流 : 只能操作纯文本文件
- 什么是纯文本文件?
用Windows系统自带的记事本打开并且能读懂的文件(txt文件,md文件,xml文件,lrc文件等)
二、FileOutputStream的介绍
FileOutputStream书写细节
创建字节输出流对象
细节1:参数是字符串表示的路径或者是FIle对象都可以
细节2: 如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的。
细节3: 如果文件已经存在,则会清空文件写数据
细节:write方法的参数是整数,但是实际上写到本地文件中的是整数的ASCII上对应的字符释放资源
每次使用完流之后都要释放资源
换行 和 续写
换行写:
再次写出一个换行符号就可以了
Windows: \r\n
早期的dos系统当中的回车是将光标放到这一行的开头,
换行才是真正的将光标移到下一行
Linux: \n
Mac: \r
细节:
在Windows系统当中,java对回车换行进行了优化。
虽然完整的是\r\n,但是我们写其中一个\r或者\n.
java也可以实现换行,因为java在底层会补全。
建议:
不要省略,还是写全了。
续写:
如果想要续写,打开续写开关即可
开关位置:创建对象的第二个参数
默认false:表示关闭续写,此时创建对象就会清空文件
手动传递true:表示打开续写,此时创建对象不会清空文件
总结
FileOutputStream作用
可以把程序中的数据写在本地文件上,是字节流的基本流
书写步骤
创建对象,写出数据,释放资源
三步操作的细节
1)创建对象:文件存在(会清空内容,打开续写开关就可以在后面append)、文件不存在(会创建文件、但是需要保证父级文件存在,否则会报错)、追加写入
2)写出数据:写出整数(实际是ASCII表中对应的字符)、写出字节数组(相当于把字节数组的全部,或者一部分写入到本地文件当中)、换行写(写出一个换行符 windows : \r\n; Linux : \n; Mac : \r;)
3)释放资源: 关闭通道
三、FileInputStream的介绍
FileInputStream书写细节
创建字节输入流对象
细节1:如果文件不存在,就会直接报错。
Java为什么会这么设计?
输出流:不存在,创建
把数据写到文件当中输入流:不存在,而是报错呢? 因为创建出来的文件是没有数据的,没有任何意义。 所以Java就没有设计这种无意义的逻辑,文件不存在直接报错。
程序中最重要的是:数据。
读取数据
细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字
细节2:读到文件末尾了,read方法返回-1。
tip:如果文件中最后一个是-1,会分两次读取,首先读取-号,然后读取1;
释放资源
细节:每次使用完流之后都要释放资源
字节输入流循环读取
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
四个汉字[阿 玮 好 帅]
总结
在计算书机中,任意数据都是以二进制的形式来存储的
计算机中最小的存储单元是一个字节
ASCII字符集中,一个英文占一个字节
简体中文版Windows,默认使用GBK字符集
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
练习
UTF-8是一个字符集吗?
不是,是Unicode字符集的一种编码方式
以下为Unicode字符集,利用UTF-8编码之后的二进制数据,请说出有几个中文,几个英文?
问题一:01001010 01100001 01110110 01100001
三个英文,零个汉字[j a v a]
问题二:01100001 01101001 11100100 10111101 10100000 11100101 10010011 10011111
两个英文,两个汉字[a i 你 哟]
总结
Unicode字符集的UTF-8编码格式
一个英文占一个字节,二进制第一位是0,转成十进制是正数
一个中文占三个字节,二进制第一位是1,第一个字节转成十进制是负数
乱码
为什么会产生乱码?
原因1:读取数据时未读完整个汉字
原因2:编码和解码时的方式不统一
如何不产生乱码?
- 不要使用字节流读取文本文件
- 编码解码时使用同一个码表,同一个编码方式
扩展
疑问:字节流读取中文会乱码,但是为什么拷贝不会乱码呢?
当使用字节流(如InputStream)读取中文字符时可能会出现乱码的情况,这是因为字节流是以字节为单位进行读取的,而中文字符通常需要多个字节来表示(例如UTF-8编码中的中文字符通常需要3个字节)。
如果你使用字节流直接读取,可能会把一个中文字符的一部分作为一个字节读取,导致解码时出现乱码。
然而,当你进行拷贝时,实际上是将原始的字节流进行了复制,不涉及字符编码和解码的过程。所以即使原始数据中存在中文字符,拷贝的过程中并不会对中文字符进行处理,因此不会出现乱码问题。
如果你希望在处理中文字符时避免乱码,可以考虑使用字符流(如Reader)和相应的字符编码(如UTF-8)来进行读取和写入,这样可以确保正确地处理中文字符。字符流会以字符为单位进行读取,而不是字节,从而避免了中文字符被拆分的问题。
五、FileReader 的介绍
FileReader书写细节
创建字符输入流对象
public FileReader(File file) 创建字符输入流关联本地文件
public FileReader(String pathname) 创建字符输入流关联本地文件细节1:如果文件不存在,就直接报错。
读取数据
public int read() 读取数据,读到末尾返回-1
public int read(char[] buffer) 读取多个数据,读到末尾返回-1
细节1:按字节进行读取,遇到中文,一次读多个字节,读取后解码,返回一个整数
细节2:读到文件末尾了,read方法返回-1。
释放资源
public int close() 释放资源/关流
六、FileWriter 的介绍
FileWriter书写细节
创建字符输出流对象
细节1:参数是字符串表示的路径或者File对象都是可以的
细节2:如果文件不存在会创建一个新的文件,但要保证父级路径是存在的
细节3:如果文件已经存在,则会清空文件,如果不想清空可以打开续写开关
写数据
细节:如果writer方法额参数是整数,但实际上写到本地文件中的是整数在字符集上对应的字符
释放资源
细节:每次使用完流之后都要释放资源
字符流原理解析
FileReader
创建字符输入流对象
底层:关联文件,并创建缓冲区(长度为8192的字节数组)
读取数据
底层:1.判断缓冲区是否有数据可以读取
2.缓冲区没有数据:就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区
如果文件中野没有数据了,返回-1
3.缓冲区有数据:就从缓冲区读取。
空参的read方法:一次读取一个字节,遇到中文一次读多个字节,并把字节源码转成十进制返回
有参的read方法:把读取字节,解码,强转三步合并了,强转之后的字符放到数组中