IO流——字节流和字符流

来源:互联网 发布:c语言读取目录文件名 编辑:程序博客网 时间:2024/05/29 16:21
---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

20,字节流和字符流

20.1 IO

IO流是用来处理设备之间的数据传输,IO流按照操作的数据不同分为:字节流和字符流,按照流向分为:输入流和输出流,他们都在java.io包中。

如图:

  

 

用来处理字节数据的流对象,就是字节流

用来处理字符数据的流对象,就是字符流。

 

字符流相当于,字节流+编码表,也就是通过字节流读取字节数据,再进行查表获取字符,再对字符进行操作。

因为操作字节数据和字符数据的流对象很多,对他们进行共性内容的向上抽取,就形成了字节流和字符流的继承体系。

字节流的两个抽象基类:InputStreamOutputStream.

字符流的两个抽象基类:ReaderWriter.

他们各自的子类,都以其父类作为后缀。

20.2字符流的子类FileReaderFileWriter

FileReader是用于读入字符数据的输入流,其构造函数,可以接收一个File对象,也可以是字符串路径。其构造函数接收的File对象或一个目标文件的路径,代表的是源数据,也就是被读取的数据,如果文件不存在会抛出异常。

FileWriter 是用来写入字符数据的输出流,其构造函数,也可以接收一个File对象,或字符串路径。其构造函数接收的File对象或一个目标文件的路径,表示的是目标数据,也就是被写入的数据。

FileReader FileWriter是用来操作文件对象的流,可以将读取到的源文件数据,写到另一个文件中。FileWriter中接收的目标文件,如果不存在,会创建一个文件,如果存在就会覆盖,FileWriter支持续写,在创建FileWriter对象时,可以使用FileWriter(File file, boolean append)FileWriter(String fileName, boolean append)构造函数,在第二个参数传入true,就可以对目标文件进行续写,而不是一次次的覆盖。

 

FileReader对象的read()方法,一次读取一个字符,如果读取不到字符则返回-1.

FileWriter对象的writer()方法,将读取到的字符写入目标文件。

注意:字符流每次写入后都要调用一次flush()将流上的数据刷入目标文件。当然,最后的close()也会自动刷新流上的数据。

