Java IO流02

字节缓冲区流

字节流一次读写一个数组的速度比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果。

  • 字节缓冲输出流BufferedOutputStream
  • 字节缓冲输入流BufferedInputStream

字节缓冲输出流

构造方法:BufferedOutputStream(OutputStream out)
这种构造方法提供了一个默认的缓冲区大小8192
构造方法传递的是OutputStream,而不是具体的文件或路径,因为字节缓冲区仅仅提供缓冲区,真正的底层读写数据还得基本的流对象进行操作

        //创建对象
        //FileOutputStream fos = new FileOutputStream("a.txt");
        //BufferedOutputStream bos = new BufferedOutputStream(fos);
        //等价于如下
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt"));
        bos.write("hello".getBytes());
        bos.close();

字节缓冲输入流

构造方法:BufferedIntputStream(IntputStream in)

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
        //方式1:一次读取一个字节
        int by;
        while((by=bis.read())!=-1) {
            System.out.print((char)by);
        }
        //方式2:一次读取一个字节数组
        byte[] bys = new byte[1024];
        int len;
        while((len=bis.read(bys))!=-1) {
            System.out.print(new String(bys,0,len));
        }
        bis.close();

字节流四种方式复制AVI并测试效率

需求:把d:\\复制图片.avi复制到当前项目目录下的copy.avi中
四种方式比较复制效率
基本字节流一次读写一个字节
基本字节流一次读写一个字节数组
缓冲字节流一次读写一个字节
缓冲字节流一次读写一个字节数组
提示:复制文件的时间计算可以采用System类的方法实现:public static long currentTimeMillis():返回以毫秒为单位的当前时间

把四种方式写成方法:

    //基本字节流一次读写一个字节
    private static void method1() throws IOException {
        //封装数据源
        FileInputStream fis = new FileInputStream("d:\\复制图片.avi");
        //封装目的地
        FileOutputStream fos = new FileOutputStream("copy.avi");

        int by;
        while((by=fis.read())!=-1) {
            fos.write(by);
        }

        fos.close();
        fis.close();
    }

    //基本字节流一次读写一个字节数组
    private static void method2() throws IOException {
        FileInputStream fis = new FileInputStream("d:\\复制图片.avi");
        FileOutputStream fos = new FileOutputStream("copy.avi");

        byte[] bys = new byte[1024];
        int len;
        while((len=fis.read(bys))!=-1) {
            fos.write(bys,0,len);
        }

        fis.close();
        fos.close();
    }

    //缓冲字节流一次读写一个字节
    private static void method3() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\复制图片.avi"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.avi"));

        int by;
        while((by=bis.read())!=-1) {
            bos.write(by);
        }

        bos.close();
        bis.close();
    }

    //缓冲字节流一次读写一个字节数组
    private static void method4() throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\复制图片.avi"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.avi"));

        byte[] bys = new byte[1024];
        int len;
        while((len=bis.read(bys))!=-1) {
            bos.write(bys,0,len);
        }

        bos.close();
        bis.close();
    }

注释掉其余方法,分开测试,计算时间

    public static void main(String[] args) throws IOException {
        //记录开始时间
        long start = System.currentTimeMillis();

        method1(); //共耗时50233毫秒
        method2(); //共耗时79毫秒
        method3(); //共耗时197毫秒
        method4(); //共耗时30毫秒

        //记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("共耗时:"+(end-start)+"毫秒");
    }

复制效率最高的方式是:缓冲字节流一次读写一个字节数组

转换流出现的原因

字节流读数据可能出现问题:
字节流一次读取一个字节的方式读取带有文字的文件是有问题的,因为读取到一个字节后就转为字符在控制台输出了,而汉字是由2个字节组成的,所以这里会出问题

        FileInputStream fis = new FileInputStream("a.txt");

        int by;
        while((by=fis.read())!=-1) {
            System.out.print((char)by); //输出ä½ å¥½,实际文件中是你好
        }

文件复制的时候,字节流读取一个字节,写入一个字节,没有出现问题,是因为最终底层会根据字节做拼接得到汉字

汉字存储的规则:左边的字节数据肯定是负数,右边的字节可能是负数,也可能是整数,大部分情况下是负数

        String s = "hello";
        byte[] bys = s.getBytes();
        System.out.println(Arrays.toString(bys)); //[104, 101, 108, 108, 111]
        String s2 = "你好";
        byte[] bys2 = s2.getBytes();
        System.out.println(Arrays.toString(bys2));

编码表

编码表:由字符及其对应的数据组成的一张表

常见的编码表:

  • ASCII:美国标准信息交换码,用一个字节的7位表示数据
  • ISO-8859-1:欧洲码表,用一个字节的8位表示数据,兼容ASCII
  • GB2312:中文码表,兼容ASCII
  • GBK:中文码表的升级版,融合了更多的中文文字符号,兼容ASCII
  • UTF-8:是一种可变长度的字符编码,用1-3个字节表示数据,又称为万国码,兼容ASCII。用在网页上可以统一页面中的中文简体繁体和其他语言的显示。

