Java IO之转换流和缓存流

1 转换流

  在学习字符流(FileReader、FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStreamWriter,这又是什么意思呢?

1.1 OutputStreamWriter类

  查阅OutputStreamWriter的API介绍,OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的字符编码表,将要写入流中的字符编码成字节。它的作用的就是,将字符串按照指定的编码表转成字节,在使用字节流将这些字节写出去。

TestDemo.java

package com.qtw.api;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

public class TestDemo {
    public static void main(String[] args) throws IOException {
        //创建与文件关联的字节输出流对象
        FileOutputStream fos = new FileOutputStream("e:\\test\\b.txt");
        //创建可以把字符转成字节的转换流对象,并指定编码
        OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
        //调用转换流,把文字写出去,其实是写到转换流的缓冲区中
        osw.write("你好");//写入缓冲区。
        osw.close();
        //gbk编码,一个汉字对应2个字节;utf-8编码,一个汉字对应3个字节
    }
}

  OutputStreamWriter流对象,它到底如何把字符转成字节输出的呢?
  其实在OutputStreamWriter流中维护自己的缓冲区,当我们调用OutputStreamWriter对象的write方法时,会拿着字符到指定的码表中进行查询,把查到的字符编码值转成字节数存放到OutputStreamWriter缓冲区中。然后再调用刷新功能,或者关闭流,或者缓冲区存满后会把缓冲区中的字节数据使用字节流写到指定的文件中。

1.2 InputStreamReader类

  查阅InputStreamReader的API介绍,InputStreamReader 是字节流通向字符流的桥梁:它使用指定的字符编码表读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

TestDemo.java

package com.qtw.api;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.InputStreamReader;

public class TestDemo {
    public static void main(String[] args) throws IOException {
        //创建读取文件的字节流对象
        FileInputStream in = new FileInputStream("e:\\test\\a.txt");
        //创建转换流对象
        //InputStreamReader isr = new InputStreamReader(in);这样创建对象,会用本地默认码表读取,将会发生错误解码的错误
        InputStreamReader isr = new InputStreamReader(in,"utf-8");
        //使用转换流去读字节流中的字节
        int ch = 0;
        while((ch = isr.read())!=-1){
            System.out.println((char)ch);
        }
        //关闭流
        isr.close();
    }
}

注意:在读取指定的编码的文件时,一定要指定编码格式,否则就会发生解码错误,而发生乱码现象

1.3 转换流和子类区别

发现有如下继承关系:

OutputStreamWriter:
    |--FileWriter:
InputStreamReader:
    |--FileReader;

父类和子类的功能有什么区别呢?
  OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。字符转换流原理:字节流+编码表。
  FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
FileReader fr = new FileReader("a.txt");
//这三句代码的功能是一样的,其中第三句最为便捷。

注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。

2 缓冲流

  在我们学习字节流与字符流的时候,大家都进行过读取文件中数据的操作,读取数据量大的文件时,读取的速度会很慢,很影响我们程序的效率,那么,我想提高速度,怎么办?Java中提高了一套缓冲流,它的存在,可提高IO流的读写速度。缓冲流,根据流的分类分为字节缓冲流与字符缓冲流。

2.1 字节缓冲流

字节缓冲流根据流的方向,共有2个

  1. 写入数据到流中,字节缓冲输出流 BufferedOutputStream
  2. 读取流中的数据,字节缓冲输入流 BufferedInputStream

它们的内部都包含了一个缓冲区,通过缓冲区读写,就可以提高了IO流的读写速度

2.1.1 字节缓冲输出流BufferedOutputStream

通过字节缓冲流,进行文件的读写操作 写数据到文件的操作
构造方法

public BufferedOutputStream(OutputStream out)

TestDemo.java

package com.qtw.api;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.BufferedOutputStream;

public class TestDemo {
    public static void main(String[] args) throws IOException {
        /*
         * 写数据到文件的方法
         * 1,创建流
         * 2,写数据
         * 3,关闭流
         */

        //创建基本的字节输出流
        FileOutputStream fileOut = new FileOutputStream("e:\\test\\abc.txt");
        //使用高效的流,把基本的流进行封装,实现速度的提升
        BufferedOutputStream out = new BufferedOutputStream(fileOut);
        //2,写数据
        out.write("hello".getBytes());
        //3,关闭流
        out.close();
    }
}

2.1.2 字节缓冲输入流 BufferedInputStream

读取文件中数据的操作
构造方法

public BufferedInputStream(InputStream in) 

TestDemo.java

package com.qtw.api;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.BufferedInputStream;

public class TestDemo {
    public static void main(String[] args) throws IOException {
        /*
         * 从文件中读取数据
         * 1,创建缓冲流对象
         * 2,读数据,打印
         * 3,关闭
         */
        //1,创建缓冲流对象
        FileInputStream fileIn = new FileInputStream("e:\\test\\abc.txt");
        //把基本的流包装成高效的流
        BufferedInputStream in = new BufferedInputStream(fileIn);
        //2,读数据
        int ch = -1;
        while ( (ch = in.read()) != -1 ) {
            //打印
            System.out.print((char)ch);
        }
        //3,关闭
        in.close();
    }
}

2.1.3 使用基本的流与高效的流完成复制文件

  我们一直在说,高效的流速度快并高效,怎么体现呢?需要通过一个复制文件耗时的比较过程,来体验一下高效流带来的快感。
TestDemo.java

package com.qtw.api;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;

public class TestDemo {
    /*
     * 需求:将d:\\test.avi文件进行复制
     *      采用4种方式复制
     *      方式1: 采用基本的流,一次一个字节的方式复制 共耗时 317304毫秒
     *      方式2: 采用基本的流,一个多个字节的方式赋值 共耗时     427毫秒
     *      方式3: 采用高效的流,一次一个字节的方式复制 共耗时    568毫秒
     *      方式4: 采用高效的流,一个多个字节的方式赋值 共耗时      68毫秒
     *
     * 数据源: d:\\test.avi
     * 目的地1: d:\\copy1.avi
     * 目的地2: d:\\copy2.avi
     * 目的地3: d:\\copy3.avi
     * 目的地4: d:\\copy4.avi
     *
     * 实现的步骤:
     *  1,指定数据源
     *  2,指定目的地
     *  3,读数据
     *  4,写数据
     *  5,关闭流
     *
     */

    public static void main(String[] args) throws IOException {
        //开始计时
        long start = System.currentTimeMillis();
        //方式1: 采用基本的流,一次一个字节的方式复制
        //method1("e:\\test\\dd.avi", "e:\\test\\dx.avi");
        //方式2: 采用基本的流,一个多个字节的方式赋值
        //method2("e:\\test\\dd.avi", "e:\\test\\dx.avi");
        //方式3: 采用高效的流,一次一个字节的方式复制
        //method3("e:\\test\\dd.avi", "e:\\test\\dx.avi");
        //方式4: 采用高效的流,一个多个字节的方式赋值
        method4("e:\\test\\dd.avi", "e:\\test\\dx.avi");

        //结束计时
        long end = System.currentTimeMillis();
        //打印耗时多少毫秒
        System.out.println("共耗时 " +(end - start)+ "毫秒");

    }

    //方式4: 采用高效的流,一个多个字节的方式赋值
    private static void method4(String src, String dest) throws IOException {
        //1,指定数据源
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
        //2,指定目的地
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest));
        //3,读数据
        byte[] buffer = new byte[1024];
        int len = -1;
        while ( (len = in.read(buffer)) != -1) {
            //4,写数据
            out.write(buffer, 0, len);
        }
        //5,关闭流
        in.close();
        out.close();
    }

    //方式3: 采用高效的流,一次一个字节的方式复制
    private static void method3(String src, String dest) throws IOException {
        //1,指定数据源
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(src));
        //2,指定目的地
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(dest));
        //3,读数据
        int ch = -1;
        while ((ch=in.read()) != -1) {
            //4,写数据
            out.write(ch);
        }
        //5,关闭流
        in.close();
        out.close();
    }

    //方式2: 采用基本的流,一个多个字节的方式赋值
    private static void method2(String src, String dest) throws IOException {
        //1,指定数据源
        FileInputStream in = new FileInputStream(src);
        //2,指定目的地
        FileOutputStream out = new FileOutputStream(dest);
        //3,读数据
        byte[] buffer = new byte[1024];
        int len = -1;
        while ( (len=in.read(buffer)) != -1) {
            //4,写数据
            out.write(buffer, 0, len);
        }
        //5,关闭流
        in.close();
        out.close();
    }

    //方式1: 采用基本的流,一次一个字节的方式复制
    private static void method1(String src, String dest) throws IOException {
        //1,指定数据源
        FileInputStream in = new FileInputStream(src);
        //2,指定目的地
        FileOutputStream out = new FileOutputStream(dest);
        //3,读数据
        int ch = -1;
        while (( ch=in.read()) != -1) {
            //4,写数据
            out.write(ch);
        }
        //5,关闭流
        in.close();
        out.close();
    }
}

