IO

  最近,因为学习项目,就想总结前面的知识。下面我们就简单归纳归纳JAVA-IO知识。

  IO是java中十分重要的知识,那么什么是IO,即inout,java中是通过“流”来处理io的,进而实现了程序与外设的数据传输。

  • 流的特点:

    1、先进先出:最先写入的输出流,会最先被输入流读取到

    2、顺序存储:写入是可以先一个一个的写入一串字节,读入时,也可以一串一串的读入,不能随机访问(randomAccessFile)除外。

    3、每一种流的功能是单一的,只能是只读或者只写

  •   流的分类

    1、按照数据的方向:输入流,输出流

    2、按处理数据单位:字节流,字符流

    3、按照功能:   节点流,处理流

  输入流和输出流就比较好理解,从外部读取文件,就是输入流,将数据写入到外部文件,就是输出流。

  字节流和字符流:总的来说就是,字节流能够处理一切的文件,而字符流只能处理纯文本文件。那么,我们都有字节流能处理一切的文件,为什么还要有字符流呢,原因是:在java中采用Unicode编码,在Unicode中,一个英文占用一个字节(8bits),我们中文为2个字节(16bits)。但是在UTF-8编码中,英文占用2个字节,中文占用3个字节,我们使用字节流处理中文,如果一次读写一个字符对应的字节数,就不会有问题,但是如果如果我们将字符对应的的字节分裂开来,就会出现乱码了。

  区别:字节流本身是没有缓冲区的,但是缓冲字节流,相比较于字节流,效率提升就比较大。但是字符流本身是有缓冲区的,所以说缓冲字符流对于字符流的效率提升就不是很大。还有一点就是,字符流在

读入或者写出的时候,要注意刷新管道,或者关闭流(关闭时,回去刷新管道)

 

 

下面需要介绍的常用IO类

 

 

 

 

 

  1、FileOutputStream(字节输出流):,继承自OutputStream,一般用于写入诸如图像数据的原始字节流。 要编写字符流,请考虑使用FileWriter
下面我们来简单演示一下fileOutputStream的常用方法和构造方法
FileOutputStream​(File file)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream​(FileDescriptor fdObj)
创建要写入指定文件描述符的文件输出流,该文件描述符表示与文件系统中实际文件的现有连接。
FileOutputStream​(File file, boolean append)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream​(String name)
创建文件输出流以写入具有指定名称的文件。
FileOutputStream​(String name, boolean append)
创建文件输出流以写入具有指定名称的文件。 
 

 可以看到FileOutputStream主要有两种构造方法,一种是传入文件对象,另外一种方法时传入一个文件路径(保证文件存在),另外我们会发现还有一个boolean append的参数,这个参数的意义就是,是否追加,
就是如果原文件如果有字节,我们传入不写,或者是传入false的时候,它就会覆盖原来的内容。下面看演示↓:


 1 FileOutputStream fileOutputStream = new FileOutputStream("a.txt");
 2         byte[] bytes1 = new byte[]{66,67,68};       //BCD
 3         byte[] bytes2 = new byte[]{69,70,71,72,73}; //EFGHI
 4         byte[] bytes3 = "XYZ".getBytes();
 5 
 6         fileOutputStream.write(65);      //会写入A
 7         fileOutputStream.write(bytes1);     //会写入B,C,D
 8         fileOutputStream.write(bytes2,1,2);     //传入一个字节数组,从指定下标开始写入指定长度,从F开始,写入两个长度就是FG
 9         fileOutputStream.write(bytes3);     //会写入XYZ
10         
11         fileOutputStream.close();       //关闭流,一定不要忘记,一定不要忘记,一定不要忘记
12         System.out.println("ok");

字节输出流->>>>>>:结果

 

 

 可以看到:我们可以写入一个字节,一个字节数组,或者是一个从指定下标开始,写入指定长度的数组,最后不要忘记关闭就行。

 

 

 

  2、FileInputStream (字节读取流):继承自inputStream,用于读取字节,并返回一个int类型的数组或者字节。下面我们演示一下构造方法和常用api

  

 
构造器描述
FileInputStream​(File file)
通过打开与实际文件的连接来创建 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream​(FileDescriptor fdObj)
使用文件描述符 fdObj创建 FileInputStream ,该文件描述符表示与文件系统中实际文件的现有连接。
FileInputStream​(String name)
通过打开与实际文件的连接来创建 FileInputStream ,该文件由文件系统中的路径名 name命名。 

  和FileOutputStream类似,FileInputStream 主要也是两种构造方法,一个是传入文件对象,另外一个是传入文件路径(该文件存在)。

 