用同一个编码表编码和解码是正常的,用不同的编码表分开编码解码会出现乱码问题。
乱码问题:针对同一个数据采用的编码和解码的编码表不一致导致的。

String类中的编码和解码问题

编码:把看得懂的变成看不懂的
public byte[] getBytes(String charsetName) throws UnsupportedEncodingException
使用指定的字符集将此String编码为byte序列,并将结果存储到一个新的byte数组中

解码:把看不懂的变成看得懂的
public String(byte[] bytes, String charsetName)
通过使用指定的charset解码指定的byte数组,构造一个新的String

        String s = "你好";

        byte[] bys = s.getBytes(); //使用平台的默认字符集将此String编码为byte序列
        System.out.println(Arrays.toString(bys)); //[-28, -67, -96, -27, -91, -67]

        byte[] bys1 = s.getBytes("GBK"); //指定编码GBK
        System.out.println(Arrays.toString(bys1)); //[-60, -29, -70, -61]

        byte[] bys2 = s.getBytes("UTF-8"); //指定编码GBK
        System.out.println(Arrays.toString(bys2)); //[-28, -67, -96, -27, -91, -67]

        String ss = new String(bys); //通过平台默认的字符集解码指定的byte数组
        System.out.println(ss);

        String ss1 = new String(bys1,"GBK"); //指定编码GBK
        System.out.println(ss1);

        String ss2 = new String(bys1,"UTF-8"); //指定编码UTF-8
        System.out.println(ss2); //出现乱码

编码和解码的方式要一致

转换流

转换流其实就是一个字符流
转换流 = 字节流+编码表

转换流中的编码和解码问题

OutputStreamWriter 字符输出流
public OutputStreamWriter(OutputStream out)
根据默认编码把字节流的数据转换为字符流
public OutputStreamWriter(OutputStream out, String charsetName)
根据指定编码把字节流数据转换为字符流

InputStreamWriter 字符输入流
public InputStreamWriter(InputStream in)
用默认的编码读数据
public InputStreamWriter(InputStream in, String charsetName)
用指定的编码读取数据

        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("osw.txt"));
        osw.write("你好");
        osw.close();

        OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream("osw1.txt"),"GBK");
        osw1.write("你好");
        osw1.close();

        OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("osw2.txt"),"UTF-8");
        osw2.write("你好");
        osw2.close();

        InputStreamReader isr = new InputStreamReader(new FileInputStream("osw.txt"));
        int ch;
        while((ch=isr.read())!=-1) {
            System.out.print((char)ch);
        }
        isr.close();

        InputStreamReader isr1 = new InputStreamReader(new FileInputStream("osw1.txt"),"GBK");
        int ch1;
        while((ch1=isr1.read())!=-1) {
            System.out.print((char)ch1);
        }
        isr1.close();

OutputStreamWriter写数据的5种方式

public void write(int c):写一个字符
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"));

        osw.write(97);
        osw.write('a');
        //写完数据后没有发现数据
        //1个字符等于两个字节,文件中的数据存储的基本单位是字节
        //void flush():刷新该流的缓冲
        osw.flush();
        
        osw.close(); //关闭此流,但要先刷新
public void write(char[] cbuf):写一个字符数组
        char[] chs = {'a', 'b','c', 'd', 'e'};
        osw.write(chs);
public void write(char[] cubf, int off, int len):写一个字符数组的一部分
        char[] chs = {'a', 'b','c', 'd', 'e'};
        osw.write(chs,1,3); //bcd
public void write(String str):写一个字符串
        osw.write("hello");
public void write(String str, int off, int len):写一个字符串的一部分
        osw.write("hello",0,3); //hel

InputStreamReader读数据的2种方式

public int read():一次读取一个字符
        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));

        int ch;
        while((ch=isr.read())!=-1) {
            System.out.print((char)ch);
        }

        isr.close();
public int read(char[] cbuf):一次读取一个字符数组
        char[] chs = new char[1024];
        int len;
        while((len=isr.read(chs))!=-1) {
            System.out.print(new String(chs,0,len));
        }

字符流的练习

复制Java文件

需求:
把当前项目目录下的StringDemo.java内容复制到当前项目下Copy.java中

数据源:StringDemo.java – 读数据 – 字符流 – InputStreamReader
目的地:Copy.java – 写数据 – 字符流 – OutputStreamWriter

方式1:一次读写一个字符

        InputStreamReader isr = new InputStreamReader(new FileInputStream("StringDemo.java"));
        OutputStreamWriter osr = new OutputStreamWriter(new FileOutputStream("copy.java"));

        int ch;
        while((ch=isr.read())!=-1) {
            osr.write(ch);
        }

        isr.close();
        osr.close();

方式2:一次读写一个字符数组

        char[] chs = new char[1024];
        int len;
        while((len=isr.read(chs))!=-1) {
            osr.write(new String(chs,0,len));
        }

复制Java文件改进版

需求:
把当前项目目录下的StringDemo.java内容复制到当前项目下Copy.java中(改进版)
转换流的名字比较长,而我们常见的操作都是按照的本地默认编码实现的,为了简化书写,转换流提供了对应的子类:
FileWriter:写入字符文件的便捷类
FileReader:读取字符文件的便捷类

OutputStreamWriter = FileOutputStream + 编码表
FileWriter = FileOutputStream + 编码表
构造方法: FileWriter(String fileName)

InputStreamReader = FileInputStream + 编码表
FileReader = FileInputStream + 编码表
构造方法: FileReader(String fileName)

数据源:StringDemo.java – 读数据 – 字符流 – FileReader
目的地:Copy.java – 写数据 – 字符流 – FileWriter

方式1:一次读写一个字符

    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("StringDemo.java");
        FileWriter fw = new FileWriter("copy.java");

        int ch;
        while((ch=fr.read())!=-1) {
            fw.write(ch);
        }

        fw.close();
        fr.close();
    }

方式2:一次读写一个字符数组

       char[] chs = new char[1024];
        int len;
        while((len=fr.read(chs))!=-1) {
            fw.write(chs,0,len);
        }

字符缓冲区流

字符缓冲输出流

BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。可以指定缓冲区大小,或者接受默认的大小。在大多数情况下,默认值就足够大了

构造方法:BufferedWriter(Writer out)

        BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
        
        bw.write("hello");
        
        bw.close();

字符缓冲输入流

BufferedReader:从字符输入流中读取文本,缓冲哥哥自负,从而实现字符、数组和行的高效读取。可以指定缓冲区大小,或者接受默认的大小。在大多数情况下,默认值就足够大了

构造方法:BufferedReader(Reader in)

        BufferedReader br = new BufferedReader(new FileReader("StringDemo.java"));

        int ch;
        while((ch=br.read())!=-1) {
            System.out.print((char)ch);
        }

        br.close();
        char[] chs = new char[1024];
        int len;
        while((len=br.read(chs))!=-1) {
            System.out.print(new String(chs,0,len));
        }

练习 复制文本文件

需求:把项目目录下的a.txt内容复制到项目目录下的b.txt中

数据源:a.txt – 读数据 – 字符流 – InputStreamReader – FileReader – BufferedReader

目的地:b.txt – 写数据 – 字符流 – OutputStreamWriter – FileWriter – BufferedWriter

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));

        int ch;
        while((ch=br.read())!=-1) {
            bw.write(ch);
        }

        br.close();
        bw.close();
    }
        char[] chs = new char[1024];
        int len;
        while((len=br.read(chs))!=-1) {
            bw.write(new String(chs,0,len));
        }

特殊功能

BufferedWriter
void newLine():写入一个行分隔符,这个行分隔符是由系统决定的

        BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));

        for(int x=0; x<3; x++) {
            bw.write("hello");
            //bw.write("\r\n");
            bw.newLine();
            bw.flush();
        }

        bw.close();

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

        BufferedReader br = new BufferedReader(new FileReader("bw.txt"));

        String line = br.readLine();
        System.out.println(line); //hello1
        line = br.readLine();
        System.out.println(line); //hello2
        line = br.readLine();
        System.out.println(line); //hello3
        line = br.readLine();
        System.out.println(line); //null

		//循环改进
        String line1;
        while((line1=br.readLine())!=null) {
            System.out.print(line1); //hello1hello2hello3
        }

        br.close();

特殊功能复制Java文件

数据源:StringDemo.java – BufferedReader
目的地:copy.java – BufferedWriter

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("StringDemo.java"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("copy.java"));

        String line;
        while((line=br.readLine())!=null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        br.close();
        bw.close();
    }

字符流练习

5种方式复制文本文件

数据源:d:\\林青霞.txt

目的地:窗里窗外.txt

