由于字节流操作中文不是特别的方便,所以 Java 就提供了字符流。
字符流 = 字节流 + 编码表
用字节流复制文本文件时,文本文件也会有中文,但是不会出现问题,原因是最终底层操作会自动将复制到的内容进行字节拼接成中文,那么如何识别它是中文呢?
汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数。
基础知识:
① 计算机中存储的信息都是用二进制数表示的,我们在屏幕上看到的英文、汉字等字符都是二进制数转换之后的结果;
② 按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。按照 A 编码存储,就必须按照 A 编码解析,这样才能显示正确的文本符号,否则就会导致乱码;
③ 字符编码就是一套自然语言的字符与二进制数之间的对应规则(A,65);
④ 常见的字符集有 ASCLL 字符集、GBXXX字符集、Unicode 字符集等,其中 GBK 是最常用的中文码表,Unicode 字符集是为表达任意语言的任意字符而设计的,被称为统一码、标准万国码,最为常用的是 UTF-8 编码,互联网工程工作小组要求所有互联网协议都必须支持 UTF-8 编码
采用何种规则编码,就要采用对应的规则解码,否则就会出现乱码!
① 编码:
byte[] getBytes():使用平台默认的字符集将该 String 编码为一系列字节,将结果存储到新的字节数组中;
byte[] getBytes(String charsetName):使用指定的字符集将该 String 编码为一系列字节,将结果存储到新的字节数组中。
② 解码:
String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的 String;
String(byte[] bytes, String charsetName):通过指定的字符集解码指定的字节数组来构造新的 String。
//Test.javapackage 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();
用字符流写数据是有缓冲的,写入的内容在文件中一下显示不出来,需要用刷新流方法 flush() 实现刷新!
//Test.javapackage 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 代表的是所取字符的个数,并不是索引!
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();
字符流读数据和字节流读数据的格式是一样的!
需求:把模块目录下的某文件内容复制到模块目录下的另一文件中。
分析:
① 我们可以使用上面的 InputStreamReader 和 OutputStreamWriter 转换流进行操作,但转换流的名字太长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化书写,转换流为我们提供了对应的子类;
② FileReader(String fileName),用于读取字符文件的便捷类;
③ FileWriter(String fileName),用于写入字符文件的便捷类。
//Test.javapackage 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();}
}
如果不指定编码类型,我们完全可以使用其子类,简化代码!
字符缓冲流:
① BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小,默认值足够大,可用于大多数用途;
② BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符、数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小,默认值足够大,可用于大多数用途。
BufferedWriter bw = new BufferedWriter(new FileWriter("idea_test\\myCharStream.txt"));
String s = "我爱你中国!";
bw.write(s, 0, s.length());
bw.close();
① newLine():写一行行分隔符。行分隔符字符串由系统属性定义,该换行适用于任何系统;
② readLine():读一行文字。结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为 null。
//Test.javapackage 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!
//Test.javapackage 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();}
}
用字符缓冲流的特有功能复制文件,是我们最常用的一种方法!
需求:给出一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器。
思路:
① 创建字符缓冲输入流对象;
② 创建 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 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));}
}
需求:键盘录入 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;}@Overridepublic 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);}@Overridepublic 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 ts = new TreeSet<>(new Comparator() {@Overridepublic 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();}
}
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();}
}
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 对象,递归调用复制文件夹的方法。
上一篇:C语言的灵魂---指针(基础)
下一篇:Lock接口——JUC随记2