IO流之字符流

1. 字符流概述

由于字节流操作中文不是特别的方便,所以 Java 就提供了字符流。

字符流 = 字节流 + 编码表

用字节流复制文本文件时,文本文件也会有中文,但是不会出现问题,原因是最终底层操作会自动将复制到的内容进行字节拼接成中文,那么如何识别它是中文呢?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数。

2. 编码表

基础知识:
① 计算机中存储的信息都是用二进制数表示的,我们在屏幕上看到的英文、汉字等字符都是二进制数转换之后的结果;
② 按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。按照 A 编码存储,就必须按照 A 编码解析,这样才能显示正确的文本符号,否则就会导致乱码;
③ 字符编码就是一套自然语言的字符与二进制数之间的对应规则(A,65);
④ 常见的字符集有 ASCLL 字符集、GBXXX字符集、Unicode 字符集等,其中 GBK 是最常用的中文码表,Unicode 字符集是为表达任意语言的任意字符而设计的,被称为统一码、标准万国码,最为常用的是 UTF-8 编码,互联网工程工作小组要求所有互联网协议都必须支持 UTF-8 编码

采用何种规则编码,就要采用对应的规则解码,否则就会出现乱码!

3. 编码和解码

① 编码:
byte[] getBytes():使用平台默认的字符集将该 String 编码为一系列字节,将结果存储到新的字节数组中;
byte[] getBytes(String charsetName):使用指定的字符集将该 String 编码为一系列字节,将结果存储到新的字节数组中。

② 解码:
String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的 String;
String(byte[] bytes, String charsetName):通过指定的字符集解码指定的字节数组来构造新的 String。

//Test.java

package com.zxe;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        String s = "中国伟大";
        byte[] bys = s.getBytes("GBK");
        String ss = new String(bys, "GBK");
        System.out.println(ss);
    }
}

在这里插入图片描述

以上是字符串中的编码和解码问题,接下来我们看一下字符流中的编码解码问题。

① InputStreamRead:是从字节流到字符流的桥梁,它读取字节,并使用指定的编码将其解码为字符,它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集;
② OutputStreamWriter:是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节,它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。


OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("idea_test\\myCharStream.txt"), "GBK");
osw.write("我爱你中国!");
osw.close();
InputStreamReader isr = new InputStreamReader(new FileInputStream("idea_test\\myCharStream.txt"), "GBK");
int ch;
while ((ch = isr.read()) != -1) {
    System.out.print((char) ch);
}
isr.close();

4. 写数据的方式

在这里插入图片描述
在这里插入图片描述

用字符流写数据是有缓冲的,写入的内容在文件中一下显示不出来,需要用刷新流方法 flush() 实现刷新!

//Test.java

package com.zxe;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("idea_test\\myCharStream.txt"));
        osw.write(97);
        char[] chs = {'a', 'b', 'c', 'd'};
        osw.write(chs);
        osw.write(chs, 0, chs.length);
        osw.write("qweqq");
        osw.write("qweqq", 2, 3);
        osw.flush();
        osw.close();
    }
}

在这里插入图片描述

从 off 索引开始切入,取 len 长度,注意 len 代表的是所取字符的个数,并不是索引!

5. 读数据的方式

在这里插入图片描述


InputStreamReader isr = new InputStreamReader(new FileInputStream("idea_test\\myCharStream.txt"));
//一次读一个字符数据
int ch;
while ((ch = isr.read()) != -1) {
    System.out.print((char) ch);
}
//一次读一个字符数组数据
char[] chs = new char[1024];
int len;
while ((len = isr.read(chs)) != -1) {
    System.out.println(new String(chs, 0, len));
}
isr.close();

字符流读数据和字节流读数据的格式是一样的!

6. 转换流的子类

需求:把模块目录下的某文件内容复制到模块目录下的另一文件中。

分析:
① 我们可以使用上面的 InputStreamReader 和 OutputStreamWriter 转换流进行操作,但转换流的名字太长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化书写,转换流为我们提供了对应的子类;
② FileReader(String fileName),用于读取字符文件的便捷类;
③ FileWriter(String fileName),用于写入字符文件的便捷类。

//Test.java

