> 文章列表 > Java -- IO流

Java -- IO流

Java -- IO流

IO流

主要用于读写数据

IO流按照流的方向可以分为以下两种:

  1. 输入流
  2. 输出流

IO流按照操作文件类型可以分为以下两种:

  1. 字节
  2. 字符流

字节流可以操作所有类型的文件,而字符流只可以操作纯文本文件

下面四个都是抽象类,具体使用要使用他的子类。

FileOutputStream

操作本地文件的字节输出流,可以把程序中的数据写到本地文件中。

下面是一个小示例:

    public static void main(String[] args) throws IOException {FileOutputStream fos = new FileOutputStream("inarray.txt");fos.write(97);fos.close();}

文件写入的步骤有三个

  1. 创建字节输出流对象

参数是字符串表示的路径或者File对象

如果文件不存在会创建一个新文件,但是要保证父级路径是存在的,也就是不可以创建文件夹

如果文件已经存在,会清空文件

  1. 写数据

write方法的参数是整数,但是实际是写的对应的ascii值

参数也可以是字节数组

  1. 释放资源

解除资源的占用

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();

文件读取也有三个步骤:

  1. 创建字节输入流对象
  • 如果文件不存在直接报错
  1. 读取数据
  • 每次读取的是一个字节,并且返回的是ASCII值
  • 读取到末尾read会返回-1
  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. 汉字以两个字节存储
  2. 高位字节二进制一定以1开头(为了与ASCII表区分开)

unicode码中有一个utf-8规格,其中:

(unicode是一个字符集,utf-8是对于unicode的编码规则)

ASCII码:1个字节

简体中文:3个字节

Java -- IO流

乱码出现的原因

  1. 读取时没有读完

因为一个汉字由三个字节存储,使用字节流读取时,按照一个字节一个字节的读取,每次读取的字节在表中查不到对应的数据就会显示?方框

  1. 编码和解码规则不相同

比如说一个汉字按照Unicode编码,占用了三个字节,而解码时按照GBK格式解码,前两个字节解释为一个汉字,最后一个字节找不到对应的编码,就会显示黑三角问号

为了预防乱码的出现

  1. 不要以字节流读取文件
  2. 要保证编码解码方式一致

Java中的编码解码方式

方法 作用
public byte[] getBytes() 使用默认方式进行编码
public byte[] getBytes(String charsetName) 使用指定方式进行编码
String(byte[] bytes) 使用默认方式进行解码
String(byte[] bytes[],String charsetName) 使用指定方式进行解码

字符流

字符流的底层还是字节流,但是他更加的高级:

  1. 输入流中,会一次读取一个字节,当遇到中文的时候一次读取多个字节
  2. 输出流中,也会把数据按照指定的编码形式进行编码,变为字节再写到文件中去

适用于纯文本文件

FileReader

  1. 创建字符输入流对象
方法 作用
public FileReader(File file) 创建字符输入流关联本地文件
public FileReader(String pathname) 创建字符输入流

如果文件不存在直接报错

  1. 读取数据
方法 作用
public int read() 读取数据,都到末尾返回-1
public int read(char[]buffer) 读取多个数据,都到末尾返回-1

按照字节进行读取,如果遇到中文,一次读取多个字节,读取后解码返回一个整数

  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() 关闭通道,关闭后无法再写出数据;释放资源