IO.2(须知少时凌云志,曾许人间第一流)

字符流    
    既然字节流可以操作所有的文件,为什么还有字符流呢
        如果利用字节流,把文本中的中文,读取到内存中,有可能出现乱码
        如果利用字节流,把中文写到文本文件中,也有可能出现乱码
        如果是英文 可以打印出来,中文就会出现乱码
        如果字节流写入到文本中也会出现乱码
    基础知识:
        计算机存储方式的信息是用二进制数表示的
        按照某种规则,将字符变成二进制,再存储到计算机中,称为编码
        按照同样的规则,将存储在计算机中的二进制数解析显示出来,称为解码
        编码和节码的方式必须一致,否之会导致乱码
    简单理解:
            存储一个字符a,首先再码表中查到对应的数字是97,然后转成二进制进行存储
            读取的时候,先把二进制解析出来,再转成97,通过97查找到对应的字符应该是a
    编码表简单理解:
            ASCII字符集:
                ASCII(American Standard Code for Information Interchanae,美国信息交换标准代码):包括了数字,大小写字符和一些常见的标点符号
                注意:ASCII码表是没有中文的
            GBK:
                window系统默认的码表。兼容了ASCII码表,也包含了21003个汉字,并支持繁体汉字以及部分日韩文字
                注意:GBK是中国的码表,一个中文以两个字节的形式存储,但不包含世界上所有国家的文字
            Unicode码表:
                由国际组织ISO定制,是统一的万国码,计算机科学领域里的一项业界标准,容纳世界上大多数国家所有常见的文字和符号
                但是因为表示的字符太多,所以Unicode码表中的数字不是直接以二进制的形式存储到计算机的
                会先通过UTF-7,UTF-7.5,UTF-8,UTF-16,以及UTF-32进行编码,再存储到计算机,其中最为常见的UTF-8
                注意:Unicode是万国码,以UTF-8编码后一个中文以三个字节的形式存储
            
            重点:windows默认使用的码表为:GBK,一个字符两个字节
                idea和其他工具默认使用Unicode的UTF-8的编码格式,一个中文三个字节
            字节串中的编码节码问题
                编码:
                    byte[] getBytes():使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
                    byte[] getBytes(String charsetName):使用用户指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
                解码:
                    String(byte[] bytes):通过使用平台默认的字符集解码指定的字节数组来构造新的String
                    String(byte[] bytes,String charseName):通过指定的字符集节码指定的字节数组来构造新的String
        代码:

public class test15 {
            public static void main(String[] args) throws UnsupportedEncodingException {
        //        编码:
        //        byte[] getBytes():使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
        //        byte[] getBytes(String charsetName):使用用户指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
        //
                String s="我以后一定会称为高级开发";
                //利用idea默认的UTF-8将中文编码为一系列的字节
                byte[] bytes=s.getBytes();
                System.out.println(Arrays.toString(bytes));
                //[-26, -120, -111, -28, -69, -91, -27, -112, -114, -28, -72, -128, -27, -82, -102, -28, -68, -102, -25, -89, -80, -28, -72,
                // -70, -23, -85, -104, -25, -70, -89, -27, -68, -128, -27, -113, -111]
                byte[] bytes1 = s.getBytes("GBK");
                System.out.println(Arrays.toString(bytes1));
                //[-50, -46, -46, -44, -70, -13, -46, -69, -74, -88, -69, -31, -77, -58, -50, -86, -72, -33, -68, -74, -65, -86, -73, -94]
        
        //        解码:
                String(byte[] bytes):通过使用平台默认的字符集解码指定的字节数组来构造新的String
                String(byte[] bytes,String charseName):通过指定的字符集节码指定的字节数组来构造新的String
                byte[] bytes2={-26, -120, -111, -28, -69, -91, -27, -112, -114, -28, -72, -128, -27, -82, -102, -28, -68, -102, -25, -89, -80, -28, -72, -70, -23, -85, -104, -25, -70, -89, -27, -68, -128, -27, -113, -111};
                byte[] bytes3={-50, -46, -46, -44, -70, -13, -46, -69, -74, -88, -69, -31, -77, -58, -50, -86, -72, -33, -68, -74, -65, -86, -73, -94};
                //默认使用UTF-8进行解析
                String s1=new String(bytes2);
                System.out.println(s1);//我以后一定会称为高级开发
                //利用只当GBK进行解码
                String s2=new String(bytes3,"GBK");
                System.out.println(s2);//我以后一定会称为高级开发
            }
        }


                
    字节流读取文本文件出现乱码的原因
        因为字节流一次读一个字节,而不管是GBK还是UTF-8一个中文都是多个字符的,而字节流每次只能读取其中一部分,所以就会出现乱码问题
    字符流读取中文的过程
        字符流=字节流+编码表
    基础知识:
        不管再那张码表中,中文的第一个字节一定是负数
    