package com.zxe;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("idea_test\\myCharStream.txt");
        FileWriter fw = new FileWriter("idea_test\\copy.txt");
        char[] chs = new char[1024];
        int len;
        while ((len = fr.read(chs)) != -1) {
            fw.write(chs, 0, len);
        }
        fr.close();
        fw.close();
    }
}

在这里插入图片描述

如果不指定编码类型,我们完全可以使用其子类,简化代码!

7. 字符缓冲流

字符缓冲流:
① BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小,默认值足够大,可用于大多数用途;
② BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符、数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小,默认值足够大,可用于大多数用途。


BufferedWriter bw = new BufferedWriter(new FileWriter("idea_test\\myCharStream.txt"));
String s = "我爱你中国!";
bw.write(s, 0, s.length());
bw.close();
        

7.1 特有功能

① newLine():写一行行分隔符。行分隔符字符串由系统属性定义,该换行适用于任何系统;
② readLine():读一行文字。结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为 null。

//Test.java

package com.zxe;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("idea_test\\myCharStream.txt"));
        BufferedReader br = new BufferedReader(new FileReader("idea_test\\myCharStream.txt"));
        for (int i = 0; i < 10; i++) {
            bw.write("hello" + i);
            bw.newLine();
            bw.flush();
        }
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
        bw.close();
    }
}

在这里插入图片描述

在这里插入图片描述

注意一次读取一行的时候,这里是只读内容不读换行符号,所以要想换行得用 println!

7.2 复制文件

//Test.java

package com.zxe;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("idea_test\\copy.txt"));
        BufferedReader br = new BufferedReader(new FileReader("idea_test\\myCharStream.txt"));
        String line;
        while ((line = br.readLine()) != null) {
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        br.close();
        bw.close();
    }
}

用字符缓冲流的特有功能复制文件,是我们最常用的一种方法!

7.3 点名器

需求:给出一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器。

思路:
① 创建字符缓冲输入流对象;
② 创建 ArrayList 集合对象;
③ 调用字符缓冲输入流对象的方法读数据;
④ 把读取到的字符串数据存储到集合中;
⑤ 释放资源;
⑥ 使用 Random 产生一个随机数,随机数的范围在 [0,集合的长度];
⑦ 把第 6 步产生的随机数作为索引到 ArrayList 集合中获取值;
⑧ 把第 7 步得到的数据输出在控制台。

package com.zxe;

import java.io.*;
import java.util.ArrayList;
import java.util.Random;

public class Test {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new FileReader("idea_test\\student.txt"));
        ArrayList<String> array = new ArrayList<>();

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

        Random r = new Random();
        int index = r.nextInt(array.size());
        System.out.println(array.get(index));
    }
}

在这里插入图片描述

在这里插入图片描述

7.4 总分排名

需求:键盘录入 5 个学生信息(学号,姓名,语文成绩,数学成绩,英语成绩)。要求按照成绩总分从高到低写入文本文件。

思路:
① 定义学生类;
② 创建 TreeSet 集合,通过比较器排序进行排序;
③ 键盘录入学生数据;
④ 创建学生对象,把键盘录入的数据对应赋值给学生的成员变量;
⑤ 把学生对象添加到 TreeSet 集合;
⑥ 创建字符缓冲输出流对象;
⑦ 遍历集合,得到每一个学生对象;
⑧ 把学生对象的数据拼接成指定格式的字符串;
⑨ 调用字符缓冲输出流对象的方法写数据。

package com.zxe;

import java.util.Objects;

public class Student {
    private int Sno;
    private String Sname;
    private int chinese;
    private int math;
    private int english;

    public Student() {
    }

    public Student(int sno, String sname, int chinese, int math, int english) {
        Sno = sno;
        Sname = sname;
        this.chinese = chinese;
        this.math = math;
        this.english = english;
    }

    public int getSno() {
        return Sno;
    }

    public void setSno(int sno) {
        Sno = sno;
    }

    public String getSname() {
        return Sname;
    }

    public void setSname(String sname) {
        Sname = sname;
    }

