标签:File、二进制I/O
日常办公中,我们从诸多方面与诸多数据打交道,例如通过网页浏览数据,例如通过新建*.txt文件进行日记记录,例如对文件进行增删改查等等,java也提供了对这些数据的操作,众所周知文件分类:文件分为文本文件(.txt,.md,.java,.conf等等)和二进制文件(视频、图片、语音等等)。。。
1.File类
File类,顾名思义是对文件进行操作,主要的方法包括构造File对象,增删改查File对象的属性,注意这里并不涉及对文件内容的crud操作。
赛前科普:Windows中的各种文件,如.exe可执行文件,.txt文本文档等等都存放于磁盘中,通过绝对路径和相对路径标识其位置,例如C:UsersXXXDesktopqq.exe就是绝对路径,指的是从C盘根目录下的User文件夹下的XXX用户的Desktop桌面文件夹下有一个qq.exe的可执行文件,而假设现在位于Desktop文件夹下,那么.qq.exe表示当前文件夹下有一个qq.exe的文件,这就是相对路径。
【注意】绝对文件名依赖于操作系统,例如Linux下上述文件可能在/home/xxx/desktop/qq.exe文件夹下。同时,java中的相对路径起那么没有.,即如果现在的项目文件位于Desktop文件夹下,则:
qq.exe
表示C:UsersXXXDesktopqq.exe。
File类有许多方法,下面举例说明:
【注意】为Windows下的分隔符,而/为java的分隔符,故采用绝对路径时,windows中应当为:
"D:\IDEA_temp\src\Demo\qq.txt"
而采用相对路径时,应该是:
"src/Demo/qq.txt"
因为此时项目上级目录为D:IDEA_temp。
而如果写成下面这种:
"/src/Demo/qq.txt"
则会从当前盘符根目录开始执行,上级目录为根目录。
【注意2】File只是对其进行标记,该文件可以不存在。
2.文件输入/输出
2.1 文本文件的输入输出_Scanner读和PrintWriter覆盖写
【注意】PrintWriter创建并写文件之后必须关闭,否则不会保存到磁盘,建议使用try(){}方法自动保存。
【注意2】Scanner中的nextLine和next方法前如果有回车键,那么会发生逻辑错误,因此建议nextInt和nextDouble之后不要有上述两个方法。
2.2 从URL读取_java.net.url类
2.3 二进制I/O
I/O文本文件时,如写入132,会将其看出三个字符,默认是java认为是Unicode,假设文件设置为ascii码,则依次把1、3和2最终映射到ascii码;读取文件时,将其转换为UNICODE码进行读取。
而I/O二进制文件时,每次读取单位是文件的一个字节,直接转换,效率更高。
2.3.1 分类:
2.3.2 FileInputStream和FileOutputStream
二者分别继承自InputStream和OutputStream类,即各种read和write方法,默认读取一个字节。
2.3.3 过滤流_FilterInputStram和FilterOutputStream
上述的读取方式只能以字节读取或以字符读取,字节读取为int类型,那么怎么读取浮点数和字符串呢,怎么读取较大的整数呢?
下面讲讲读取基本类型值的DataInputStram和DataOutStream:
DataInputStram:字节读取→基本数据类型值/字符串;DataOutputStram则与此相反;
前面讲过,编码分为Ascii码和Unicode码,键盘上的所有单个字符可以用Ascii码表示,即所有的单个数字和英文字母,其他键盘符,但是由于中国汉字众多,因此引入兼容1个字节的Unicode码,分为UTF-8,UTF-16等等,Unicode使用2个字节存储,而UTF-8是改进的Unicode,对于Ascii码的字符用一个字节存储,反之存储2个字节,因此为了节省资源,DataOutputStream中的write方法规定:
- writeByte(v):写入参数的8位低位值;→适合ascill码;
- writeBytes(s):写入字符串每个字符的低8位值;
- writeChar(c):写入一个字符的2字节形式→都适合,特别是非ascii码字符;
- writeChars(s):依次写每个字符的2字节形式
- writeUTF(s):以UTF形式写字符串s
- 前两个字节中含有长度信息以及字符的编码方案
- 举例:writeUTF("ABFE")一共5个字符,每个字符都是ASCII码,因此可以每个字符采取一个字节的存储方式,故一共2+5=7个字节,其中还要表示每个字符由“1个字节组成”的信息,故如下→16进制形式:
- 00 05 41 42 46 45(红色表编码方案,橙色表字符串的长度-字符个数)
- 作用:将字符串转化为UTF编码,写入到输出的0101比特流;
下面是经过改进的方法,即引入缓冲区加快读写磁盘速度:
总结:
如果采用FileInputStream和FileOutputStream读取,读到末尾→input.read返回-1或者发送EOF异常;
【复制小程序】
【总结2】不难发现,可用FileInputStream【单个字节】、DataInputStream【基本值和字符串】和BufferedInputStream【加强缓冲】来实例化对象,不过由于缓冲机制就,建议使用BufferedInputStream,同理建议BufferOutputStream。
3 对象的IO
前面讲到,FileInputStream的读取单位是单个字节,DataInputStream的单位是基本值和字符串的二进制IO,即不定长字节数读取映射,除此外,java还提供了对【一个对象】的输入输出。
不难发现,由于基本值和字符串也可以视为对象,故ObjectInputStream和ObjectOutputStream类可完全替代DataInputStream和DataOutputStream。
值得注意的是,同前面一样,读的单位必须与写的对应一致,否则会发生错误。
简而言之:
FileInputStream只认识一个一个的字节,不知道具体什么含义和表示什么,因此编码与解码都涉及到自动转换;DataInputStream是不定长的读取,具体看调用方法,此时单位随着读取的范围变化,如文件为3“43”,此时应该是一个字节读取、4字节读取合成字符串;而ObjectInputStream相比前面则是可以读取Object对象,其余并无差别。
4. 基于DataInput和DataOutput的RandomAccessFile类
如上图所示,创建一个RandomAccessFile类,可以指定文件的读写的首指针位置,即seek方法,值得注意的是RandomAccessFile不能读写Object对象。



