3.Java-字符和字符编码

字符的编码

字符编码有两大类:非Unicode编码 (ASCII、ISO 8859-1、Windows-1252、GB2312、GBK、GB18030和Big5),和Unicode编码
其中非Unicode编码是每个国家的各种计算机厂商都对自己常用的字符进行编码,Unicode是给世界上所有字符分配了唯一数字编号。它并没有规定这个编号怎么对应到二进制表示,对应到二进制表示,主要有UTF-32、UTF-16和UTF-8几种方案。接下来具体看看。

非Unicode编码

1.ASCII

全称是American Standard Code for InformationInterchange,即美国信息互换标准代码,该编码只考虑了美国需求,大约只有128个字符,使用7位(0 ~ 127)表示刚好,计算机最小单位是byte-8位,所以首位设置为0。其中128字符中32~126为可打印字符,如图
可打印字符
其余0~31和127为不可打印的字符,来进行一些控制操作,常用如图
不可打印字符

2.ISO 8859-1、Windows-1252

给西欧国家使用,使用一个字节的无符号整型(0~255)来表示一个字符。目前使用更为广泛的是Windows-1252编码,基本上可以认为,ISO 8859-1已被Windows-1252取代

ISO 8859-1标准比较早,而欧元比较晚,缺少欧元(€)这个符号,Windows-1252编码中加入了欧元符号以及一些其他常用的字符,在很多应用程序中,即使文件声明它采用的是ISO 8859-1编码,解析的时候依然被当作Windows-1252编码。

3.GB2312、GBK、GB18030

中文的编码从GB2312,发展到GBK,再到GB18030,均能够向下兼容,而能表示的范围从7000个汉字扩展到76 000多个字符,包括了很多少数民族字符,以及中日韩统一字符。

美国和西欧的字符使用一个字节足够,对于中文而言是不够使的,GB2312固定使用两个字节表示汉字,最高位都是1,如果是0,就认为是ASCII字符。GBK同样使用固定的两个字节表示,GB18030使用变长编码,有的字符是两个字节,有的是四个字节,根据第二个字节的范围来判断是两个字节还是四个字节

4.Big5

针对繁体中文

Unicode编码

Unicode主要做了这么一件事,就是给所有字符分配了唯一数字编号。它并没有规定这个编号怎么对应到二进制表示,对应到二进制的方案,主要有UTF-32、UTF-16和UTF-8。

1.UTF-32

每个字符使用4个字节表示,空间浪费,实际很少使用

2.UTF-16

字节变长表示,字符集合使用2个字节,增补字符集使用四个字节,UTF-16常用于系统内部编码,对于美国和西欧国家而言,还是很浪费的。

UTF-16使用两个或4个字节表示一个字符,Unicode编号范围在65536以内的占两个字节,超出范围的占4个字节,BE就是先输出高位字节,再输出低位字节,这与整数的内存表示是一致的

3.UTF-8

字节变长表示,1~4字节个数不等,UTF-8是兼容ASCII的,对大部分中文而言,一个中文字符需要用三个字节表示。

总结Unicode编码
“Unicode给世界上所有字符都规定了一个统一的编号,编号范围达到110多万,但大部分字符都在65 536以内。Unicode本身没有规定怎么把这个编号对应到二进制形式。

UTF-32/UTF-16/UTF-8都在做一件事,就是把Unicode编号对应到二进制形式,其对应方法不同而已。UTF-32使用4个字节,UTF-16大部分是两个字节,少部分是4个字节,它们都不兼容ASCII编码,都有字节顺序的问题。UTF-8使用1~4个字节表示,兼容ASCII编码,英文字符使用1个字节,中文字符大多用3个字节”

乱码的产生

1.编码的转换

Unicode出现后,字符有多种编码方式,不同编码通过Unicode进行转化。具体的过程为,一个字符从A编码转到B编码,先找到字符的A编码格式,通过A的映射表找到其Unicode编号,然后通过Unicode编号再查B的映射表,找到字符的B编码格式,比如“马”从GB18030转到UTF-8,先查GB18030->Unicode编号表,得到其编号是9A 6C,然后查Uncode编号->UTF-8表,得到其UTF-8编码:E9 A9AC。
不可打印字符

2.乱码的原因

(1) 解析错误
A编码的字符,被当做B编码的字符来解析。 通过切换编码方式就可以得到纠正。

(2) 解析错误+编码转换
计算机程序为了便于统一处理,经常会将所有编码转换为一种方式,比如UTF-8。在转换时需要通过原始编码来映射Unicode,如果原始编码是A,被当做B进行映射后就会出现乱码。

(3) 从乱码中恢复
基本方法
1)public byte[] getBytes(String charsetName),这个方法可以获取一个字符串的给定编码格式的二进制形式。
2)public String(byte bytes[], String charsetName),这个构造方法以给定的二进制数组bytes按照编码格式charsetName解读为一个字符串。

      // 假设原来的编码是GB18030,被错误解析成windows-1252
      String str = "ÀÏÂí";
      String newStr = new String(str.getBytes("windows-1252"), "GB18030");
      System.out.println(newStr);

使用循环实现的方法

// 通过A编码转换,B编码查看,以下共12中组合方式
public static void recover(String str)
                throws UnsupportedEncodingException{
            String[] charsets = new String[]{
                    "windows-1252", "GB18030", "Big5", "UTF-8"};
            for(int i=0; i<charsets.length; i++){
                for(int j=0; j<charsets.length; j++){
                    if(i! =j){
                        String s = new String(str.getBytes(charsets[i]), charsets[j]);
                        System.out.println("---- 原来编码(A)假设是: "
                            +charsets[j]+", 被错误解读为了(B): "+charsets[i]);
                        System.out.println(s);
                        System.out.println();
                    }
                }
            }
        }

字符的本质

char在java中使用Unicode处理,具体使用UTF-16BE进行编码,是一个固定占用两个字节的无符号正整数,这个正整数对应于Unicode编号,用于表示那个Unicode编号对应的字符。

       // 多种赋值
        1. char c = 'A' // ASCII码表示的字符
        2. char c = '马'  // 中文字符,赋值时需要注意中文编码,将其对应的Unicode赋值
        3. char c = 39532 // 直接赋值字符的Unicode编码的10进制
        4. char c = 0x9a6c  // 直接赋值字符Unicode编码的16进制
        5. char c = '\u9a6c' // 直接赋值字符Unicode编码的字符形式

因为char本质是unicode编码的正整数,所以能够进行算数运算和比较运算。加减运算的使用场景是针对ASCII码字符,大写A~Z的编号是65~90,小写a~z的编号是97~122,正好相差32,所以大写转小写只需加32,而小写转大写只需减32。

书籍:18年出版/机械工业出版社/马俊昌/《Java编程的逻辑》第一部分第二章

posted @   顾的江小鱼  阅读(461)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示