一、字节流

FileInputStream/FileOutputStream

顶级父类:inputStream、OutputStream

  1. public class Demo03 {
  2. public static void main(String[] args) {
  3. File sourceFile = new File("03-IOTest" + File.separator + "test.jpg");
  4. File targetFile = new File("03-IOTest" + File.separator + "test2.jpg");
  5. try (FileInputStream fis = new FileInputStream(sourceFile)) {
  6. try (FileOutputStream fos = new FileOutputStream(targetFile)) {
  7. int len;
  8. byte[] b = new byte[1024];
  9. // 每次读取b个长度的数据到数组中,当fis读到某尾时 返回-1
  10. while (((len = fis.read(b)) != -1)) {
  11. System.out.println(new String(b, 0, len));
  12. fos.write(b, 0, len);
  13. }
  14. }
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }

允许追加
public FileOutputStream(File file, boolean append) : 创建文件输出流以写入由指定的 File对象表示的 文件。

二、字符流

字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

FileReader

顶级父类:Reader、Writer

  1. public class Demo01 {
  2. public static void main(String[] args) {
  3. File file = new File("03-IOTest" + File.separator + "2.txt");
  4. try (FileReader fileReader = new FileReader(file)) {
  5. int b;
  6. // read 方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回 -1 ,循环读 取,代码使用演示:
  7. while (((b = fileReader.read()) != -1)) {
  8. System.out.println((char) b);
  9. }
  10. } catch (Exception e) {
  11. e.printStackTrace();
  12. }
  13. }
  14. }
  15. public class Demo02 {
  16. public static void main(String[] args) {
  17. File file = new File("03-IOTest" + File.separator + "2.txt");
  18. try (FileReader fileReader = new FileReader(file)) {
  19. int len;
  20. char[] cbuf = new char[2];
  21. // read(char[] cbuf) ,每次读取b的长度个字符到数组中,返回读取到的有效字符个数, 读取到末尾时,返回 -1
  22. while (((len = fileReader.read(cbuf)) != -1)) {
  23. System.out.println(new String(cbuf,0,len));
  24. }
  25. } catch (Exception e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. }

FileWrite

  1. public class Demo01 {
  2. public static void main(String[] args) {
  3. File file = new File("03-IOTest" + File.separator + "6.txt");
  4. try (FileWriter fw = new FileWriter(file)) {
  5. fw.write(97);
  6. fw.write('b');
  7. fw.write('c');
  8. fw.write(30000);
  9. // 刷新缓冲区,流对象可以继续使用。
  10. fw.flush();
  11. // 继续写出第2个字符,写出成功
  12. fw.write("开");
  13. fw.write("关");
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }
  1. public class Demo03 {
  2. public static void main(String[] args) throws IOException {
  3. File file = new File("03-IOTest" + File.separator + "2.txt");
  4. // 使用文件名称创建流对象
  5. FileWriter fw = new FileWriter(file);
  6. // 字符串
  7. String msg = "江苏丹阳";
  8. // 写出字符数组
  9. fw.write(msg); //江苏丹阳
  10. // 写出从索引2开始,2个字节。索引2是'丹',两个字节,也就是'丹阳'。
  11. fw.write(msg,2,2); // 丹阳
  12. // 关闭资源
  13. fw.close();
  14. }
  15. }

三、Properties

  1. public class ProDemo {
  2. public static void main(String[] args) throws FileNotFoundException {
  3. // 创建属性集对象
  4. Properties properties = new Properties();
  5. // 添加键值对元素
  6. properties.setProperty("filename", "a.txt");
  7. properties.setProperty("length", "209385038");
  8. properties.setProperty("location", "a.txt");
  9. // 打印属性集对象
  10. System.out.println(properties);
  11. // 通过键,获取属性值 System.out.println(properties.getProperty("filename")); System.out.println(properties.getProperty("length")); System.out.println(properties.getProperty("location"));
  12. Set<String> strings = properties.stringPropertyNames(); // 打印键值对
  13. for (String key : strings) {
  14. System.out.println(key + " ‐‐ " + properties.getProperty(key));
  15. }
  16. }
  17. }
  1. public class ProDemo2 {
  2. public static void main(String[] args) throws IOException {
  3. // 创建属性集对象
  4. Properties pro = new Properties();
  5. // 加载文本中信息到属性集
  6. pro.load(new FileInputStream("read.txt"));
  7. // 遍历集合并打印
  8. Set<String> strings = pro.stringPropertyNames();
  9. for (String key : strings) {
  10. System.out.println(key + " ‐‐ " + pro.getProperty(key));
  11. }
  12. }
  13. }

四、缓冲流

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。 缓冲流,也叫高效流,是对4个基本的FileXxx 流的增强,所以也是4个流,按照数据类型分类:

  • 字节缓冲流:BufferedInputStream,BufferedOutputStream
  • 字符缓冲流:BufferedReader,BufferedWriter

1、字节缓冲流

BufferedInputStream/BufferedOutputStream

缓冲流读写方法与基本的流是一致的,我们通过复制大文件(375MB),测试它的效率。

  1. 基本流,代码如下: ```java public class BufferedDemo { public static void main(String[] args) throws FileNotFoundException {
    1. // 记录开始时间
    2. long start = System.currentTimeMillis();
    3. // 创建流对象
    4. try (
    5. FileInputStream fis = new FileInputStream("jdk9.exe");
    6. FileOutputStream fos = new FileOutputStream("copy.exe")
    7. ){
    8. // 读写数据
    9. int b;
    10. while ((b = fis.read()) != -1) {
    11. fos.write(b);
    12. }
    13. } catch (IOException e) {
    14. e.printStackTrace();
    15. }
    16. // 记录结束时间
    17. long end = System.currentTimeMillis();
    18. System.out.println("普通流复制时间:"+(end - start)+" 毫秒");
    } }

十几分钟过去了…

  1. 2. 缓冲流,代码如下:
  2. ```java
  3. public class BufferedDemo {
  4. public static void main(String[] args) throws FileNotFoundException {
  5. // 记录开始时间
  6. long start = System.currentTimeMillis();
  7. // 创建流对象
  8. try (
  9. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));
  10. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));
  11. ){
  12. // 读写数据
  13. int b;
  14. while ((b = bis.read()) != -1) {
  15. bos.write(b);
  16. }
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. // 记录结束时间
  21. long end = System.currentTimeMillis();
  22. System.out.println("缓冲流复制时间:"+(end - start)+" 毫秒");
  23. }
  24. }
  25. 缓冲流复制时间:8016 毫秒

如何更快呢?
使用数组的方式,代码如下:

  1. public class BufferedDemo {
  2. public static void main(String[] args) throws FileNotFoundException {
  3. // 记录开始时间
  4. long start = System.currentTimeMillis();
  5. // 创建流对象
  6. try (
  7. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));
  8. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));
  9. ){
  10. // 读写数据
  11. int len;
  12. byte[] bytes = new byte[8*1024];
  13. while ((len = bis.read(bytes)) != -1) {
  14. bos.write(bytes, 0 , len);
  15. }
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. // 记录结束时间
  20. long end = System.currentTimeMillis();
  21. System.out.println("缓冲流使用数组复制时间:"+(end - start)+" 毫秒");
  22. }
  23. }
  24. 缓冲流使用数组复制时间:666 毫秒