import java.io.File;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;public class IODemo{      public static void main(String []args)throws IOException{                   demo_1();             }      public static void demo_1()throws IOException {             FileReader fr=new FileReader(new File("d:\\a.txt"));//创建一个FileReader对象,接收一个源文件。             FileWriter fw=new FileWriter(new File("d:\\b.txt"),true);//创建一个FileWriter对象,并支持续写,接收一个目标文件。             int ch=0;//用于记录数据的变量。             while((ch=fr.read())!=-1){//如果数据量大,这里需要循环读取。                    fw.write(ch);//将读取到的数据写入输出流。                    fw.flush();//刷新数据到目的地。             }             fw.close();//关闭输出流。             fr.close();//关闭输入流。      }}  

上面代码中,只用了一个变量来记录读取到的字符,对于较大的文件,效率会很低时,这时可以自定义一个字符数组,用来作为临时容器存储读取到的字符数据,然后再写入目标文件中。

如:字符流,使用自定义缓冲区。

import java.io.File;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;public class IODemo{      public static void main(String []args)throws IOException{                   demo_1();             }      public static void demo_1()throws IOException{             FileReader fr=new FileReader(new File("d:\\a.txt"));//创建一个FileReader对象。             FileWriter fw=new FileWriter(new File("d:\\b.txt"),true);//创建一个FileWriter对象,并支持续写。             char[]chs=newchar[1024];//定义一个字符数组作为临时容器,存储读取到的字符。             int len=0;//定义一个变量记录读取的字符数。             while((len=fr.read(chs))!=-1){//循环读取源文件数据,将数据读入字符数组。                    fw.write(chs,0,len);//将读取到的数据写入输出流。                    fw.flush();//刷新数据到目的地。             }             fw.close();//关闭输出流。             fr.close();//关闭输入流。      }} 

 

20.3字符缓冲区流对象BufferedReaderBufferedWriter

当读取的字符数据较大时,也可以使用字符流缓冲区,这两个是装饰类,用于增强字符流的功能,内部维护一个8kB临时容器,用于提高读写的效率。构造函数可以接收字符流的所有子类。

BufferedReader对象的readLine()方法,通过判断换行和回车标记,一次可以读取一行,增强了对字符数据读取的效率。

如:

import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileReader;import java.io.FileWriter;import java.io.IOException;public class IODemo{      public static void main(String []args)throws IOException{                   demo_1();             }       public static void demo_1()throws IOException {             BufferedReader br=new BufferedReader(new FileReader(new File("d:\\a.txt")));//创建一个BufferedReader对象,接收一个字符读取流。             BufferedWriter bw=new BufferedWriter(new FileWriter(new File("d:\\b.txt"),true));//创建一个BufferedWriter对象,接收一个字符写入流             String line=null;             while((line=br.readLine())!=null){//将读取的一行行字符存储在字符串常量line中。                    bw.write(line);//将读取到的字符数据写入输出流。                    bw.newLine();//在行结尾写入换行符。                    bw.flush();//刷新数据到目的地。             }             bw.close();//关闭输出流。             br.close();//关闭输入流。      }}

 BufferedReader对象的readLine()方法,内部实际也是调用了,被装饰对象的read(),只是增加了对换行标记和回车标记的判断。提高字符的读取效率。

BuffereWriter对象的newLine()方法,可以写入换行符,将读到的行,写入目标文件,并加上换行符。

 

20.4字节流的子类:FileInputStreamFileOutputStream

FileInputStream是用来读取字节数据的字节输入流,其构造函数可以接收File对象或字符串路径。字节流既可以读取字符数据,可以读取字节数据。

FileOutputStream是用来写入字节数据的字节输出流,其构造函数可以接收File对象或字符串路径,并支持续写。

import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class IODemo{      public static void main(String []args)throws IOException{                   demo_1();             }      public static void demo_1()throws IOException {             FileInputStream fis=new FileInputStream(new File("d:\\a.txt"));//创建字节输入流对象,关联源文件             FileOutputStream fos=new FileOutputStream(new File("d:\\b.txt"),true);//创建字节输出流对象,关联目标文件。             int ch=0;//定义一个变量,用于存储字节数据。             while((ch=fis.read())!=-1){//循环读取字节数据。                    fos.write(ch);//通过字节输出流写入目标文件。             }             fos.close();//关闭资源。             fis.close();//关闭资源。      }}

FileInputStream类中的read()方法,一次读取一个字节,所以对于数据大的文件,可以循环读取。

以上代码中,只用了一个变量存储读取的字节数据,效率较低,在字符流中可以定义一个字符数组,作为临时容器,在字节流中同样可以定义一个字节数据,作为字节输入流的临时容器,提高读写的效率。

FileOutputStream类的write()将字节数据写入目标文件,因为是字节数据,会直接写入目标文件,所以,不需要调用flush()方法。

如:字节输入流,使用自定义缓冲区

import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class IODemo{      public static void main(String []args)throws IOException{                   demo_1();             }      public static void demo_1()throws IOException {             FileInputStream fis=new FileInputStream(new File("d:\\a.txt"));//创建字节输入流对象,关联源文件             FileOutputStream fos=new FileOutputStream(new File("d:\\b.txt"),true);//创建字节输出流对象,关联目标文件。             byte[]buf=newbyte[1024];//创建字节数组,作为临时缓冲区。             intlen=0;//定义一个变量,记录读取到的字节数。             while((len=fis.read(buf))!=-1){//循环读取字节数据并存储到字节数组中。                    fos.write(buf,0,len);//通过字节输出流写入目标文件。             }             fos.close();//关闭资源。             fis.close();//关闭资源。      }}

20.5字节缓冲区流对象:BufferedInputStream ,BufferedOutputStream

字节流缓冲区和字符流缓冲区一样,都是为了增强流对象的功能,字节缓冲区流也是使用装饰设计模式,用于装饰字节流,其构造函数可以接收字节流的所有子类。

字节缓冲区流中维护一个8KB临时容器提高字节流的读写效率。

如:

import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class IODemo{      public static void main(String []args)throws IOException{                   demo_1();             }      publicstaticvoid demo_1()throws IOException {           BufferedInputStream bis=new BufferedInputStream(new FileInputStream(new File("d:\\a.txt")));//创建字节缓冲区输入流对象,关联源文件             BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(new File("d:\\b.txt"),true));//创建字节缓冲区输出流对象,关联目标文件。             int len=0;//定义一个变量,记录读取到的字节数。             while((len=bis.read())!=-1){//循环读取字节数据并存储到字节数组中。                    bos.write(len);//通过字节输出流写入目标文件。                    bos.flush();//刷新缓冲区。             }             bos.close();//关闭资源。             bis.close();//关闭资源。      }} 

总结:字符流与字节流的读写操作非常相似,只不过字符流中使用了字符数组,而字节流使用的是字节数组。其读取和写入的方式一样的。

 

20.6 IO流的异常处理

IO流在读写操作中,因为许多方法都抛出了异常,所以在不声明异常的情况下,就要对异常进行处理。按照异常处理的原则,对IO流的异常进行处理如下:

import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class IODemo{      public static void main(String []args) {                   demo_1();             }      public static void demo_1(){             //1,将流对象的声明放在try块的外面。             BufferedInputStream bis=null;             BufferedOutputStream bos=null;                          try{             //2,流对象的创建放在try块内部                    bis=new BufferedInputStream(new FileInputStream(new File("d:\\a.txt")));//创建字节缓冲区输入流对象,关联源文件                    bos=new BufferedOutputStream(new FileOutputStream(new File("d:\\b.txt"),true));//创建字节缓冲区输出流对象,关联目标文件。                    int len=0;//定义一个变量,记录读取到的字节数。                    while((len=bis.read())!=-1){//循环读取字节数据并存储到字节数组中。                           bos.write(len);//通过字节输出流写入目标文件。                           bos.flush();//刷新缓冲区。                    }                                 }catch(IOException e){                                 }finally{                    //3,流的关闭要放在finally块中,并进行健壮性判断。                    if(bos!=null)//因为,如果流对象创建失败,如果调用close()方法关闭资源,就会抛出异常,所以进行判断。                    try {                           bos.close();//关闭资源。                    } catch (IOException e) {                           thrownew RuntimeException("关闭失败");                    }                    if(bis!=null)//因为,如果流对象创建失败,如果调用close()方法关闭资源,就会抛出异常,所以进行判断。                    try {                           bis.close();//关闭资源。                    } catch (IOException e) {                           throw new RuntimeException("关闭失败");                   }            }      }}

20.7键盘录入

IO流对象操作的数据,有些是文件中数据,还可能键盘录入的数据。

键盘录入:System.in;

其实就是调用System类中的静态子段in,能够一个字节输入流InputStream= System.in;

所以,键盘录入也就是用字节流读取键盘录入的数据。

如:

import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.IOException;public class IODemo{      public static void main(String []args) {                   demo_1();             }      public static void demo_1() {             BufferedInputStream bis=null;             BufferedOutputStream bos=null;             int len=0;             try {                    bis=new BufferedInputStream(System.in);//读取键盘录入数据。                    bos=new BufferedOutputStream(System.out);//输出键盘录入数据到控制台。                    while((len=bis.read())!=-1){                           bos.write(len);                           bos.flush();                    }             } catch (IOException e) {                    e.printStackTrace();             }             if(bis!=null)             try {                    bis.close();             } catch (IOException e) {                    e.printStackTrace();             }             if(bos!=null)             try {                    bos.close();             } catch (IOException e) {                    e.printStackTrace();             }      }}

PrintStream=System.out;

PrintStreamOutputStream的子类,System.out代表控制台。以上代码将键盘录入的数据,输出到了控制台。

 

20.8转换流:InputStreamReader ,OutputStreamWriter

转换流,是字节流和字符流间的桥梁,可以实现字节流转字符流和字符流转字节流。

什么时候需要用转换流?

1,当源和目标数据都是字节数据,而需要用字符流对他们进行操作时。

2,需要用指定编码表进行编码或解码时,因为转换流的输入流和输出流都可以使用指定的编码表进行编解码

 

如:需要用字符流操作键盘录入的数据。因为键盘录入是字节流,所以需要用转换流将键盘录入的数据转换成字符流。

再由字符流进行操作。

import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;public class IODemo{      public static void main(String []args) {                   demo_1();             }      public static void demo_1() {             BufferedReader br=null;             BufferedWriter bw=null;             int len=0;             try {                    br=new BufferedReader(new InputStreamReader(System.in));//使用转换流,将键盘录入转换成字符流。                    bw=new BufferedWriter(new OutputStreamWriter(System.out));//System.out.是字节流,所以把字符流再转成字节流。                    while((len=br.read())!=-1){                           bw.write(len);                           bw.flush();                    }             } catch (IOException e) {                    e.printStackTrace();             }             if(br!=null)             try {                    br.close();             } catch (IOException e) {                    e.printStackTrace();             }             if(bw!=null)             try {                    bw.close();             } catch (IOException e) {                    e.printStackTrace();            }      }}

当需要用指定的码表进行编码和解码时,可以使用转换流。

InputStreamReader(InputStream in,String charsetName)//charsetName表示指定的码表。

OutputStreamWriter(OutputStream out,String charsetName)//同上

如:

import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.IOException;import java.io.InputStreamReader;import java.io.OutputStreamWriter;public class IODemo{      public static void main(String []args) {                   demo_1();             }      public static void demo_1() {             BufferedReader br=null;             BufferedWriter bw=null;             int len=0;             try {             br=new BufferedReader(new InputStreamReader(System.in,"ISO8859-1"));//使用转换流,将键盘录入转换成字符流。             bw=new BufferedWriter(new OutputStreamWriter(System.out,"ISO8859-1"));//System.out.是字节流,所以把字符流再转成字节流。                    //上面的转换流中,都指定了码表。                    while((len=br.read())!=-1){                           bw.write(len);                           bw.flush();                    }             } catch (IOException e) {                    e.printStackTrace();             }             if(br!=null)             try {                    br.close();             } catch (IOException e) {                    e.printStackTrace();             }             if(bw!=null)             try {                    bw.close();             } catch (IOException e) {                    e.printStackTrace();             }       }}

注意:用什么码表编码,就用什么码表解码。

 

20.9字符打印流:PrintWriter,字节打印流:PrintStream

PrintWriterWriter的字类,是用于将数据打印到目的地的输出流。

PrintWriter的构造函数,可以接收:字节流,File对象,字符串路径,字符流,并有自动刷新的功能和指定编码表。

构造函数:

PrintWriter(File file)
使用指定文件创建不具有自动行刷新的新 PrintWriter。

PrintWriter(File file,String csn)
创建具有指定文件和字符集且不带自动刷行新的新 PrintWriter。

PrintWriter(OutputStream out)
根据现有的 OutputStream 创建不带自动行刷新的新 PrintWriter。

PrintWriter(OutputStream out, boolean autoFlush)
通过现有的 OutputStream 创建新的 PrintWriter。

PrintWriter(String fileName)
创建具有指定文件名称且不带自动行刷新的新 PrintWriter。

PrintWriter(String fileName,String csn)
创建具有指定文件名称和字符集且不带自动行刷新的新 PrintWriter。

PrintWriter(Writer out)
创建不带自动行刷新的新 PrintWriter。

PrintWriter(Writer out, boolean autoFlush)
创建新 PrintWriter。

PrintWriter对象的print(),println()方法,可以将数据打印到目的地,并保证数据的原有表示形式。

print().println()方法可以接收各种数据类型参数,如:int,long,float,double,字符数组等,

writer()方法,在某些情况下并不能保证按原有表示形式写入目的地的。

且,PrintWriter对象,可以将数据输出到一个字节流上。

 

PrintStream字节打印流,相当于:System.out;此流与其他输出流不同,永远不会抛出IOException.

构造函数:

PrintStream(File file)
创建具有指定文件且不带自动行刷新的新打印流。

PrintStream(File file,String csn)
创建具有指定文件名称和字符集且不带自动行刷新的新打印流。

PrintStream(OutputStream out)
创建新的打印流。

PrintStream(OutputStream out, boolean autoFlush)
创建新的打印流。

PrintStream(OutputStream out, boolean autoFlush,String encoding)
创建新的打印流。

PrintStream(String fileName)
创建具有指定文件名称且不带自动行刷新的新打印流。

PrintStream(String fileName,String csn)
创建具有指定文件名称和字符集且不带自动行刷新的新打印流。

PrintStream可将数据打印到File对象,字节流,字符串路径所表示的目的地。也具有自动刷新的功能。将数据自动刷入目的地。其print().println()方法可以接收各种数据类型参数,如:int,long,float,double,字节数组,字符数组等,也能够保持数据原有的表示形式。

如:

public class IODemo{      public static void main(String []args)throws FileNotFoundException {                   demo_1();             }      public static void demo_1()throws FileNotFoundException {             PrintWriter pw=new PrintWriter("d:\\b.txt");             pw.print(97);//文件中的内容是:97,但是write(97),文件中的内容是:a             pw.flush();             pw.close();              }} 

字节流和字符流都有很多的流对象,如何去明确具体使用哪一个对象呢?

总结,流对象的操作规律。

1,分析源和目的

操作源的流:InputStream ,Reader

操作目的的流:OutputStream ,Writer

2,是否是纯文本数据

源,是纯文本数据:Reader,否则,InputStream

目地,是纯文本数据:Writer,否则,OutputStream

3,分析设备

源:

硬盘:File   键盘:System.in  内存:数组    网络:Socket

目的:

硬盘:File   键盘:System.out  内存:数组    网络:Socket

4,是否需要额外功能

1,是否需要缓冲区:

源:BufferedReader ,BufferedInputStream

目的:BufferedWriter ,BufferedOutputStream

2,是否需要转换流

源:InputStreamReader

目的:OutputStreamWriter

……

 

 

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------