小结:
    1.想要进行拷贝,一律使用字节流或者字节缓冲流
    2.想要把文本文件中的数据读取到内存中,请使用字符输入流
      想要把内存中的数据写道文本文件中,请使用字符输出流
     3.GBK码表一个中文两个字节,UTF-8编码格式一个中文三个字节
字符流写数据:
    步骤:1.创建字符输出流对象
          2.写数据
          3.释放资源
    字符流写数据的5中方式
        void write(int c)     写一个字符
        void write(char[] cbuf)    写一个字符数组
        void write(char[] cbuf,int off,int len)   写出字符数组的一部分
        void write(String str)    写一个字符串
        void write(String str,int off,int len)    写一个字符串的一部分
代码:        
  

 public class test16 {
            public static void main(String[] args) throws IOException {
        //        步骤:1.创建字符输出流对象
        //        2.写数据
        //        3.释放资源
        //                字符流写数据的5中方式
        //        void write(int c)     写一个字符
        
                //字符流的底层就是字节流
                //下边两种创建方式都可以
                //FileWriter fw = new FileWriter(new File("fileIo\\a.txt"));
                FileWriter fw= new FileWriter("fileIo\\a.txt");
                //写出数据
                fw.write(98);
                fw.write(97);
                fw.write(99);
                //释放资源
                fw.close();
                //        void write(char[] cbuf)    写一个字符数组
                char[] chars={97,98,99};
                fw.write(chars);
                //        void write(char[] cbuf,int off,int len)   写出字符数组的一部分
                char[] chars1={97,98,99,100};
                fw.write(chars1,0,2);//从0所以开始写入三个
                //        void write(String str)    写一个字符串
                String s="我很厉害No1";
                fw.write(s);
                //        void write(String str,int off,int len)    写一个字符串的一部分
                fw.write(s,0,2);
                //每次运行完  切记要关掉流  不然你内存多少G都不够   这坑踩过
            }
        }


    字符流写数据 注意事项 
          1.创建字符输出流对象
            如果文件不存在,就创建,但是要保证父级路径存在
            如果文件存在就清空
          2.写数据
            写出int类型的整数,实际写出的是整数再码表上对应的字母
            写出字符串数据,是把字符串本身原样写出
          3.释放资源    
            每次释放必须关闭,不然电脑内存会直接溢出,直至没有空间继续写
    flush和close方法
            flush()  刷新流,还可以继续写数据
            close()  关闭流,释放资源,但是在关闭之前会先刷新流,一旦关闭,就不能再写数据
    代码:
        

public class test17 {
            public static void main(String[] args) throws IOException {
        //        flush()  刷新流,还可以继续写数据
        //        close()  关闭流,释放资源,但是在关闭之前会先刷新流,一旦关闭,就不能再写数据
                FileWriter fw = new FileWriter("test//a.txt");
                fw.write("阿星在努力");//这个时候运行并不会打印数据
            // fw.flush();  //
                fw.write("666");
                //fw.flush();
        
                fw.close();
                fw.write("aaa");//Stream close
        
            }
        }


    字符流读的两种方式
    单个字符读取:
        

public class test18 {
            public static void main(String[] args) throws IOException {
                //创建字符输入流的对象
                //底层是字节流+编码表
            // FileReader fr = new FileReader(new File("test\\a.txt"));
                FileReader fr = new FileReader("fileIo\\a.txt");
                //读取数据  一次读取一个字符
                int ch;
                while ((ch=fr.read())!=-1){
                    System.out.println((char) ch);
                }
                //释放资源
                fr.close();
            }
        }


    多个字符读取:
        

public class test19 {
            public static void main(String[] args) throws IOException {
                //一次读取多个字符
                //创建对象
                FileReader fr = new FileReader("fileIo\\a.txt");
                //创建一个数组
                char[] chars = new char[1024];
                int len;
                //read 读取  一次读取多个字符
                //他把读到的字符都存入到了chars数组
                //返回值:表示本次读到了多少个字符
                while ((len=fr.read())!=-1){
                    System.out.println(new String(chars,0,len));
                }
                fr.close();
            }
        }


    写一个练习  
            需求:用户输入的用户名和密码保存到本地实现永久化存储  用户名占一行,密码占一行
        步骤:1.写一个键盘录入用户名和密码
              2.将用户名和密码写到本地文件中
        代码:
    