2.2 字符缓冲流

字符缓冲输入流 BufferedReader
字符缓冲输出流 BufferedWriter
完成文本数据的高效的写入与读取的操作

2.2.1 字符缓冲输出流 BufferedWriter

将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

void newLine() 根据当前的系统,写入一个换行符

TestDemo.java

package com.qtw.api;
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws IOException {
        /*
         * BufferedWriter 字符缓冲输出流
         * 方法
         *  public void newLine()写入一个行分隔符
         *
         * 需求: 通过缓冲输出流写入数据到文件
         * 分析:
         *  1,创建流对象
         *  2,写数据
         *  3,关闭流
         *
         */
        //创建流
        //基本字符输出流
        FileWriter fileOut = new FileWriter("e:\\test\\file.txt");
        //把基本的流进行包装
        BufferedWriter out = new BufferedWriter(fileOut);
        //2,写数据
        for (int i=0; i<5; i++) {
            out.write("hello");
            out.newLine();
        }
        //3,关闭流
        out.close();
    }
}

2.2.2 字符缓冲输入流 BufferedReader

从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

public String readLine() 读取一个文本行,包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null

TestDemo.java

package com.qtw.api;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws IOException {
        /*
         * BufferedReader 字符缓冲输入流
         *
         * 方法:
         *  String readLine()
         * 需求:从文件中读取数据,并显示数据
         */
        //1,创建流
        BufferedReader in = new BufferedReader(new FileReader("e:\\test\\file.txt"));
        //2,读数据
        //一次一个字符
        //一次一个字符数组
        //一次读取文本中一行的字符串内容
        String line = null;
        while( (line = in.readLine()) != null ){
            System.out.println(line);
        }
        //3,关闭流
        in.close();
    }
}

2.2.3 使用字符缓冲流完成文本文件的复制

刚刚我们学习完了缓冲流,现在我们就使用字符缓冲流的特有功能,完成文本文件的复制
TestDemo.java

package com.qtw.api;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;

public class TestDemo {
    public static void main(String[] args) throws IOException {
        /*
         * 采用高效的字符缓冲流,完成文本文件的赋值
         *
         * 数据源: file.txt
         * 目的地: copyFile.txt
         *
         * 分析:
         *  1,指定数据源, 是数据源中读数据,采用输入流
         *  2,指定目的地,是把数据写入目的地,采用输出流
         *  3,读数据
         *  4,写数据
         *  5,关闭流
         */

        //1,指定数据源, 是数据源中读数据,采用输入流
        BufferedReader in = new BufferedReader(new FileReader("e:\\test\\file.txt"));
        //2,指定目的地,是把数据写入目的地,采用输出流
        BufferedWriter out = new BufferedWriter(new FileWriter("e:\\test\\copyFile.txt"));
        //3,读数据
        String line = null;
        while ( (line = in.readLine()) != null ) {
            //4,写数据
            out.write(line);
            //写入换行符号
            out.newLine();
        }
        //5,关闭流
        out.close();
        in.close();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348