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");//写完后,自动打开
 }

}
原文地址:https://www.cnblogs.com/yiqiu2324/p/3168496.html