一、数据流
文件的读写采用数据流的方式,类似于一个储水池,输入的一端叫做 输入流,输出的一端叫做 输出流。而输入流和输出流采用的是 不同的类。如下图的关系。
为什么input是读取,output是写入?
程序和运行时数据是在内存中驻留的,而 input,output都是相对内存来说 的,input是从磁盘读取到内存 ,output是从内存写到磁盘.
二、InputStream 概述
1. 方法
InputStream 只是一个抽象类,如果要创建实例化还需要具体的实现类。关于InputStream 的实现类有很多,而我们只关心从文件中读取,所以通常用 FileInputStream 实现
read() -
从文件输入流中读取一个字节。read(byte[] b)
-从此输入流中读取最多b.length
个字节的数据到一个字节数组中。read(byte[] b, int off, int len)
-从此输入流中读取最多len
个字节的数据到一个字节数组中。
2. FileInputStream 子类
①构造方法
FileInputStream(File file) -创建文件输入流以从File对象读取。
FileInputStream(String name) -创建文件输入流以从指定的文件名读取。
FileInputStream(FileDescriptor fdObj) -创建从指定文件描述符读取的文件输入。
②代码案例
注意 !!!!
~ ~ 为什么每次关闭代码一定要关闭文件?
每个进程对应着一个PCB,而PCB里有一个文件描述符表(同一个进程中多个PCB共同使用一份文件描述符表),文件描述附表其实就相当于一个 数组\顺序表。每次进程打开一个文件,都会在这个表里创建一个项,这个项就表示一个文件。
表的项最大容量,可以进行配置。
如果关闭一个文件就相当于把表里对应的项 给释放掉,但是如果一直不关闭,就会一直占用。这就意味着一直打开都不关闭,就会导致 表项被耗尽~~这是一个非常严重的bug
~ ~ 所以才能每次都关闭呢?
1. 使用 close() , 但是要注意每次一定能执行到这一条语句
2. 使用 try-with-resource 结构(推荐使用!),try结束就能保证关闭文件,不用考虑是否执行到close 语句。(下面的代码案例就是这样写的)
代码1:试用 read()
public class Demo1 { public static void main(String[] args) throws IOException { try (InputStream stream = new FileInputStream("./文件部分/测试.txt")) { while (true) { int b = stream.read(); System.out.print(b); if (b == -1) {//代表文件已经读完 break; } } } } }
文件中的内容是: Hello word.
输出结果是
代码2:尝试读取中文字符
public static void main(String[] args) throws IOException { try (InputStream stream = new FileInputStream("./文件部分/测试.txt")){ byte[] b = new byte[12]; int len; while (true){ len = stream.read(b);//每次读取一个字节,返回刚读到的字节 if (len == -1){ break; } } for (int i =0;i< b.length;i+=3){// //每次读取3字节进行TTF-8解码,得到中文字符 //利用String的构造方法完成,这个方法不是通用解法,知道就行 String s = new String(b,i,3,"UTF-8"); System.out.printf("%s",s); } } }
文件中的内容是: 你好中国
输出结果是:
代码3:使用 Scanner 读取
- Scanner(InputStream ,String charset) --使用charset字符集进行 is 的扫描读取
public static void main(String[] args) throws IOException { try (InputStream is = new FileInputStream("./文件部分/测试.txt")){ Scanner s = new Scanner(is); while (s.hasNext()){ String ch = s.next(); System.out.println(ch); } } }
文件中的内容是: 你好中国
输出结果是:
三、OutputStream概述
1. 方法
write() -
写入要给字节的数据。
read(byte[] b) -将b这个字符数组中的数据全部写入OS(OutputStream)中。
read(byte[] b, int off, int len) -将b这个字符数组中从off开始的数据写入OS中,一共写len个
close() -关闭字节流。
flush() -重要!我们知道I/O操作是比较慢的,大多的OutputStream为了减少设备操作的次数,在写数据的时候会 先暂时写入内存的一个指定区域,知道这个区域满了以后才一起写入设备中,而这个区域一般称为 缓冲区。但是因此我们的部分数据可能会留在缓冲区,因此我们在一段代码完成的时候,记得进行 flush(刷新) 操作。
2. FileInputStream 子类
1. 构造方法
FileOutputStream(File file)
-从File
对象读取。FileOutputStream(String name)
-从指定的文件名读取。
FileOutputStream(FileDescriptor fdObj)
-从指定文件描述符读取。
2.代码案例
代码1:写入文件
public static void main(String[] args) throws IOException { try (OutputStream os = new FileOutputStream("./文件部分/测试.txt")){ os.write('I');//传入一个字符 os.write(' '); byte[] am = {'a','m',' '};//传入一个 byte类型的数组 os.write(am); String str = "a boy."; byte[] boy = str.getBytes();//将字符串转换为 数组 os.write(boy);//再从内存写入到文件中 String s = "你呢?"; byte[] b = s.getBytes("utf-8");//使用utf-8将其转换为中文传入 os.write(b); os.flush();//不要忘记 flush } }
程序运行结束后,文件内容发生改变:
代码2:利用 PrintWriter 这种高效的方法完成输出
public static void main(String[] args) throws IOException { //创建一个文件流实例os 与本地文件 对应 try (OutputStream os = new FileOutputStream("./文件部分/测试.txt")){ //这个构造是 把输入的内容存放到os这个实例中,才用utf-8编码 try(OutputStreamWriter osWriter= new OutputStreamWriter(os,"utf-8")){ //这个构造 封装了print等输入方法,print到osWrite这个实例中 try (PrintWriter printWrite = new PrintWriter(osWriter)){ printWrite.println("第一行!"); printWrite.print("第二行\n"); printWrite.flush(); } } } }
程序运行结束后,文件内容发生改变: