• day21(下)_编码解码



    ----android培训 java培训、期待与您交流!----

    1.常见编码表概念:

    /*
    (参考了百度百科/文库,维基百科,他人的博客等等)
    常见的编码标准:
    1.ASCII码
       ASCII码一共规定了128个字符(0000 0000~0111 1111)的编码,
       只占用了一个字节的后面7位,最前面的1位统一规定为0
    2.EASCII码
        英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。
        比如,在法语中,字母上方有注音符号,它就无法用ASCII码表示。
        于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。
        比如,法语中的é的编码为130(二进制10000010)。
        这样一来,这些欧洲国家使用的编码体系,可以表示最多256(128+128)个符号.
    3.GB2312与GBK
     ①gb2312
        GB2312编码适用于汉字处理、汉字通信等系统之间的信息交换,
      通行于中国大陆;新加坡等地也采用此编码。
      中国大陆几乎所有的中文系统和国际化的软件都支持GB 2312。
        
        GB2312兼容ASCII码,对于文件中的ASCII码使用1byte,中文为2byte
        
        每个汉字及符号(全角符号)以两个字节来表示。第一个字节称为“高位字节”,第二个字节称为“低位字节”。
        
     ②GBK:
       GBK即汉字内码扩展规范,GBK是对GB2312-80的扩展
       字符有一字节和双字节编码,00–7F范围内是一位,和ASCII保持一致.
        之后的双字节中,前一字节是双字节的第一位。
    GBK字符集范围
    分区                      高位     低位
    ----------------------------------------------
    ●GBK/1:GB2312非汉字符号: A1~A9    A1~FE
    ●GBK/2:GB2312汉字      : B0~F7    A1~FE
    
    ●GBK/3:扩充汉字        : 81~A0    40~FE
    ●GBK/4:扩充汉字        : AA~FE    40~A0
    
    ●GBK/5:扩充非汉字      : A8~A9    40~A0
    其中1和2就是对应的GB2312字符集。
    */
    /*
    4.Unicode与UTF-8
     ①Unicode
       统一码,统一使用2byte编码
     ②UTF-8
       UTF-8(8-bit Unicode Transformation Format)
      是一种针对Unicode的可变长度字符编码,也是一种前缀码。
      它可以用来表示Unicode标准中的任何字符,
      且其编码中的第一个字节仍与ASCII兼容,
      这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。
      因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。
    
     ③Uicode与UTF-8转换
      UTF-8用1到6个字节编码 UNICODE字符。
      如果UNICODE字符由2个字节表示,则编码成UTF-8很可能需要3个字节,
      而如果UNICODE字符由4个字节表示,则编码成 UTF-8可能需要6个字节。
      用4个或6个字节去编码一个UNICODE字符可能太多了,但很少会遇到那样的UNICODE字符。
      
     UTF-8转换表
    UNICODE    UTF-8 
    00000000 - 0000007F 0xxxxxxx 
    00000080 - 000007FF 110xxxxx 10xxxxxx 
    00000800 - 0000FFFF 1110xxxx 10xxxxxx 10xxxxxx 
    00010000 - 001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
    00200000 - 03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
    04000000 - 7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
    例如:
      “汉”字的Unicode编码是0x6C49。
      0x6C49在0x0800-0xFFFF之间,
      使用用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。
      将0x6C49写成二进制是:0110 1100 0100 1001,
      用这个比特流依次代替模板中的x,
      得到:11100110 10110001 10001001,
      即E6 B1 89。
    
    5.记事本保存的Unicode,UTF-8,ANSI文件
       ①在UTF-8文件的开首,很多时都放置一个U+FEFF字符(UTF-8以EF,BB,BF代表),以显示这个文本文件是以UTF-8编码。
       ②Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且FF比FE大1。
        如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。
        (关于大头,小头下面在解释)
       ③ANSI:
          在简体中文系统下,ANSI 编码代表 GB2312 编码,
          在日文操作系统下,ANSI 编码代表 JIS 编码。
    6.大端(Big Endian)与小端(Little Endian)
        考虑一个W位的整数。
        它的各位表达如下:[Xw-1, Xw-2, ... , X1, X0],
        它的
        MSB (Most Significant Byte, 最高有效字节)为 
         [Xw-1, Xw-2, ... Xw-8];
        LSB (Least Significant Byte, 最低有效字节)为
         [X7,X6,..., X0]。 
        其余的字节位于MSB, LSB之间。
    
         big endian是指低地址存放最高有效字节(MSB),
          而little endian则是低地址存放最低有效字节(LSB)。
      例如:
        0x12 34 56 78:
          最高有效字节:12
          最低有效字节:78
      
      低地址 ---->  高地址     
       12    34 56   78     大端
       78    56 34   12     小端
    
    对于如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;
        如果头两个字节是FF FE,就表示该文件采用小头方式。
    简单理解为:
       文件开头:FE FF->大头方式->第一个字节+第二个字节
                FF FE->小头方式->第二个字节+第一个字节
    */

    2.转换流中的编码:

    /*
    字符流的本质是:
       字节流+转换流
    例如:FileReader内部会用到FileInputStream和InputStreamReader
    */
    package transstream;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    class TransDemo{
     public static void decoding(String fileName,String strCoding)throws IOException{
      OutputStreamWriter osr=new OutputStreamWriter(new FileOutputStream(fileName),strCoding);
      osr.write("你好");
      osr.close();
     } 
     public static void encoding(String fileName,String strCoding)throws IOException{
      InputStreamReader isr=new InputStreamReader(new FileInputStream(fileName),strCoding);
      char[] chArr=new char[20];
      int charas=isr.read(chArr);//返回读取的字符数
      System.out.println(new String(chArr,0,charas));
    
     }
     public static void main(String[] args)throws IOException{
       decoding("gbk.txt","gbk");
       encoding("gbk.txt","gbk");
       
       decoding("gbk.txt","gbk");
       encoding("gbk.txt","utf-8");
       
       decoding("unicode.txt","unicode");
       encoding("unicode.txt","gbk");
       
       decoding("utf-8.txt","utf-8");
       encoding("utf-8.txt","gbk");
     }
    }

    看两个示意图:

    OutputStreamWriter

    解码

    3.出现乱码的还原:

    /*
    编码:字符串->字节数组
    
    解码:字节数组->字符串
    */
    package encode;
    import java.util.Arrays;
    import java.io.IOException;
    class EncodeDemo{
     /*见示意图*/
     public static void main(String[] args)throws IOException{
      String s="你好";
      byte[] gbk=s.getBytes("GBK");
      System.out.println(Arrays.toString(gbk));
    
      
      String s2=new String(gbk,"ISO8859-1");//ISO8859-1,单字节编码,最多能表示的字符范围是0-255
      System.out.println(s2);//打印出????
     
    /*再次使用ISO8859-1编码得到原字节序列,再次使用GBK解码得到 你好*/
      byte[] ISO=s2.getBytes("ISO8859-1");
      System.out.println(new String(ISO,"GBK"));
     
     /*
      如果把ISO8859-1换成UTF-8则不能还原成你好
      这是因为
      UTF-8与GBK都能识别中文
      [-60,-29,-70,-61]----utf-8解码-->???(没有查到对应的文字,在对应字符区没有查到,会去查未知字符区)
       --utf-8编码->[-17, -65, -67, -17, -65, -67, -17, -65, -67]--gbk解码-->锟斤拷锟?
                            ?              ?             ?
     */
     }
    }
    /*
    
    不要忘了,计算机是以补码的形式存储二进制的.
    那么
    你好的机内码为:
       C4    E3
    1100 0100(补码)
    字节数组存放的十进制数据需要转换原码即:
    1011 1100->-60
    同理:
    E3:1110 0011->1001 1101->-29
    BA:1011 1010->1100 0110->-70
    C3:1100 0011->1011 1101->-61
    */

    GBK与ISO8859-1

    4.联通与微软”有仇”

    package encode;
    import java.io.IOException;
    import java.util.Arrays;
    class EncodeDemo2{
        public static void main(String[] args)throws IOException{
             String str="联通";
             byte[] byteArr=str.getBytes("GBK");
             for(byte aByte : byteArr)
                System.out.println(Integer.toBinaryString(aByte&255));//aByte首先提升为int,在与255逐位相&
                /*
                当一个byte会转换成int时,由于int是32位,而byte只有8位这时会进行补位
                例如                                         
                         byte补码:11000001       
                             原码:10111111                       
                             十进制:-63
                          int补码:11111111 11111111 11111111 11000001
                                
                             原码:10000000 00000000 00000000 00111111
                         十进制:-63
                      其实byte补码转int补码对于负数来说就是补1,对于正数补0.
                          
               */
              
              
        } 
    }
    /*
    联通:
    11000001 10101010
    11001101 10101000
    联通的GBK编码恰好符合UTF-8形式,记事本按UTF-8解码导致乱码.
    解决方式:联通前面添加一个非ASCII码的字符,使记事本按GBK解码.
    (ASCII码(0 xxxxxxx)依然满足UTF-8一字节形式)
    */
    联通编码

    5.小练习(集合+流)

    /*
    有五个学生,每个学生有3门成绩,从键盘输入以上数据(包括姓名,三门课成绩),
    输入格式:如:zhangsan,30,40,60计算出总成绩
    并把学生的信息和计算出的总分数按降幂放在磁盘文件"stud.txt"中.
    */
    /*
    分析:1.如何获取输入?
             使用BufferedReader获取一行输入(readLine)
           2.如何排序?
             排序使用到集合,这里使用TreeSet,也就是说将new Student(zhangsan,30,40,60)
             存入TreeSet.(如果采用TreeMap(姓名,总成绩),会依照姓名(Key)排序,很不方便)
         3.如何存储?
             集合转数组写入文件
    */
    package test;
    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.InputStreamReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.util.Arrays;
    import java.util.TreeSet;
    import java.util.Set;
    import java.util.Collections;
    import java.util.Comparator;
    
    import java.util.*;
    
    class Student implements Comparable<Student>{
     private String name;
     private int score_1,score_2,score_3,totalScore;
     public Student(String name,int s1,int s2,int s3){
      this.name=name;
      score_1=s1;
      score_2=s2;
      score_3=s3;
      totalScore=score_1+score_2+score_3;
     }
     public int compareTo(Student stu){
      int value=this.totalScore-stu.totalScore;
      if(value==0)//总成绩相同,按姓名从大到小排序
       return this.name.compareTo(stu.name);
      else
         return value;
     }
     public int getTotal(){
     
       return totalScore;
     }
     public String toString(){
        return name+" "+score_1+" "+score_2+" "+score_3;
     }
     /*可能存入HashSet集合复写equals和hashCode*/
     public  int hashCode(){
       return name.hashCode()+totalScore*29;
      
     }
     public boolean equals(Object obj){
       Student stu=(Student)obj;
       if(!(obj instanceof Student))
        throw new ClassCastException("类型不匹配");
       else
         return this.name.equals(stu.name)&&this.totalStore==stu.totalStore;
     }
    }
    
    
    
    class Test{
     /*从键盘获取学生信息,total为学生总数*/
     public static String[] getInfo(int total)throws IOException{
       BufferedReader bufr =new BufferedReader(new InputStreamReader(System.in));
       String[] stuInfo=new String[total];
       for(int i=0;i<total;++i)
         stuInfo[i]=bufr.readLine();
       return stuInfo;
     }
     
     /*将学生信息存入TreeSet*/
    
     public static Set<Student> storeSet(String[] stu){
        return storeSet(stu,null);//不使用指定比较器
     }
     
     public static Set<Student> storeSet(String[] stu,Comparator<Student> cmp){
       Set<Student> ts=null;
       if(cmp==null)
        ts=new TreeSet<Student>();
       else
         ts=new TreeSet<Student>(cmp);
       for(int i=0;i<stu.length;++i){
        String[] newStu=stu[i].split(" ");
        int score_1=Integer.parseInt(newStu[newStu.length-3]);
        int score_2=Integer.parseInt(newStu[newStu.length-2]);
        int score_3=Integer.parseInt(newStu[newStu.length-1]);
        ts.add(new Student(newStu[0],score_1,score_2,score_3));
       }
      return ts;
     }
     /*将TreeSet中的信息存入stud.txt*/
     public static void writeStu(Set<Student> ts,String fileName)throws IOException{
       Student[] stu=ts.toArray(new Student[ts.size()]);//将集合转成数组
       BufferedWriter bfw=new BufferedWriter(new FileWriter(fileName));
       for(Student s : stu){
          bfw.write(s.toString()+"	");//学生信息写入
          bfw.write(s.getTotal()+"");
          bfw.newLine();
          bfw.flush();
       }
       bfw.close();
    
     }
     
     
     
     public static void main(String[] args)throws IOException{
       Comparator<Student> cmp=Collections.reverseOrder();
       Set<Student> ts= storeSet(getInfo(2),cmp);//不能直接传入Collections.reverseOrder(),必须指明泛型.
       System.out.println(ts.toString());
       writeStu(ts,"stud.txt");
       Runtime.getRuntime().exec("notepad.exe stud.txt");//写完后,自动打开
     }
    
    }
    
  • 相关阅读:
    年轻人的第一个 Spring Boot 应用,太爽了!
    面试问我 Java 逃逸分析,瞬间被秒杀了。。
    Spring Boot 配置文件 bootstrap vs application 到底有什么区别?
    坑爹的 Java 可变参数,把我整得够惨。。
    6月来了,Java还是第一!
    Eclipse 最常用的 10 组快捷键,个个牛逼!
    Spring Cloud Eureka 自我保护机制实战分析
    今天是 Java 诞生日,Java 24 岁了!
    厉害了,Dubbo 正式毕业!
    Spring Boot 2.1.5 正式发布,1.5.x 即将结束使命!
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/3168496.html
Copyright © 2020-2023  润新知