IO流
主要用于读写数据
IO流按照流的方向可以分为以下两种:
- 输入流
- 输出流
IO流按照操作文件类型可以分为以下两种:
- 字节流
- 字符流
字节流可以操作所有类型的文件,而字符流只可以操作纯文本文件
下面四个都是抽象类,具体使用要使用他的子类。
FileOutputStream
操作本地文件的字节输出流,可以把程序中的数据写到本地文件中。
下面是一个小示例:
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("inarray.txt");
fos.write(97);
fos.close();
}
文件写入的步骤有三个
- 创建字节输出流对象
参数是字符串表示的路径或者File对象
如果文件不存在会创建一个新文件,但是要保证父级路径是存在的,也就是不可以创建文件夹
如果文件已经存在,会清空文件
- 写数据
write方法的参数是整数,但是实际是写的对应的ascii值
参数也可以是字节数组
- 释放资源
解除资源的占用
write方法的三种重载
方法 | 作用 |
---|---|
void write(int b) | 一次写一个字节数据 |
void write(byte[] b) | 一次写一个字节数组数据 |
void write(byte[] b,int off, int len) | 一次写一个字节数组的部分数据 |
后两种的方法演示:
FileOutputStream fos = new FileOutputStream("inarray.txt");
fos.write("Hello world!".getBytes());
fos.close();
FileOutputStream fos = new FileOutputStream("inarray.txt");
byte[] bytes = "Hello world!".getBytes();
fos.write(bytes, 5, bytes.length - 5);
fos.close();
换行写和续写
换行写可以添加一个换行符的ascii值
在字符串中添加一个换行符即可,但是不同操作系统的换行符是不一样的:
在windows操作系统中,回车换行是\r\n
在linux操作系统中,是\n
在mac操作系统中,是\r
在windows操作系统中,java对回车换行进行了优化,我们使用\r或者\n均可以实现回车换行效果,java会自动给我们补齐,但是在书写的过程中还是要完整书写,养成良好的习惯。
FileOutputStream fos = new FileOutputStream("inarray.txt");
byte[] bytes = "Hello world!".getBytes();
fos.write(bytes, 5, bytes.length - 5);
fos.write("\n666".getBytes());
fos.close();
构造方法其实有两个参数,第二个参数是续写开关,设置第二个参数为true就不会把原来的内容清空了
FileOutputStream fos = new FileOutputStream("inarray.txt", true);
byte[] bytes = "Hello world!".getBytes();
fos.write(bytes, 5, bytes.length - 5);
fos.write("\n666".getBytes());
fos.close();
FileInputStream
下面是读取一个字节的示例:
FileInputStream fis = new FileInputStream("inarray.txt");
System.out.println(fis.read());
fis.close();
read方法会一个字节一个字节的读取文件,如果读取不到了返回值-1
下面是一个循环读取字符的例子:
FileInputStream fis = new FileInputStream("inarray.txt");
int b = fis.read();
while (b != -1) {
System.out.println((char) b);
b = fis.read();
}
fis.close();
文件读取也有三个步骤:
- 创建字节输入流对象
- 如果文件不存在直接报错
- 读取数据
- 每次读取的是一个字节,并且返回的是ASCII值
- 读取到末尾read会返回-1
- 释放资源
每次使用完流必须释放资源
循环读取
FileInputStream fis = new FileInputStream("inarray.txt");
int b;
while ((b = fis.read()) != -1) {
System.out.println((char) b);
}
fis.close();
这是对于while循环的一种简便的写法
文件拷贝
拷贝一个小文件的例子:
FileInputStream fis = new FileInputStream("C:\\Users\\nanming\\Desktop\\汇编语言实验报告打印\\1.pdf");
FileOutputStream fos = new FileOutputStream("to.pdf");
int b;
while ((b = fis.read()) != -1)
fos.write(b);
fos.close();
fis.close();
运行会发现,程序运行的时间会比较长
如果文件比较大,速度则会非常的慢
这时候就要用read的重载方法:
方法 | 作用 |
---|---|
public int read() | 读取一个字节 |
public int read(byte[] buffer) | 一次读取一个字节数组 |
理论上数组越大,读取速度也就越快,但是实际上数组如果过大,内存会直接崩溃,通常申请一个1024整数倍大小的数组,比如说1024*1024*5即5M的数组
FileInputStream fis = new FileInputStream("C:\\Users\\nanming\\Desktop\\汇编语言实验报告打印\\1.pdf");
FileOutputStream fos = new FileOutputStream("to.pdf");
byte b[] = new byte[1024 * 1024 * 5];
while (fis.read(b) != -1)
fos.write(b);
fos.close();
fis.close();
将字节数组转换为字符串可以使用String类型的构造方法
上面的方法会有一个弊端:
FileInputStream fis = new FileInputStream("raf.txt");
FileOutputStream fos = new FileOutputStream("inarray.txt");
byte b[] = new byte[5];
while (fis.read(b) != -1)
fos.write(b);
fos.close();
fis.close();
当读取到最后一次时,没有将数组读满,但是写入的时候写入的还是整个数组,也就是包含了上一次读出的数据,数据重叠了
下面是对于文件拷贝的改写:
FileInputStream fis = new FileInputStream("raf.txt");
FileOutputStream fos = new FileOutputStream("inarray.txt");
byte b[] = new byte[5];
int len;
while ((len = fis.read(b)) != -1)
fos.write(b, 0, len);
fos.close();
fis.close();
在使用try catch捕获异常时,如果还要照顾close时的异常,那么还要再嵌套一层的try catch,这样的话就会显得非常复杂,JAVA设计了特殊的方法可以自动关闭资源,按照下面两种方式书写均可
try(创建流1;创建流2){
可能出现异常的代码;
}catch(异常类名 变量名){
异常处理;
}
创建流对象1;
创建流对象2;
try(流1;流2){
可能出现异常的代码;
}catch(异常类名 变量名){
异常处理;
}
而如果不自动关闭则需要按照下面的方式书写:
try{
可能出现异常的代码;
}catch(异常类名 变量名){
异常处理;
}finally{
执行所有资源释放操作;//这里还要嵌套try catch块
}
但是自动释放是有前提的,需要流实现了接口AutoCloseable
编码规则
GBK编码规则:
GBK兼容ASCII码
- 汉字以两个字节存储
- 高位字节二进制一定以1开头(为了与ASCII表区分开)
unicode码中有一个utf-8规格,其中:
(unicode是一个字符集,utf-8是对于unicode的编码规则)
ASCII码:1个字节
简体中文:3个字节
乱码出现的原因
- 读取时没有读完
因为一个汉字由三个字节存储,使用字节流读取时,按照一个字节一个字节的读取,每次读取的字节在表中查不到对应的数据就会显示?
或方框
- 编码和解码规则不相同
比如说一个汉字按照Unicode编码,占用了三个字节,而解码时按照GBK格式解码,前两个字节解释为一个汉字,最后一个字节找不到对应的编码,就会显示黑三角问号
为了预防乱码的出现
- 不要以字节流读取文件
- 要保证编码解码方式一致
Java中的编码解码方式
方法 | 作用 |
---|---|
public byte[] getBytes() | 使用默认方式进行编码 |
public byte[] getBytes(String charsetName) | 使用指定方式进行编码 |
String(byte[] bytes) | 使用默认方式进行解码 |
String(byte[] bytes[],String charsetName) | 使用指定方式进行解码 |
字符流
字符流的底层还是字节流,但是他更加的高级:
- 输入流中,会一次读取一个字节,当遇到中文的时候一次读取多个字节
- 输出流中,也会把数据按照指定的编码形式进行编码,变为字节再写到文件中去
适用于纯文本文件
FileReader
- 创建字符输入流对象
方法 | 作用 |
---|---|
public FileReader(File file) | 创建字符输入流关联本地文件 |
public FileReader(String pathname) | 创建字符输入流 |
如果文件不存在直接报错
- 读取数据
方法 | 作用 |
---|---|
public int read() | 读取数据,都到末尾返回-1 |
public int read(char[]buffer) | 读取多个数据,都到末尾返回-1 |
按照字节进行读取,如果遇到中文,一次读取多个字节,读取后解码返回一个整数
- 释放资源
public int close();//关流
FileReader fis = new FileReader("inarray.txt");
char b[] = new char[5];
int len;
while ((len = fis.read(b)) != -1)
System.out.print(new String(b, 0, len));
fis.close();
FileWriter
方法 | 作用 |
---|---|
public FileWriter(File file) | 创建字符输出流 |
public FileWriter(String pathName) | 创建字符输出流 |
public FileWriter(File file,boolean append) | 是否续写 |
public FileWriter(String PathName,boolean append) | 是否续写 |
方法 | 作用 |
---|---|
void write(int c) | 写一个字符 |
参数 | 作用 |
String str | 写一个字符串 |
String str, int off, int len | 写一个字符串的一部分 |
char[] cbuf | 写一个字符数组 |
char[] cbuf, int off, int len | 写出字符数组的一部分 |
根据当前字符集进行编码把编码之后的数据写入对应文件中去
内存中有一个8K的缓冲区,读取的时候先放到缓冲区,再按照要求读取,读完了再把文件中的内容放到缓冲区,每次都尽可能填满缓冲区
写入的时候也是先写入缓冲区,当缓冲区满或者手动刷新或者关闭资源时写入文件
方法 | 作用 |
---|---|
public void flush() | 刷新缓冲区,将缓冲区数据放到文件中,刷新后可以继续向文件中写入数据 |
public void close() | 关闭通道,关闭后无法再写出数据;释放资源 |