2、字符缓冲流

BufferReader

  1. public static void main(String[] args) throws IOException {
  2. File sourceFile = new File("03-IOTest" + File.separator + "5.txt");
  3. BufferedReader br = new BufferedReader(new FileReader(sourceFile));
  4. String line;
  5. while ((line = br.readLine()) != null) {
  6. System.out.println(line);
  7. }
  8. br.close();
  9. }

BufferWriter

  1. public class BufferedWriterDemo throws IOException {
  2. public static void main(String[] args) throws IOException {
  3. // 创建流对象
  4. BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
  5. // 写出数据
  6. bw.write("king");
  7. // 写出换行
  8. bw.newLine();
  9. bw.write("supking");
  10. bw.newLine();
  11. bw.write("11");
  12. bw.newLine();
  13. // 释放资源
  14. bw.close();
  15. }
  16. }
  17. 输出效果:
  18. king
  19. supking
  20. 11
  1. /**
  2. 将以下文本排序
  3. 3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
  4. 8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
  5. 4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
  6. 2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
  7. 1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
  8. 9.今当远离,临表涕零,不知所言。
  9. 6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
  10. 7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
  11. 5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
  12. */
  13. // 复制并排序,使用hashmap自动排序
  14. public class Test01 {
  15. public static void main(String[] args) throws IOException {
  16. FileReader fileReader = new FileReader("03-IOTest" + File.separator + "test01.txt");
  17. FileWriter fileWriter = new FileWriter("03-IOTest" + File.separator + "test02.txt");
  18. BufferedReader bufferedReader = new BufferedReader(fileReader);
  19. BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
  20. Map<String, String> map = new HashMap<>();
  21. String line;
  22. while ((line = bufferedReader.readLine()) != null) {
  23. String[] split = line.split("\\.");
  24. map.put(split[0], split[1]);
  25. }
  26. for (Map.Entry<String, String> mapEntry : map.entrySet()) {
  27. bufferedWriter.write(mapEntry.getKey() + "." + mapEntry.getValue());
  28. bufferedWriter.newLine();
  29. }
  30. bufferedReader.close();
  31. bufferedWriter.close();
  32. }
  33. }