int read()
从此输入流中读取一个字节的数据。
int read​(byte[] b)
从此输入流 b.length最多 b.length字节的数据读 b.length字节数组。
int read​(byte[] b, int off, int len)
从此输入流 len最多 len字节的数据读入一个字节数组。 

    Read(byte[] b):读取一个字节数组的数据    
    Read():读取一个字节的数据
    read(byte[] b int off ,int len):从指定长度开始,读取指定的长度字节的数据



演示一次读取一个数据:
1 FileInputStream fileInputStream = new FileInputStream("a.txt");  //a.txt的内容是:ABCDEF
2         while (true){
3             int i = fileInputStream.read();
4             if (i == -1){                   //字节读取流,返回的是一个int类型,如果读取到结尾,就会返回-1,返回-1就跳出循环
5                 break;
6             }
7             System.out.print((char) i+" ");
8         }
9         fileInputStream.close();

输出结果:A B C D E F 
分析:::可以看出,一次读入一个字节数据,读到末尾就返回-1


下面演示一次读取一个byte数组的数据:
 1  ////txt的内容是小写字母表,一共26个
 2         FileInputStream fileInputStream = new FileInputStream("a.txt");
 3         //创建一个10个字节长度的字节数组
 4         byte[] bytes = new byte[10];
 5         int len = fileInputStream.read(bytes);
 6         //每读取一次,就new为字符串,并打印
 7         System.out.println(new String(bytes)+"长度为:"+len);
 8         fileInputStream.read(bytes);
 9         System.out.println(new String(bytes)+"长度为:"+len);
10         fileInputStream.read(bytes);
11         System.out.println(new String(bytes)+"长度为:"+len);
12         fileInputStream.close();

输出结果:

abcdefghij长度为:10
klmnopqrst长度为:10
uvwxyzqrst长度为:10

 

  分析:可以看到我们每一次读取了10b长度的数组,但是细心就会发现,我们最后一次明明没有10个b长度的数据,但是也读取了10个,这里我们就分析其中的具体过程,我们第一次读取abcdefghij,

然后第二次读取klmnopqrst,其中就会替换掉原来第一次读取的,我们第三其实读取的是uvwxyz,替换掉第二次读取的klmnopqrst,但是只有uvwxyz,最后的qrst就没有被替换掉,所以就造成了这个结果,

这也是我们平时,下载的安装包,总会比官方的给出的大小会多出那么几kb。

  解决方法,在构建为字符串时,将返回的长度,传给new String()构造方法,下面看代码:

 

 1    ////txt的内容是小写字母表,一共26个
 2         FileInputStream fileInputStream = new FileInputStream("a.txt");
 3         //创建一个10个字节长度的字节数组
 4         byte[] bytes = new byte[10];
 5         int len = fileInputStream.read(bytes);
 6         //每读取一次,就new为字符串,并打印
 7         System.out.println(new String(bytes,0,len)+"长度为:"+len);
 8         len = fileInputStream.read(bytes);
 9         System.out.println(new String(bytes,0,len)+"长度为:"+len);
10         len = fileInputStream.read(bytes);
11         System.out.println(new String(bytes,0,len)+"长度为:"+len);
12         fileInputStream.close();

输出结果:

abcdefghij长度为:10
klmnopqrst长度为:10
uvwxyz长度为:6

  

 

 3、上面我们介绍了常用的字节读取流和字节写出流,下面我们看看字符写出流:FileWriter:继承自writer,使用默认缓冲区大小将文本写入字符文件,编码采用平台默认编码或者是构造方法中指定的编码

 

构造器描述
FileWriter​(File file)
File写一个 FileWriter ,使用平台的 default charset
FileWriter​(FileDescriptor fd)
构造一个 FileWriter给出的文件描述符,使用该平台的 default charset
FileWriter​(File file, boolean append)
在给出要写入的 FileWriter下构造 File ,并使用平台的 default charset构造一个布尔值,指示是否附加写入的数据。
FileWriter​(File file, Charset charset)
构造一个FileWriter给予File编写和charset
FileWriter​(File file, Charset charset, boolean append)
构造FileWriter给出File写入, charset和一个布尔值,指示是否附加写入的数据。
FileWriter​(String fileName)
构造一个 FileWriter给出文件名,使用平台的 default charset
FileWriter​(String fileName, boolean append)
使用平台的 default charset构造一个 FileWriter给定一个文件名和一个布尔值,指示是否附加写入的数据。
FileWriter​(String fileName, Charset charset)
构造一个FileWriter给出文件名和charset
FileWriter​(String fileName, Charset charset, boolean append)
构造一个FileWriter给定一个文件名, charset和一个布尔值,指示是否附加写入的数据。

    通过这些构造方法,可以发现其实跟我们的字节流,都差不多,只是增加了一个编码方式

 

    下面看一下,常用的api(其中writer()和append()方法比较常用,注意一点就是,append()会返回一个this对象,而且append底层调用的也是writer方法),源码如下

 

 

 

 

    

