文件内容的读写
针对文本文件, 提供了一组类, 统称为 “字符流”, 典型代表 : Reader, Writer. (读写的基本单位是字符)
针对二进制文件, 提供了一组类, 统称为 “字节流”, 典型代表 : InputStream, OutputStream (读写的基本单位是字节)
所谓的 “流”, 就相当于水流, 打开水龙头有水流出来, 就可以通过水流来接水.
同样的如果我们不用了, 也要给它关闭上, 不然会造成资源的浪费.
每种流对象又分为两种 :
输入流 : Reader, InputStream
输出流 : Writer, OutputStream
(注: 这里输入输出, 针对的是CPU来讨论的, 输出就是从硬盘输出到内存, 输入就是从内存写入硬盘)
InputStream 的使用
可以看到, InputStream 是个抽象类, 不能直接实例化, 要使用还需要具体的实现类.
因为 InputStream 是进行 IO 访问的类, 而 IO 不仅仅是读写硬盘的文件, 在网络编程里还可以用来读写网卡.
所以 InputStream 的实现类有很多,基本可以认为不同的输入设备都可以对应一个 InputStream 类,因为我们现在只关心从文件中读取,所以使用 FileInputStream.
InputStream inputStream = new FileInputStream("test.txt");
意思是 : 打开 hello.txt 文件, 让变量 inputStream 能够和硬盘上的此文件关联起来, 就像有了遥控器, 以后我们要对该文件进行操作, 就可以通过该变量, 间接操作了.
当然, 如果该文件不存在, 就会抛异常 : FileNotFoundException
注意 :
当我们不需要对这个文件操作时, 就要把这个文件进行关闭, 就像打开水流接水, 不用了也得关.
关闭文件用到了 close 方法.
inputStream.close();
补充 :
为啥不关就会造成为文件资源泄漏呢 ?
这里的资源主要就是指 : 文件描述符表, 它是进程的一个结构.
进程的结构是使用 PCB 来表示的.
包括了 : 1.pid, 2.内存指针, 3.文件描述符表
文件描述符表就是记载了当前进程都打开了哪些文件, 每打开一个文件, 就会在表里申请一个位置.
这个表就可以当成一个数组, 数组下标就是文件描述符, 数组元素就是这个文件在内核中的结构体表
示.
遗憾的是, 这个表长度有限, 不能无休止的打开且不释放, 一旦满了 就会打开失败. 这就是文件资源泄漏.
public static void main(String[] args) throws IOException { InputStream inputStream = new FileInputStream("d:/test.txt"); //...代码 inputStream.close(); }
其实在中间代码执行过程中, 还可能出现问题, 比如中间就 return 了, 这样同样会导致文件资源泄漏.
我们可以利用 try :
public static void main(String[] args) throws IOException { InputStream inputStream = null; try { inputStream = new FileInputStream("d:/test.txt"); }finally { inputStream.close(); //这里的代码一定会被执行到 } }
上述代码还可以进行优化, 变得更简洁 :
public static void main(String[] args) throws IOException { try(InputStream inputStream = new FileInputStream("d:/test.txt")) { } }
这个操作叫 : try with resources, 也就是带有资源的 try 操作, 资源会在 try 代码块结束时, 自动执行 close 操作.
为啥它能写进 try 里面呢?
其实 InputStream 实现一个特定接口 : Closeable, 实现了该接口的类, 就可以写成这种语法.
读操作
以 read 来说明 : 从输入流读取数据的下一个字节。 读到的字节被作为 int 返回, 因为是字节, 所以范围就是 0~255 。 如果没有读到字节,可能已经到达流的末尾,则返回值-1 .
现在我们来读取数据, 首先在 d 盘创建个文件 :
public class Test { public static void main(String[] args) throws IOException { try(InputStream inputStream = new FileInputStream("d:/test.txt")) { while(true) { int a = inputStream.read(); //为什么不用byte接收, 就是因为它的表示范围是 0 ~ 255,不能表示-1 if(a == -1) { //读到末尾结束循环 break; } System.out.printf("%d\n", a); //以十进制打印每个字符 } } } }
java默认使用UTF-8 来进行编码, 我们可以通过查看字符编码网站 来检验 :
网址链接
换个内容试试 :
输出 :
对应的检验一下 :
为啥结果不同呢?
因为一个汉字是由三个字符组成的, 而之前是打印每个字符的10进制, 我们可以换成 16 进制打印每个字符 :
public class Test { public static void main(String[] args) throws IOException { try(InputStream inputStream = new FileInputStream("d:/test.txt")) { while(true) { int a = inputStream.read(); if(a == -1) { break; } System.out.printf("%x\n", a); //以 16 进制打印每个字符 } } } }
这样就对应上了.