五、转换流

计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。 编码:字符(能看懂的)—>字节(看不懂的) 解码:字节(看不懂的)—>字符(能看懂的) 是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。

image.png

  • 字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。

计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。
image.png

1、编码引出的问题

在IDEA中,使用FileReader 读取项目中的文本文件。由于IDEA的设置,都是默认的UTF-8编码,所以没有任何问题。但是,当读取Windows系统中创建的文本文件时,由于Windows系统的默认是GBK编码,就会出现乱码。

  1. public class ReaderDemo {
  2. public static void main(String[] args) throws IOException {
  3. FileReader fileReader = new FileReader("E:\\File_GBK.txt");
  4. int read;
  5. while ((read = fileReader.read()) != -1) {
  6. System.out.print((char)read);
  7. }
  8. fileReader.close();
  9. }
  10. }
  11. 输出结果:
  12. ���

那么如何读取GBK编码的文件呢? 如下

2、InputStreamReader类

转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。

  • InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。
  • InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。

    1. public class ReaderDemo2 {
    2. public static void main(String[] args) throws IOException {
    3. // 定义文件路径,文件为gbk编码
    4. String FileName = "E:\\file_gbk.txt";
    5. // 创建流对象,默认UTF8编码
    6. InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
    7. // 创建流对象,指定GBK编码
    8. InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");
    9. // 定义变量,保存字符
    10. int read;
    11. // 使用默认编码字符流读取,乱码
    12. while ((read = isr.read()) != -1) {
    13. System.out.print((char)read); // ��Һ�
    14. }
    15. isr.close();
    16. // 使用指定编码字符流读取,正常解析
    17. while ((read = isr2.read()) != -1) {
    18. System.out.print((char)read);// 大家好
    19. }
    20. isr2.close();
    21. }
    22. }

    3、OutputStreamWriter

    转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

  • OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。

  • OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。

    1. public class OutputDemo {
    2. public static void main(String[] args) throws IOException {
    3. // 定义文件路径
    4. String FileName = "E:\\out.txt";
    5. // 创建流对象,默认UTF8编码
    6. OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
    7. // 写出数据
    8. osw.write("你好"); // 保存为6个字节
    9. osw.close();
    10. // 定义文件路径
    11. String FileName2 = "E:\\out2.txt";
    12. // 创建流对象,指定GBK编码
    13. OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
    14. // 写出数据
    15. osw2.write("你好");// 保存为4个字节
    16. osw2.close();
    17. }
    18. }

    4、转化文件编码

    1. public class TransDemo {
    2. public static void main(String[] args) {
    3. // 1.定义文件路径
    4. String srcFile = "file_gbk.txt";
    5. String destFile = "file_utf8.txt";
    6. // 2.创建流对象
    7. // 2.1 转换输入流,指定GBK编码
    8. InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile) , "GBK");
    9. // 2.2 转换输出流,默认utf8编码
    10. OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile));
    11. // 3.读写数据
    12. // 3.1 定义数组
    13. char[] cbuf = new char[1024];
    14. // 3.2 定义长度
    15. int len;
    16. // 3.3 循环读取
    17. while ((len = isr.read(cbuf))!=-1) {
    18. // 循环写出
    19. osw.write(cbuf,0,len);
    20. }
    21. // 4.释放资源
    22. osw.close();
    23. isr.close();
    24. }
    25. }