IO流

发布时间 2023-09-09 11:49:22作者: SimpleWord
title: IO流之基本流
index_img: img/25.svg
tags:
  - Java SE
  - 异常
  - IO流
categories:
  - Java SE
excerpt: IO流

IO流概述

IO流就是程序从外部或网络读取数据。

分类

根据流的方向:

根据操作的文件类型:

字节流

实现类速记:File+接口

本地字节输出流

  • 创建字节输出流对象
  • 写数据
  • 释放资源
public class Main {
    public static void main(String[] args) throws Exception {
        //【对于程序来说】写出一段数据到文本,使用输出流
        FileOutputStream out = new FileOutputStream("src\\main\\java\\bid\\simpleword\\io\\my.txt");
        String str = "Hello,java!\nmy name is simpleWord"; //要换行,只需要写入换行符即可,不想环也可以使用\\转义
        out.write(str.getBytes()); //注意write的参数
        out.close();
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        String path = "src\\main\\java\\bid\\simpleword\\io\\my.txt";
        FileOutputStream out = new FileOutputStream(path, true); //开启追加
        String str = "Hello,java!\nmy name is simpleWord";
        out.write(str.getBytes());
        out.close();
    }
}
  • FileOutputStream
    • 文件不存在,会创建文件【前提是父级目录有效】
    • 文件存在,构造方法默认会清空文件
      • 第二个参数修改为true可开启追加功能
  • write:支持ASCIIbyte字符byte数组byte数组的部分写入

本地字节输入流

  • 创建字节输入流对象
  • 写数据
  • 释放资源
public class Main {
    public static void main(String[] args) throws Exception {
        //【对于程序来说】读入一段数据到文本,使用输入流
        String path = "src\\main\\java\\bid\\simpleword\\io\\my.txt";
        FileInputStream in = new FileInputStream(path);
        byte[] bytes = in.readAllBytes();
        String str = new String(bytes); //通过String构造方法返回字符串形式
        System.out.println(str);
        in.close();
    }
}

//上一个指针到末尾了

public class Main {
    public static void main(String[] args) throws Exception {
        String path = "src\\main\\java\\bid\\simpleword\\io\\my.txt";
        FileInputStream in = new FileInputStream(path);
        int b;
        byte[] bs = new byte[1024];
        while ((b = in.read(bs)) != -1) {
            String s = new String(bs, 0, b); // 创建新字符串
            System.out.println(s);
        }
        in.close();
    }
}

//上一个指针到末尾了

public class Main {
    public static void main(String[] args) throws Exception {
        String path = "src\\main\\java\\bid\\simpleword\\io\\my.txt";
        FileInputStream in = new FileInputStream(path);
        int b;
        while ((b = in.read()) != -1) { //指针会后移,要存储读到的值
            System.out.print((char) b);
        }
        in.close();
    }
}
  • FileInputStream:文件不存在,直接报错
  • read方法每次读取一个字节【包括空】的ASCII指针后移,读到末尾会返回-1;
    • read支持一个一个读【搭配循环】
      • 太慢
      • 支持边读边写
    • read支持一次读取byte[k]长度或部分的数据【返回值是读取到的长度
      • 推荐,但数组不推荐设置的太大,一般是1024的倍数。
      • 支持边读编写,指针每次移动数组长度
    • 使用readALLBytes读取全部字节。【返回值是数组】
      • 全部读到内存,再一次性写入文件
      • 适用于小文件传输

文件拷贝

  • 输入流读入,输出流写出
public class Main {
    public static void main(String[] args) throws Exception {
        String path1 = "src\\main\\java\\bid\\simpleword\\io\\my.txt";
        String path2 = "src\\main\\java\\bid\\simpleword\\io\\you.txt";

        FileInputStream in = new FileInputStream(path1);
        FileOutputStream out = new FileOutputStream(path2);
        int b;
        byte[] bytes = new byte[1024 * 1024 * 5];
        while ((b = in.read(bytes)) != -1) {
            out.write(bytes, 0, b); //写入的长度是有效长度b
        }

        in.close();
        out.close();
    }
}

字符集

ASCII字符集

  • 最大128,1byte最大\(2^8=256\)\(2^7\)即可解决存储。
  • 只能存储英文且占1字节
  • 编码规则左边第一位用不到,补为0

GBK中文字符

  • 存储英文兼容ASCII:1字节
  • 存储中文2字节,最大\(2^16=65536\),足够使用
  • 编码规则:
    • 英文:左边第一位用不到,补为0
    • 中文:左边字节第一位一定是1【防止与英文混淆,看到1就知道还要读一个字节】

Unicode

UTF-8(Unicode Transfer Format),Unicode下的一种编码方式【一般默认使用此编码

  • 存储英文兼容ASCII:1字节
  • 存储中文:3字节
  • 编码规则:
    • 英文:左边第一位用不到,补为0
    • 中文:1110xxxx 10xxxxxx 10xxxxxx的格式【1110开头,3个字节

