----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"); } }看两个示意图:
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 */
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");//写完后,自动打开 } }