Writer append​(char c)
将指定的字符追加到此writer。
Writer append​(CharSequence csq)
将指定的字符序列追加到此writer。
Writer append​(CharSequence csq, int start, int end)
将指定字符序列的子序列追加到此writer。
abstract void close()
关闭流,先冲洗它。
abstract void flush()
刷新流。
static Writer nullWriter()
返回一个新的 Writer ,它丢弃所有字符。
void write​(char[] cbuf)
写一个字符数组。
abstract void write​(char[] cbuf, int off, int len)
写一个字符数组的一部分。
void write​(int c)
写一个字符。
void write​(String str)
写一个字符串。
void write​(String str, int off, int len)
写一个字符串的一部分。 

下面我们演示一下,writer方法:
 
 1     FileWriter fileWriter = new FileWriter("a.txt",true);       //向a.txt采用追加的模式
 2         fileWriter.write(65);           //写入对应的字符
 3         fileWriter.write(66);
 4         fileWriter.write(67);
 5         fileWriter.write(68);
 6         fileWriter.write(69);
 7         fileWriter.write("\n"+"锄禾日当午,"+"\n");       //写入一个字符串
 8         fileWriter.write("汗滴禾下土."+"\n");
 9         fileWriter.write("谁知盘中餐,"+"\n");
10         fileWriter.write("粒粒皆辛苦."+"\n");
11         char[] chars = {'我','爱','学','习','编','程'};       //写入一个char类型数组
12         fileWriter.write(chars,0,6);
13         System.out.println("ok");
14         fileWriter.close();
15 
16 输出结果:
17 ABCDE
18 锄禾日当午,
19 汗滴禾下土.
20 谁知盘中餐,
21 粒粒皆辛苦.
22 我爱学习编程

 

 
再看看append()
 1  FileWriter fileWriter = new FileWriter("a.txt",true);
 2         FileWriter append = (FileWriter)fileWriter.append((char) 65);   //调用append方法之后,就返回一个writer,我们强转一下
 3         append.append((char)66).append((char)67).append((char)68).append((char)69); //  因为返回一个writer对象,所以我们就可以一直追加了
 4         append.append("fghijk");    //append一个字符串
 5         append.append("xyz",0,3);   //append一个从指定位置开始的指定长度的字符串
 6         System.out.println("ok");
 7         fileWriter.close();
 8 
 9 
10 输出结果:
11 ABCDEfghijkxyz

 

4、下面我们看看字符读取流,FileReader:继承自Reader,使用默认缓冲区大小从字符文件中读取文本,FileReader用于读取字符流。 要读取原始字节流,请考虑使用FileInputStream
  构造方法也就是比字符写出流,少了append参数。大相径庭。
构造器描述
FileReader​(File file)
使用平台 FileReader ,在 File读取时创建一个新的 FileReader
FileReader​(FileDescriptor fd)
使用平台 default charset创建一个新的 FileReader ,给定 FileDescriptor进行读取。
FileReader​(File file, Charset charset)
创建一个新的FileReader ,给出File读取和charset
FileReader​(String fileName)
使用平台 default charset创建一个新的 FileReader ,给定要读取的文件的 名称
FileReader​(String fileName, Charset charset)
给定要读取的文件的名称和FileReader ,创建一个新的FileReader


常用API:
int read()
读一个字符。
int read​(char[] cbuf)
将字符读入数组。
abstract int read​(char[] cbuf, int off, int len)
将字符读入数组的一部分。 





演示read()方法,
 1   //a.txt的内容是:计算机中的一切数据都是以二进制存储的
 2         FileReader fileReader = new FileReader("a.txt");
 3         while (true){
 4             int read = fileReader.read();
 5             if (read == -1){        //读到末尾就直接跳出循环
 6                 break;
 7             }
 8             System.out.print((char) read+" ");
 9         }
10         fileReader.close();
11 
12 输出结果:计 算 机 中 的 一 切 数 据 都 是 以 二 进 制 存 储 的 

 

 

 

read(char[]):一次性读取一个字符数组


 1  //a.txt的内容是:计算机中的一切数据都是以二进制存储的
 2         FileReader fileReader = new FileReader("a.txt");
 3         char[] chars = new char[100];       //构造一个字符数组长度为100
 4         int length = fileReader.read(chars);        //返回读取的长度
 5         String str = new String(chars);     //将该字符数组构造为一个字符串
 6         System.out.println(new String(chars)+"------读取字符串的长度是:"+length);
 7         System.out.println("实际长度是:"+str.length());      //打印该字符串的长度
 8         fileReader.close();
 9 