5种方式

  1. 基本字符流一次读写一个字符
  2. 基本字符流一次读写一个字符数组
  3. 缓冲字符流一次读写一个字符
  4. 缓冲字符流一次读写一个字符数组
  5. 缓冲字符流一次读写一个字符串
    public static void main(String[] args) throws IOException {
        method1();
        method2();
        method3();
        method4();
        method5();
    }

    public static void method1() throws IOException {
        FileReader fr = new FileReader("d:\\林青霞.txt");
        FileWriter fw = new FileWriter("窗里窗外.txt");

        int ch;
        while((ch=fr.read())!=-1) {
            fw.write(ch);
        }

        fr.close();
        fw.close();
    }

    public static void method2() throws IOException {
        FileReader fr = new FileReader("d:\\林青霞.txt");
        FileWriter fw = new FileWriter("窗里窗外.txt");

        char[] chs = new char[1024];
        int len;
        while((len=fr.read(chs))!=-1) {
            fw.write(chs,0,len);
        }

        fr.close();
        fw.close();
    }

    public static void method3() throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("d:\\林青霞.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("窗里窗外.txt"));

        int ch;
        while((ch=br.read())!=-1) {
            bw.write(ch);
        }

        br.close();
        bw.close();
    }

    public static void method4() throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("d:\\林青霞.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("窗里窗外.txt"));

        char[] chs = new char[1024];
        int len;
        while((len=br.read(chs))!=-1) {
            bw.write(chs,0,len);
        }

        br.close();
        bw.close();
    }

    public static void method5() throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("d:\\林青霞.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("窗里窗外.txt"));

        String line;
        while((line=br.readLine())!=null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        br.close();
        bw.close();
    }

把集合中的字符串数据存储到文本文件

需求:把ArrayList集合中的字符串数据存储到文本文件,每一个字符串元素作文文件中的一行数据

分析:

  1. 创建集合对象
  2. 往集合中添加字符串元素
  3. 创建字符缓冲输出流对象
  4. 遍历集合得到每一个字符串元素,把字符串元素作为数据写入到文本文件
  5. 释放资源
        ArrayList<String> array = new ArrayList<>();

        array.add("hello");
        array.add("world");
        array.add("java");

        BufferedWriter bw = new BufferedWriter(new FileWriter("array.txt"));

        for(String s:array) {
            bw.write(s);
            bw.newLine();
            bw.flush();
        }

        bw.close();

把文本文件中的字符串数据读取到集合

需求:从文本文件中读取数据到ArrayList集合中,并遍历集合。每一行数据作为一个字符串元素

分析:

  1. 创建字符缓冲输入流对象
  2. 创建集合对象
  3. 读取数据,每一次读取一行并把该数据作为元素存储到集合中
  4. 释放资源
  5. 遍历集合
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("array.txt"));
        ArrayList<String> array = new ArrayList<>();

        String line;
        while((line=br.readLine())!=null) {
            array.add(line);
        }

        br.close();

        for(String s: array) {
            System.out.println(s);
        }
    }

集合中的学生对象数据存储到文本文件

需求:把ArrayList集合中的学生数据存储到文本文件,每一个学生数据作为文件中的一行数据

定义一个学生类:学号,姓名,年龄,所在地

public class Student {
    private String sid;
    private String name;
    private int age;
    private String city;

    public Student() {}

    public Student(String sid, String name, int age, String city) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.city = city;
    }

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        return "Student{" +
                "sid='" + sid + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", city='" + city + '\'' +
                '}';
    }
}

分析:

  1. 创建集合对象
  2. 创建学生对象
  3. 把学生对象添加到集合中
  4. 创建字符缓冲输出流对象
  5. 遍历集合,得到每一个学生对象,然后把该对象的数据拼接成一个制定合适的字符串写到文本文件
  6. 释放资源
    public static void main(String[] args) throws IOException {
        ArrayList<Student> array = new ArrayList<>();

        Student s1 = new Student("it001","林青霞",30,"西安");
        Student s2 = new Student("it002","张曼玉",35,"北京");
        Student s3 = new Student("it003","王祖贤",33,"上海");

        array.add(s1);
        array.add(s2);
        array.add(s3);

        BufferedWriter bw = new BufferedWriter(new FileWriter("student.txt"));

        for(Student s: array) {
            bw.write(s.getSid()+","+s.getName()+","+s.getAge()+","+s.getCity());
            bw.newLine();
            bw.flush();
        }

        bw.close();
    }

把文本文件中的学生对象数据读取到集合

需求:从文本文件中读取学生数据到ArrayList集合中,并遍历集合。每一行数据作为一个学生元素
提示:这里要使用String中的一个方法:split()

分析:

  1. 创建字符缓冲输入流对象
  2. 创建集合对象
  3. 读取数据,每一次读取一行数据,把该行数据想办法封装成学生对象并把学生对象存储到集合中
  4. 释放资源
  5. 遍历集合
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("Student.txt"));
        ArrayList<Student> array = new ArrayList<>();

        String line;
        while((line=br.readLine())!=null) {
            String[] strArray = line.split(",");

            Student s = new Student();
            s.setSid(strArray[0]);
            s.setName(strArray[1]);
            s.setAge(Integer.parseInt(strArray[2]));
            s.setCity(strArray[3]);

            array.add(s);
        }

        br.close();

        for(Student s : array) {
            System.out.println(s);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值