zoukankan      html  css  js  c++  java
  • 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");//写完后,自动打开
     }
    
    }
    
  • 相关阅读:
    python 包与模块
    互斥锁与自旋锁
    TCP三次握手四次挥手
    缓存击穿、缓存穿透、缓存雪崩
    五种IO模型
    MySQL使用mysqldump进行数据备份
    golang数据库连接池参数设置
    golang代码文件目录组织、包目录组织学习笔记
    【转】如何用Vim提高开发效率
    emacs显示行号
  • 原文地址:https://www.cnblogs.com/yiqiu2324/p/3168496.html
Copyright © 2011-2022 走看看