public class test20 {
            public static void main(String[] args) throws Exception {
                //1.键盘录入用户名和密码
                Scanner sc=new Scanner(System.in);
                System.out.println("请录入用户名");
                String username = sc.next();
                System.out.println("请录入密码");
                String password = sc.next();
                //2.分别把用户名和密码写到本地文件
                FileWriter fw = new FileWriter("filelo\\a.txt");
                //将用户名和密码写到文件中
                fw.write(username);
                //写出一个回车换行符
                fw.write("\r\n");
                fw.write(password);
                //刷新流
                fw.flush();
                //释放资源
                fw.close();
            }
        }


    字符缓冲流 
        BufferedWriter:可以将数据高效的写出
        BufferedReader:可以将数据高效的读取到内存
    代码实例:
        

public class test21 {
            public static void main(String[] args) throws IOException {
                //字符缓冲输入流
                BufferedReader br=new BufferedReader(new FileReader("filelo\\a.txt"));
                //读取数据
                char[] chars=new char[1024];
                int len;
                while ((len=br.read())!=-1){
                    System.out.println(new String(chars,0,len));
                }
                br.close();
            }
        }    


    字符缓冲输出流
        BufferedWrited:可以将数据高效的写出
        BufferedReader:可以将数据高效的读取到内存
    构造方法:
        BufferedWrited(Writer out);
        BufferedReader(Reader in);
    代码:
        

public class test22 {
            public static void main(String[] args) throws Exception {
                //字符缓冲输出流
                BufferedWriter bw=new BufferedWriter(new FileWriter("filelo\\a.txt"));
                //写出数据
                bw.write(98);
                char[]  chars=new char[1024];
                bw.write(chars);
                bw.write(chars,0,3);
                bw.write("有梦终会达");
                String s="adsafsa";
                bw.write(s,0,3);
                bw.flush();
                bw.close();
            }
        }


    字符缓冲流特有功能
        BufferedWrited:
            void newLine(): 写一行行分隔符,行分隔符字符串由系统属性定义
        BufferedReader:    
            public StringReadLine():读一行文字,结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾以及到达,则为null
    代码实例:
      

 public class test23 {
            public static void main(String[] args) throws Exception {
                //创建对象
                BufferedWriter bw = new BufferedWriter(new FileWriter("filelo\\a.txt"));
                //写出数据
                bw.write("在黑夜里发光");
                //跨平台的回车换行
                bw.newLine();
                bw.write("asdasd");
                bw.newLine();
                bw.write("153123");
                //刷新流 释放资源
                bw.flush();
                bw.close();
            }
        }


    代码实例:
        public class test24 {
            public static void main(String[] args) throws Exception {
                //创建对象
                BufferedReader br=new BufferedReader(new FileReader("filelo\\a.txt"));
                //读取数据
                //在之前,读不到数据返回就是-1,显示现在readLine方法读不到数据就返回null
                String line = br.readLine();
                String line1 = br.readLine();
                String line2 = br.readLine();
                System.out.println(line);
                System.out.println(line1);
                System.out.println(line2);//如果这一行没有数据  就在控制台输出null
                //释放资源
                br.close();
            }
        }
    用循环做一个改进        

public class test25 {
    public static void main(String[] args)throws Exception {
            //创建对象
            BufferedReader br=new BufferedReader(new FileReader("filelo\\a.txt"));
            //读取数据
            //在之前,读不到数据返回就是-1,显示现在readLine方法读不到数据就返回null
    
        String line;
        //可以读取一整行数据,一直都,读到回车换行为止
        //但是他不会读取回车换行符
        while ((line=br.readLine())!=null){
            System.out.println(line);
        }
            //释放资源
            br.close();
    }
    }


    案例:读取文件中的数据排序后再次写到本地  文件中的数据 9 8 2 4 3 1 5 3
    1.读取数据
    2.排序
    3.写到本地
    代码:
    

public class test26 {
            public static void main(String[] args) throws Exception {
        //        1.读取数据
                BufferedReader br=new BufferedReader(new FileReader("filelo\\a.txt"));
                这个不能提前创建  如果a.txt 有东西  会直接清空 不能写道BufferedReader定义的下一行
                //BufferedWriter bw=new BufferedWriter(new FileWriter("filelo\\a.txt"));
                String line=br.readLine();
                System.out.println("读取到的数据为"+line);
                br.close();
        //        2.排序
                //按照空格进行切割
                String[] s = line.split(" ");
                //把字符串类型的数组变成int类型
                int[] arr=new int[s.length];
                //遍历s数组,可以进行类型转换
                for (int i = 0; i < s.length; i++) {
                String smallStr= s[i];
                    int number = Integer.parseInt(smallStr);
                    //把转换后的结果存到arr中
                    arr[i] =number;
                }
                Arrays.sort(arr);
                System.out.println(Arrays.toString(arr));
        //        3.写到本地
            BufferedWriter bw=new BufferedWriter(new FileWriter("filelo\\a.txt"));
                for (int i = 0; i < arr.length; i++) {
                    bw.write(arr[i]+" ");
                    bw.flush();
                }
                bw.close();
            }
        }