    public int getChinese() {
        return chinese;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public int getMath() {
        return math;
    }

    public void setMath(int math) {
        this.math = math;
    }

    public int getEnglish() {
        return english;
    }

    public void setEnglish(int english) {
        this.english = english;
    }

    public int getSum() {
        int sum = chinese + math + english;
        return sum;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return Sno == student.Sno && chinese == student.chinese && math == student.math && english == student.english && Objects.equals(Sname, student.Sname);
    }

    @Override
    public int hashCode() {
        return Objects.hash(Sno, Sname, chinese, math, english);
    }
}

package com.zxe;

import java.io.*;
import java.util.Comparator;
import java.util.Scanner;
import java.util.TreeSet;

public class Test {
    public static void main(String[] args) throws IOException {

        //创建TreeSet集合,通过比较器排序法进行排序
        TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int num1 = o2.getSum() - o1.getSum();
                int num2 = num1 == 0 ? o2.getSno() - o1.getSno() : num1;
                return num2;
            }
        });

        //键盘录入学生对象并添加到TreeSet集合
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < 5; i++) {
            System.out.println("请录入第" + (i + 1) + "个同学的信息:");
            System.out.print("学号:");
            int sno = sc.nextInt();
            System.out.print("姓名:");
            String sname = sc.next();
            System.out.print("语文成绩:");
            int chinese = sc.nextInt();
            System.out.print("数学成绩:");
            int math = sc.nextInt();
            System.out.print("英语成绩:");
            int english = sc.nextInt();
            Student s = new Student(sno, sname, chinese, math, english);
            ts.add(s);
        }

        //遍历TreeSet集合将学生信息有序写入文本文件
        BufferedWriter bw = new BufferedWriter(new FileWriter("idea_test\\grades.txt"));
        bw.write("学号  姓名  语文  数学  英语  总分");
        bw.newLine();
        for (Student s : ts) {
            StringBuilder sb = new StringBuilder();
            sb.append(s.getSno()).append("  ").append(s.getSname()).append("  ")
                    .append(s.getChinese()).append("  ").append(s.getMath()).append("  ").
                    append(s.getEnglish()).append("  ").append(s.getSum());
            bw.write(sb.toString());
            bw.newLine();
            bw.flush();
        }
        bw.close();
    }
}

在这里插入图片描述
在这里插入图片描述

8. 复制文件夹

8.1 复制单级文件夹

package com.zxe;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        File srcFolder = new File("F:\\tyut");
        String srcFolderName = srcFolder.getName();
        File destFolder = new File("idea_test", srcFolderName);
        if (!destFolder.exists()) {
            destFolder.mkdir();
        }
        File[] files = srcFolder.listFiles();
        for (File srcFile : files) {
            String srcFileName = srcFile.getName();
            File destFile = new File(destFolder, srcFileName);
            copyFile(srcFile, destFile);
        }
    }
    public static void copyFile(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
        byte[] bys = new byte[1024];
        int len;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
        bis.close();
        bos.close();
    }
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

8.2 复制多级文件夹

package com.zxe;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        File srcFolder = new File("F:\\tyut");
        File destFolder = new File("idea_test");
        copyFolder(srcFolder, destFolder);
    }

    public static void copyFolder(File srcFolder, File destFolder) throws IOException {
        if (srcFolder.isDirectory()) {
            String srcFileName = srcFolder.getName();
            File newFolder = new File(destFolder, srcFileName);
            if (!newFolder.exists()) {
                newFolder.mkdir();
            }
            File[] srcFiles = srcFolder.listFiles();
            for (File srcFile : srcFiles) {
                copyFolder(srcFile, newFolder);
            }
        } else {
            File newFile = new File(destFolder, srcFolder.getName());
            copyFile(srcFolder, newFile);
        }
    }

    public static void copyFile(File srcFile, File destFile) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
        byte[] bys = new byte[1024];
        int len;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
        bis.close();
        bos.close();
    }
}

在这里插入图片描述

在这里插入图片描述

判断数据源 File 是否是目录?
① 不是,说明是文件,直接复制,用字节流;
② 是,在目的地下创建和数据源 File 名称一样的目录,获取数据源 File 下所有文件或者目录的 File 数组,遍历该 File 数组,得到每一个 File 对象,最后把该 File 作为数据源 File 对象,递归调用复制文件夹的方法。

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栈老师不回家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值