中文乱码

原因

  • 读取时字节不对,比如一个一个字节的

    • 如果我们只是拷贝等,只要数据不丢失,不会乱码
  • 编码和解码规则不一致

解决

  • 使用字符流取中文文本【可能包含中英文,不建议使用字节流
  • 编码和解码规则保持一致

编码和解码代码

方法 说明
public byte[ ] getBytes() 使用默认方式进行编码
public byte[] getBytes(string charsetName) 使用指定方式进行编码
string(byte[ ] bytes) 使用默认方式进行解码
string(byte[ ] bytes,string charsetName) 使用指定方式进行解码
public class Main {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "I am 张三";

        //编码
        int length = s.getBytes().length; //默认字符集:UTF-8
        System.out.println(length); //中文占3字节,共11字节

        int length1 = s.getBytes("GBK").length;
        System.out.println(length1); //中文占2字节,共9字节

        //解码
        String s1 = new String(s.getBytes()); //默认解码UTF-8
        System.out.println(s1.equals(s)); //解码结果是否一致

        String s2 = new String(s.getBytes("GBK"), "GBK"); //GBK
        System.out.println(s2.equals(s));
    }
}

image-20230810215629587

字符流

实现类速记:File+接口

简述

编码规则都默认UTF-8,我们只需要采用字符流读取中文相关文本即可。

特点:

  • 底层还是字符流

  • 一次读取一个字节,遇到中文,读多个字节【与字符集相关】,写入时按指定编码好后写入文件

  • 只能操作纯文本

本地字符输出流

参考字节流

public class Main {
    public static void main(String[] args) throws Exception {
        String path = "src\\main\\java\\bid\\simpleword\\io\\my.txt";
        FileWriter out = new FileWriter(path, true); //开启追加
        String s = "\n折戟沉沙铁未销,自将磨洗认前朝.";
        String s1 = "\n谈笑间樯橹灰飞烟灭.";
        out.write(s);
        out.write(s1.toCharArray());
        out.close();
    }
}

image-20230810224106991

本地字符输入流

参考字节流

public class Main {
    public static void main(String[] args) throws IOException {
        FileReader in = new FileReader("src\\main\\java\\bid\\simpleword\\io\\my.txt");
        int b;
        while ((b = in.read()) != -1) {
            System.out.print((char) b);
        }
        in.close();
    }
}
//上一个指针到末尾了

public class Main {
    public static void main(String[] args) throws IOException {
        FileReader in = new FileReader("src\\main\\java\\bid\\simpleword\\io\\my.txt");
        int b;
        char[] chars = new char[1024 * 1024];
        while ((b = in.read(chars)) != -1) {
            String s = new String(chars, 0, b);
            System.out.println(s);
        }
        in.close();
    }
}

image-20230810221650784

拷贝文件

还是推荐使用字节流

public class Main {
    public static void main(String[] args) throws Exception {
        String path1 = "src\\main\\java\\bid\\simpleword\\io\\my.txt";
        String path2 = "src\\main\\java\\bid\\simpleword\\io\\you.txt";

        FileReader in = new FileReader(path1);
        FileWriter out = new FileWriter(path2);

        int b;
        char[] chars = new char[1024];
        while ((b = in.read(chars)) != -1) {
            String s = new String(chars, 0, b);
            out.write(s);
        }

        in.close();
        out.close();
    }
}

IO流异常捕获

之前我们与IO相关的异常,层层上抛,最后交给JVM,如果我们自己要处理,但是关闭资源又执行不到,怎么处理?

try…catch..finally

try{
  
}catch(异常类型 变量){
  
}finally{
  //这里的代码一定执行,除非JVM停止
}
public class Main {
    public static void main(String[] args) {
        String path1 = "src\\main\\java\\bid\\simpleword\\io\\my.txt";
        String path2 = "src\\main\\java\\bid\\simpleword\\io\\you.txt";

        FileInputStream in = null;          //只定义【finally要使用】,因为创建有编译时异常,创建应该在try中
        FileOutputStream out = null;

        try {
            in = new FileInputStream(path1);  //这里可能建立不成功,也有异常,在close之前要处理
            out = new FileOutputStream(path2);
            int b;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((b = in.read(bytes)) != -1) {
                out.write(bytes, 0, b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {  //关闭也有异常,还要继续异常处理
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

简化方案

上方代码的复杂来自close

jdk9方案,自动关闭(特殊情况下)

public class Main {
    public static void main(String[] args) throws FileNotFoundException { //抛
        String path1 = "src\\main\\java\\bid\\simpleword\\io\\my.txt";
        String path2 = "src\\main\\java\\bid\\simpleword\\io\\you.txt";

        FileInputStream in = new FileInputStream(path1);
        FileOutputStream out = new FileOutputStream(path2);

        try (in; out) {
            int b;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((b = in.read(bytes)) != -1) {
                out.write(bytes, 0, b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}