I
    转换流:
        转换输入流:InputStreamReader
        转换输出流:OutputStreamWriter
    转换流的使用场景
            JDK11之前,指定编码读写
    之前的方法会产生乱码
    

 

 

public class test27 {
    public static void main(String[] args) throws IOException {
        //这个方法直接读取会产生乱码
        //因为文件时GBK码表
        //而IDEA 默认时UTF-8  所以两者不一致  就会出现乱码
        FileReader fr = new FileReader("D:\\a.txt");
        int ch;
        while ((ch=fr.read())!=-1){
            System.out.println((char) ch);
        }
        fr.close();
        
    }
}


转换流的实例:

public class test27 {
    public static void main(String[] args) throws IOException {
        //第二个参数 大小写都可以 指定编码读
        InputStreamReader isr = new InputStreamReader(new FileInputStream("D\\a.txt"), "GBK");
        int ch;
       while ((ch=isr.read())!=-1){
           System.out.println((char) ch);
       }
       isr.close();

        OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("D\\a.txt"),"GBK");
        osw.write("我爱学习");
        osw.close();

        //JDK11之后,字符流新推出了一个构造,也可以指定编码表
        FileReader fr = new FileReader("D\\a.txt", Charset.forName("gbk"));
        int ch1;
        while ((ch1=isr.read())!=-1){
            System.out.println((char) ch1);
        }
    }
}


对象操作流 
        特点:可以把对象以字节的形式写道本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中
    对象操作流分为两类:对象操作输入流和对象操作输出流
    对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者网络中传输对象
    对象操作输入流(对象反序列化流):把写到本地文件中的对象读取到内存中,或者接收网络中传输的对象


序列化代码  
    User类:            public class User implements Serializable {
    //如果这个类的对象能被序列化,那么这个类必须要实现一个Serializable接口
    //Serializable  称之为一个标记性接口,里面没有任何抽象方法  只要一个类实现了这个接口,那么就表示这个类的对象可以被序列化
   

 

 private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}


测试类代码:

public class test28 {
    public static void main(String[] args) throws Exception {
        User user=new User("zhangsan","abc");
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("a.txt"));
        oos.writeObject(user);
        oos.close();
    }
}


对象反序列化

public class test29 {
    public static void main(String[] args) throws Exception {
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("a.txt"));
        User o = (User) ois.readObject();
        System.out.println(o);
        ois.close();
    }
}


注意点:用对象序列化一个对象后,假如我们修改了对象所属的Javabean类,读取会出现问题
        SerialVersionUID 序列化 
        如果我们自己没有定义,那么虚拟机会根据类中的信息自动计算出一个序列号
        如果我们修改了类中的信息,虚拟机就会再次计算出一个序列号
        第一步  把User对象序列化到本地  虚拟机会创建一个序列号
        第二步  修改了javabean类  导致虚拟机重新创建了一个序列号,两者不一致
        第三步 把文件中的对象读到内存,本地中的序列号和类中的序列号就不一致了
        解决 
        不让虚拟机帮我们计算,我们手动给出,而且这个值不变
        
        在User类中加入  private  static final long serialVerSionUID =1L;
        

public class User implements Serializable {
    //如果这个类的对象能被序列化,那么这个类必须要实现一个Serializable接口
    //Serializable  称之为一个标记性接口,里面没有任何抽象方法  只要一个类实现了这个接口,那么就表示这个类的对象可以被序列化

//    SerialVersionUID 序列化
//    如果我们自己没有定义,那么虚拟机会根据类中的信息自动计算出一个序列号
//    如果我们修改了类中的信息,虚拟机就会再次计算出一个序列号
//    第一步  把User对象序列化到本地  虚拟机会创建一个序列号
//    第二步  修改了javabean类  导致虚拟机重新创建了一个序列号,两者不一致
//    第三步 把文件中的对象读到内存,本地中的序列号和类中的序列号就不一致了
//            解决
//    不让虚拟机帮我们计算,我们手动给出,而且这个值不变

    private  static final long serialVerSionUID =1L;

    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值