文章目录
- ❤专栏导读
- ❤字符流
- ❤Reader类的操作
- ❤Writer类操作
- ❤Writer类的构造方法
❤专栏导读
🚀《多线程》
🚀《数据结构剖析》
🚀《JavaSE语法》
在Java标准库中,提供的读写文件的流对象有很多很多的类,但是可以将这么多的类总结成两个大的方向就是:字节流、字符流,而字节流就是针对于二进制文件进行读写操作,字符流就是针对文本文件进行读写操作
❤字符流
使用字符流,读写操作是以字符为单位的,每次最少读/写一个字符,而一个字符是多少个字节,就取决于当前的字符集是哪一种;
GBK字符集:一个中文字符是两个字节,一个英文字符是一个字节
UTF8字符集:一个中文字符是三个字节,一个英文字符是一个字节
其实,字符流是针对字节流又进行了一次封装,因为,在硬盘中,所有的文件都是以字节为单位进行保存的,都是二进制数据,但是,使用字符流的话,会将几个连续的字节给翻译成对应的字符,也就相当于自动帮我们完成了一个查字符集表的操作;
针对于字符流的操作,Java实现Reader(输入) 和 Writer(输出) 两个父类
什么叫输入(读),什么叫输出(写)?
假如,我向硬盘中保存一份内容,那么这是输入还是输出呢?
如果是站在硬盘的角度,向硬盘中保存一份内容,那么这就是输入(读),从硬盘中拿走一份数据, 那么这就是输出(写)
如果是站在操作系统的角度,向硬盘中保存一份内容,那么就是输出(写), 从硬盘中拿走一份数据,这就是输入(读)
但是,我们程序员一定要站在操作系统的角度去看,不要站在硬盘的角度看
❤Reader类的操作
public static void main(String[] args) throws FileNotFoundException {Reader reader = new FileReader("d:/新建文件夹/text.txt");//因为Reader类是一个抽象类,所以只能new它的子类}
返回值类型 | 方法 | 说明 |
---|---|---|
int | read() | 无参数,一次读取一个字符 |
int | read(char[] cbuf) | 一次读取若干字符,尽量把字符数组填满 |
int | read(char[] cbuf,int off, int len) | 一次读取若干个字符,尽量将数组中,从off这个位置开始放数据,最多放len个 |
💡==注意点一:==为什么这个无参数的read()方法是一个int类型?
因为,这里是用了0-65536范围的数表示一个char类型的字符,因为0-65535是一个无符号的char,所以使用int类型足够表示,但是这里为什么要用int表示呢?因为是为了区分当前读操作是否读到了文件内容的末尾,如果读到了内容末尾,就返回一个-1表示已经读完了,这一点在方法说明中也可以看到;
💡==注意点二:==因为read()读取的是两个字节,如果是Unicode字符集编码,那么不会出现问题,但如果是UTF8字符集编码的话,一个中文字符是3个字节,这样的话不就会出现bug么,针对于这个问题,Java内部也是解决的非常好的,因为,在Java中,如果只使用char的话,那么字符集使用的就是Unicode,但是如果使用String,大概率使用的就是UTF8, 因为,是否是UTF8编码这个事情呢,是可配置的,但是char的话,固定就是Unicode,而举个例子。
假如有char[] 数组,这个数组固定使用的就是Unicode编码,如果使用这个数组构建字符串时,Java内部就会将Unicode编码转变为UTF8编码,如果有一个字符串s,使用s.charAt(0),拿到一个字符时,就会将UTF8编码转变成Unicode
接下来就演示一下读操作,将记事本中的所有内容读出来;
1.无参数read()
public static void main(String[] args) throws IOException {Reader reader = new FileReader("d:/新建文件夹/text.txt");while(true) {int n = reader.read();if(n == -1) {//表示已经读完,退出循环break;}char ch = (char) n;System.out.print(ch);}}
2.带有指定数组的read()
public static void main(String[] args) throws IOException {Reader reader = new FileReader("d:/新建文件夹/text.txt");while(true) {char[] cbuf = new char[1024];//这里定义了一个大小为1024的数组,读取时会尽量给这个数组填满,int n = reader.read(cbuf);if(n == -1) {System.out.println(n);break;}System.out.println(n);for(int i = 0; i < n; i++) {System.out.print(cbuf[i] + " ");}System.out.println();}}
3.带有三个参数的read()
public static void main(String[] args) throws IOException {Reader reader = new FileReader("d:/新建文件夹/text.txt");while(true) {char[] cbuf = new char[10];int n = reader.read(cbuf, 2, 8);//从2下标开始放数据,最多放8个数据if(n == -1) {System.out.println(n);break;}System.out.println(n);for(int i = 0; i < cbuf.length; i++) {System.out.print(cbuf[i]);}System.out.println();}}
💡==注意点三:==对于流操作,使用完之后,**一定要记得调用 close() 释放文件描述符表,**而这个文件描述符表是一个类似于数组这样的结构,因为一个进程每打开一个文件,就会在这个数组中占一个位置,但是,这个数组是有容量限制的,所以,如果一直打开文件,而不关闭文件,就会使这个表中的元素越来越多,一直到把这个数组占满,后续再打开文件时,就会出错,所以就构成了文件资源泄露 !!!
public static void main(String[] args) throws IOException {Reader reader = new FileReader("d:/新建文件夹/text.txt");//第一种,使用try、finally释放文件资源的方式try{while(true) {char[] cbuf = new char[10];int n = reader.read(cbuf, 2, 8);if (n == -1) {System.out.println(n);break;}System.out.println(n);for (int i = 0; i < cbuf.length; i++) {System.out.print(cbuf[i]);}System.out.println();}}finally {//使用finally,防止代码中万一抛出异常,导致执行不到close();reader.close();}//第二种,使用try释放文件资源的方式//1.这个语法的目的就是,()定义的变量会在try里面的代码执行结束后,自动调用close()//2.只用实现Closeable接口的对象才能放再try()中try(Reader reader = new FileReader("d:/新建文件夹/text.txt")){while(true) {char[] cbuf = new char[10];int n = reader.read(cbuf, 2, 8);if (n == -1) {System.out.println(n);break;}System.out.println(n);for (int i = 0; i < cbuf.length; i++) {System.out.print(cbuf[i]);}System.out.println();}}}
❤Writer类操作
public static void main(String[] args) throws IOException {Writer writer = new FileWriter("d:/新建文件夹/text.txt");}
❤Writer类的构造方法
构造方法 | 说明 |
---|---|
write(int c) | 一次写入一个字符 |
write(char[] cbuf) | 一次写入一个字符数组中的内容 |
write(String str) | 一次写入一个字符串 |
write(String str, int off, int len) | 写入一个字符串,从字符串中的off位置开始写,写len个长度的字符 |
write(char[] cbuf, int off, int len) | 写入一个字符数组,从数组中的off位置开始写,写len个长度的字符 |
Writer中这些构造方法和Reader中构造方法的使用都是类似的,这里就不一一的演示了,只为大家举一个常用的
public static void main(String[] args) throws IOException {try(Writer writer = new FileWriter("d:/新建文件夹/text.txt")) {writer.write("你真的好帅");}}
💡==注意点:==这里使用write向文件中写入内容时,会将原来文件中的内容覆盖掉,如果不想覆盖,在new FileWriter时传入一个true
Writer writer = new FileWriter("d:/新建文件夹/text.txt", true)