文章目录
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 对象,递归调用复制文件夹的方法。