• IO-字符流


    字符流

    说在前面:每个类的构造和方法查API即可

    原则:

    先创建的流后关闭,后创建的流先关闭,否则可能会报错

    为什么要有字符流?

    因为世界语言的多样性

    学会字符流有啥用?

    字符流更加便利在读字方面,字节流读出来的字节,不好进行处理,容易乱码。

    特点:

    • 所有的字符流,自带(小)缓冲区,是在编解码的时候来使用的
    • 字符流的read()方法,会一次多次读入一个或者字节(看编码的符号位)。存入int中。(也是字节流和字符流的根本区别)

    引例:

    从文件中按每次两个字节读出中文:

       public static void main(String[] args) throws IOException {
            FileInputStream inputStream = new FileInputStream("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt");
           // 123.txt里面是“爱我中国” 
           // 按每两个字节去读出
           byte[] bytes = new byte[2];
           // byte[] bytes = new byte[1024];
            int readCount;
            while ((readCount = inputStream.read(bytes)) != -1){
                String s = new String(bytes, 0, readCount);
                System.out.println(s);
            }
    
            inputStream.close();
       }
    
    // 按每两个字节去读出 输出为:������������ 一堆乱码。
    // 按1024个字节读出 输出为:爱我中国
    

    从文件中按每次两个字节读出,然后写入到另一个文件中:

    // 读入到另一个文件试试
    FileInputStream in = new FileInputStream("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt");
    FileOutputStream out = new FileOutputStream("E:\JavaProjectStation\JavaBaseHomeworkFile\copy123.txt");
    
    int readCount;
    byte[] bytes1 = new byte[2];
    while ((readCount = in.read(bytes1)) != -1){
        out.write(bytes1, 0, readCount);
    }
    
    in.close();
    out.close();
    
    // 两个文件内容一样
    // 那么问题来了 读出和写入 是否都采用了utf-8的编码解码方式?
    

    如何将字符串指定编码集,指定解码集 ?

    1. 解码:字符串用getBytes(编码格式)方法转化为一个字节数组
    2. 编码: 以字符数组创建字符串对象 new String(字符数组,编码格式)
    3. 输出字符串,或者其他操作

    例子:

    String str = "不要忘记你的目标";
    // 默认为utf-8解码 然后放入字节数组
    byte[] bytes = str.getBytes();
    
    // 编码码 指定为:以GBK编码方式编码码。
    String gbk = new String(bytes, "GBK");
    System.out.println(gbk);
    
    // 默认utf-8 GBK编码:正常输出
    // 指定GBK编码,乱码
    
    

    字符流 = 字节流 + 编码表,为什么要这么说?

    image-20210119122332018

    字符转化流:

    主要就是可以指定解码和编码格式,且也只有它能

            // OutputStreamWriter(OutputStream out)
            // 创建使用默认字符编码的 OutputStreamWriter。
            FileOutputStream fileOutputStream = new FileOutputStream("b.txt");
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
            //OutputStreamWriter(OutputStream out, String charsetName)
            // 创建使用指定字符集的 OutputStreamWriter。
            //OutputStreamWriter outputStreamWriter = new OutputStreamWriter(
            //        new FileOutputStream("b.txt"),"GBK");
    
            //String getEncoding()
            // 返回此流使用的字符编码的名称。
            String encoding = outputStreamWriter.getEncoding();
            System.out.println(encoding);
    

    字符简化流:

    输入:

    通过简化字符输入流对象将从文件读出数据并打印出来:

    // 新建一个字符输入流对象
    FileReader in = new FileReader("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt");
    // 注意:用字符数组来存储 同样也需要while循环
    char[] chars = new char[1024];
    int readCount;
    while ((readCount = in.read(chars)) != -1){
        System.out.println("读出内容为:" + new String(chars, 0, readCount));
    }
    in.close();
    

    输出:

    将字符串写入文件:

    File file = new File("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt");
    // 父类引用指向子类对象
    Writer out = new FileWriter(file);
    String str = "hello world";
    // 字符输出流 不用像之前字节输出流一样,用必须一个个字节输出,或者用字节数组输出
    // 直接写入字符串就行了
    out.write(str);
    out.close();
    

    将字符串追加写入文件

    File file = new File("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt");
    Writer out = new FileWriter(file, true);
    String str = "追加写入成功";
    out.write(str);
    out.close();
    

    通过简化流对象将文件中读出以后输入到另一个文件中去:

    // 新建一个字符输入输出流对象
    Reader in = new FileReader("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt");
    Writer out = new FileWriter("E:\JavaProjectStation\JavaBaseHomeworkFile\copy123.txt", true);
    // 注意:用字符数组来存储 同样也需要while循环
    char[] chars = new char[1024];
    int readCount;
    while ((readCount = in.read(chars)) != -1){
        out.write(new String(chars, 0, readCount));
    }
    
    // 关闭
    in.close();
    out.close();
    

    字符缓冲流

    输入:

    字符缓冲输入流操作步骤

    1. 创建简化输入流
    2. 创建缓冲输入流
    3. 循环读出数据,readline时候,读完文件返回null

    题:从磁盘上读取一个文本文件(如某个java源代码),分别统计出文件中英文字母

    /* 
    思路:
     * 1.创建缓冲字符输入流对象
     * 2.一个一个的读出来,读出来的得用int型接收
     * 3.if判断计数
     *
     * @modified By:
     */
    public class Homework1 {
        public static void main(String[] args) throws IOException {
            BufferedReader in = new BufferedReader(new FileReader("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt"));
    
            // 存储读出的数据
            int readData;
    
            // 记录各种类型字符的个数
            int countLetter  = 0; // 字母
            int countSpace = 0; // 空格
            int countNumber = 0;  // 数字
    
            while ((readData = in.read()) != -1){
    //            char readDataChar = (char) readData;
                if (Character.isLetter(readData)) {
                    countLetter++;
                }
                if (Character.isDigit(readData)) {
                    countNumber++;
                }
                if (Character.isWhitespace(readData)) {
                    countSpace++;
                }
            }
            System.out.println("该文件中数字有" + countNumber + "个	字母有" + countLetter + "个	空格有" + countSpace);
        }
    }
    

    输出:

    字符缓冲输出流操作步骤

    1. 创建简化输出流
    2. 再创建缓冲输出流
    3. write写入数据
    4. 关闭

    一道题看懂:在一个磁盘的文件里保存26个英文小写字母(乱序),将他们读入内存中,进行排序,把排好顺序的数再重新追加写到磁盘的该文件中。

    /*
     * 思路:
     * 1.创建字符缓冲输入流 输出流
     * 2.按字符数组读出
     * 3.char数组做个Arrays.sort排序 直接将char[]里面的字符按照ascll表排好。
     * 4.排完以后就写入。
     * @modified By:
     */
    public class Homework2 {
        public static void main(String[] args) throws IOException {
            BufferedReader in = new BufferedReader(
                    new FileReader("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt"));
    
            char[] chars = new char[1024];
            int readCount = in.read(chars);
            // Arrays.sort直接按照 字符的ascll码值排序完毕
            Arrays.sort(chars, 0, readCount);
    
            // 写入
            BufferedWriter out = new BufferedWriter(
                    new FileWriter("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt"));
            out.write(chars, 0, 26);
    
            in.close();
            out.close();
    
        }
    
    }
    

    注意:

    字符流在对图像操作的过程中

    图像,视频文件有自己的编码。

    各种流的对比:

    框架:

    image-20210119164157971

    各种流的优缺点对比

    简化流:

    1. 无法指定解码字符集。
    2. 使用简单,因为可以直接传入文件名作为构造参数

    转化流

    1. 使用麻烦,得新建一个字节流对象作为构造参数

    缓冲流

    1. 可以设置缓冲区大小

    2. 使用相对麻烦,得新建一个简化流对象作为构造参数,也可以用转化流

    3. 输入流有个readLine方法读出来返回一个字符串,结束返回null

    字节流和字符流的区别:

    1. 字符流read()方法一次读出一个,或者多个字节。

    一个困扰很深的问题:

    为什么两个字节char型数组能够存储3个字节的字符 比如中文

    答:utf-8解码后的汉字确实是三个字节,但是其中有8个符号位,当该数据存储形式从以字节数组转化为,字符数组时,符号位会被去掉,再拼接,组成两个字节存储于字符数组。

    补充知识:utf-8是怎么取

    UTF-8不同,它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容:
    它将Unicode编码为00000000-0000007F的字符,用单个字节来表示 0111 1111 = 7F
    它将Unicode编码为00000080-000007FF的字符用两个字节表示
    它将Unicode编码为00000800-0000FFFF的字符用3字节表示
    1字节 0xxxxxxx
    2字节 110xxxxx 10xxxxxx
    3字节 1110xxxx 10xxxxxx 10xxxxxx

    细枝末节:

    • 换行

      换行符

      • System.lineSeparator() :得到系统换行符
      • windos:" "
      • linux:" "
      • mac:" "
    • 字符流,有缓冲区的字节流都要close,或者刷新

    • utf-8变长 可以兼容ascll

    其他流

    数据流:

    DataIn/OutputStream

    创建形式:

    还是包装流,包装普通字节流

    为啥要有数据流:

    普通的字节流无法写入很大的整数,或者小数。

    数据输入流读出来的是啥玩意啊

    要知道文件中,当时写入数据类型的顺序,才能正确的读出,不讲武德的读出来当然有问题。

    打印流

    • 字节打印流 PrintStream

    • 字符打印流PrintWriter

    特点:

    • 也是一个包装流(包装字节流)
    • 可以打印各种类型的数据
    • 无法改变数据来源,只能操作目的地
    • 能够自动刷新

    字节打印流 PrintStream

    方法

    void print(String str): 输出任意类型的数据,

    void println(String str): 输出任意类型的数据,自动写入换行操作

    例子:

     PrintStream ps = new PrintStream(new FileOutputStream(("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt")));
    // 就啥玩意都可以打印到这个文件中去
            ps.print(100);
            ps.print("hello world");
    

    字节打印流 PrintStream

    PrintWrite

    构造方法摘要
    PrintWriter(OutputStream out, boolean autoFlush) 通过现有的 OutputStream 创建新的 PrintWriter。
    PrintWriter(Writer out, boolean autoFlush) 创建新 PrintWriter

    例子:

    PrintWriter pw = new PrintWriter(new FileWriter("E:\JavaProjectStation\JavaBaseHomeworkFile\123.txt"), true);
    pw.println("ni好啊");	// 自带刷新 
    

    自动刷新:

    有自动刷新,和手动刷新两种构造器

    自动刷新的触发:

    打印方式只有是println printf format 才能触发刷新

    标准输入输出流

    重定向

    输出重定向:

    实现步骤:

    1. 以打印输出流包装字节输出流
    2. System.setOut(打印流对象) 将标准输出重定向
    3. 随便打印一点什么
            try( PrintStream out = new PrintStream(
                    new FileOutputStream("E:\JavaProjectStation\JavaBaseHomeworkFile\Out.txt"));)
            {
                // 重定向输出到out.txt文件中
                System.setOut(out);
    
    
                // 向标准输出输出一个字符串
                System.out.println("重定向成功");
    
                // 向标准输出输出一个对象
                System.out.println(new ChangeOutDirection());
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
    
    

    输入重定向:

    实现步骤:

    1. 以普通字节输入流创建对象
    2. System.setIn(字节输入流对象)
    3. 新建Scanner对象
    4. Scanner.useDelimiter(分隔模式)
    5. sc.hasNext() 返回值是true 意思是接下来要扫描的一行不是末尾就返回true
    6. sc.next() 读出下一个分割符之间的内容
    try(
         FileInputStream fis =
                 new FileInputStream("E:\JavaProjectStation\JavaBaseHomeworkFile\src\Day20\PrintHelloWorld.java");
         )
    {
     // 重定向到fis输入流
     System.setIn(fis);
    
     //
     Scanner sc = new Scanner(System.in);
    
     // 增加下面一行只把回车作为分隔符  设置每次读取以什么为终点
     sc.useDelimiter("
    ");
    
     // sc只要遇到
     while (sc.hasNext()){
         System.out.println("键盘输入的内容时:" + sc.next());
     }
    } catch (FileNotFoundException e) {
     e.printStackTrace();
    } catch (IOException e) {
     e.printStackTrace();
    }
    

    有啥用:

    将某个操作的数据,输入到日志文件中保存

    序列化与反序列化流

    输出对象的啥玩意?

    对象信息,普通人看起来就是乱码。我看起来就是乱码。

    为啥要有序列化?

    实例对象的信息需要永久保存的时候,需要序列化,将信息存入磁盘。

    序列化操作步骤:

    1. 创建字节流对象
    2. 包装字节流对象创建ObjectOutputStream对象
    3. 调用.writeObject()方法写入
    4. 关闭ObjectOutputStream对象
    5. 注意 写入的对象,必须实现一个接口Serializable
    // 序列化
            try(
                    ObjectOutputStream SerOut = new ObjectOutputStream(
                            new FileOutputStream("E:\JavaProjectStation\JavaBaseHomeworkFile\Person.tmp"))
                    )
            {
                // 注意这里序列化的时候transient int ID是赋予了初值的
                SerOut.writeObject(new Person(18, "熊孩子", true, 11));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
    

    反序列化步骤:

    1. 创建字节流对象(该文件应该存储了一个类的信息)
    2. 包装字节流对象创建ObjectInputStream对象
    3. 调用readObject方法反序列化,读出该对象的信息
    4. 输出
     // 反序列化
            try(
                    ObjectInputStream deserIn = new ObjectInputStream(new FileInputStream("E:\JavaProjectStation\JavaBaseHomeworkFile\Person.tmp"));
    
            )
            {
                // 将反序列化生成的对象输出
                // 反序列化出来发现transient int ID; 变为了默认值 0;
                System.out.println(deserIn.readObject());
    
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
    

    transient关键字

    • 类的声明中用该关键字修饰的成员,不加入序列化。
    • 静态修饰的成员也不会序列化
    • 对象的信息被反序列化后的内容为默认值。

    学了这么多流,实际开发到底怎么用的?

    IO概括:

     文件的读写 ,在java中是非常常用。

     而设计者设计读写控制的时候,也整理的颇为详细,可能的结果就是,给调用者带来很大的困惑,这么多类,咋用。

     其实完全不用担心:java写文件只有三步,百变不离其宗

        1:我找到三样东西: 铲子(IO工具类); 沙子(我要读取的数据); 篓子(我要放的东西)

        2:用铲子把沙子放到篓子里

        3:我把铲子还给人家

    至于我用 何种铲子,我要铲的是沙子 还是面粉,我要放到篓子还是框子里。先别管,也别上来就看那个javaIIO的类示意图   

    按铲子区分:一般单一的铲子不适用,需要组合多种功能

      java中有读写字符的(reader/writer) 读写字节的(inputstream,outputstream)。自由选择

      java按照功能 还会有 filereader xmlreder imgio buffereader 等

    按照沙子来看:

      字符串,byte[] , List,数据库查询的结果集

    按照篓子来看

      可以放到Map String 图片 文件 xml

    补充学习:

    https://www.cnblogs.com/yyy-blog/p/7003693.html

    talk is cheap,write and optimize my code。
  • 相关阅读:
    C语言字符串之无重复字符的最长子串
    C语言递归之求根到叶节点数字之和
    C语言递归之二叉树的最大深度
    C语言递归之翻转二叉树
    C语言递归之对称二叉树
    C语言链表之两数相加
    如何把笔记本电脑的有线网分享给手机上
    安利spacemacs ^^
    lambda创世纪
    jinterface包详解
  • 原文地址:https://www.cnblogs.com/xiongzk/p/14304026.html
Copyright © 2020-2023  润新知