10 输出结果:
11 计算机中的一切数据都是以二进制存储的                                                                          ------读取字符串的长度是:18
12 实际长度是:100

  

 

  分析:我们读取的字符串数组长度明明是18,但是构造为字符串的长度为什么是100,原因就在于,我们创建字符数组时,字符数组的默认值是空格,我们读取的内容,就会替换掉掉空格,我们存储的内容

只有18个字,所有后面的空格,就没有被替换掉,这也就是我们第11行,中间间隔这么多。我们构造字符串,传入地址,所以字符串长度是100.那么我们如何解决呢,跟我们字节读取流的解决方法一样。下面看代码(黄色是修改部分)。

 1  //a.txt的内容是:计算机中的一切数据都是以二进制存储的
 2         FileReader fileReader = new FileReader("a.txt");
 3         char[] chars = new char[100];       //构造一个字符数组长度为100
 4         int length = fileReader.read(chars);        //返回读取的长度
 5         String str = new String(chars,0,length);     //将该字符数组构造为一个字符串
 6         System.out.println(new String(chars)+"------读取字符串的长度是:"+length);
 7         System.out.println("实际长度是:"+str.length());      //打印该字符串的长度
 8         fileReader.close();
 9 
10 
11 计算机中的一切数据都是以二进制存储的                                                                                  ------读取字符串的长度是:18
12 实际长度是:18

  分析:我们读取的内容还是一样,会有空格们不会变,因为我们的字符数组长度就是100嘛,但是我们的字符串长度变为18,这正是我们所要的。

 

 

 

 

 

5、我们归纳完,字节流和字符流,那么想想这样一个场景,我们获取到网上的一个资源时,该资源是一个字节流,但是我们想获取其中的中文汉字,字节流肯定会出现乱码的,那么字节流如何转换为字符流呢


    1、字节输入流------》字符输入流,我们先不将其转换,看看输入结果

 1 //字节输入流 ----》  字符输入流
2 FileInputStream fileInputStream = new FileInputStream("a.txt");    //a.txt内容是:计 算 机 中 的 一 切 数 据 都 是 以 二 进 制 存 储 的  3 //InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); 4 while (true ){ 5 int read = fileInputStream.read(); 6 if (read == -1){ 7 break; 8 } 9 System.out.print((char)read+" "); 10 } 11 12 13 输入结果: 14 è ® ¡ ç ® — æ œ º ä ¸ ­ ç š „ ä ¸ € å ˆ ‡ æ • ° æ  ® é ƒ ½ æ ˜ ¯ ä » ¥ ä º Œ è ¿ › å ˆ ¶ å ­ ˜ å ‚ ¨ ç

  哈哈哈,可以看我们模拟从网络上获取的资源,要获取其中的汉字,是会乱码的。下面我们将第三行代码注释去掉。看看效果

 

//字节输入流 ----》  字符输入流
        FileInputStream fileInputStream = new FileInputStream("a.txt");
        InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
        while (true ){
            int read = inputStreamReader.read();
            if (read == -1){
                break;
            }
            System.out.print((char)read+" ");
        }

输出结果:
计 算 机 中 的 一 切 数 据 都 是 以 二 进 制 存 储 的 

 

可以看到转换为字符输入流之后,就没有乱码。



2、字节输出流------》字符输出流(一般是转换为打印流)

 1         //字节输出流----》字符输出流
 2         FileOutputStream fileOutputStream = new FileOutputStream("a.txt");
 3         PrintWriter printWriter = new PrintWriter(fileOutputStream);
 4         printWriter.println("C语言,腻害");
 5         printWriter.println("java,腻害");
 6         printWriter.flush();    //字符流一定要刷新,或者说是关闭流,不然字符保存在缓冲区中,不会写入到指定文件中。
 7 
 8 
 9 输出结果:
10 
11 C语言,腻害
12 java,腻害

 

可以看到,字节流输出流转为打印流之后,输出是相当方便。腻害腻害!!!!

 

 

  下面我们介绍一下:缓存读取流

  注意,注意,注意:字符输入流(一定要是字符输入流)-------->缓冲读取流,缓冲读取流一次能够读取一行,是相当的nice

 

下面看看使用最多的方法readLine():读到最后一行会返回null

 

 1 //字符输入流----》缓存读取流
 2         FileReader fileReader = new FileReader("a.txt");
 3         BufferedReader bufferedReader = new BufferedReader(fileReader);
 4         while (true){
 5             String s = bufferedReader.readLine();
 6             //读到最后一行会返回null,就跳出循环
 7             if (s == null){
 8                 break;
 9             }
10             System.out.println(s);
11         }
12 
13 
14 读取结果:
15 C语言,腻害
16 java,腻害

 

 

 

 

 

 





 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

posted @ 2021-05-04 12:09  kunmin  阅读(994)  评论(0编辑  收藏  举报