5-字符与字符编码

看王争专栏字符章节总结

目录

一,字符,字符集和字符编码

1)字符

2)字符集

3)字符编码

二,常见字符集和字符编码

1)ASCII

2)GB*系列字符集和字符编码

3)Unicode字符集和UTF*系列字符编码

3-1)UTF-32

3-2)UTF-16

3-3)UTF-8

三,Java的字符编码

1)默认问题

2)java汉字更编码占多少字节

3)unicode与ASCII的表示差异

4,另外unicode的四字节编码表式,以及证明了char存的是utf-16字节编码

5)为什么char是两个字节,能存汉字


一,字符,字符集和字符编码

1)字符

字符可以理解为我们平时的符号,包括汉字,数字,字母,标点,图形符号,控制符号(换行)等

2)字符集

字符集是一组字符的集合,比较常用的有ASCII, GBK,DB2312,GB18030,Unicode,比如GB2312主要是汉字的字符集,里面包括了6000多常用汉字和一些符号等。

字符集不仅包含字符,还包含字符的编号,这个编号只是方便索引与字符编码不是一回事。个人理解字符的编号是以0123方式的编号,而字符编码则指的是二进制编码,像java的char存的是字符编码(UTF-16)。

3)字符编码

字符编码是指计算机存储字符编号的一种格式,一般一种字符集会对应一种字符编码,比如GBK,GBK2312,ASCII,但也有例外,比如Unicode有UTF-8,UTF-16,UTF-32。

二,常见字符集和字符编码

1)ASCII

全称叫美国信息交换标准代码,

是最早的一种字符集,只有128个字符,所以使ASCII的字符编码很简单,刚好用一个字节,使用一个字节中的低7位来存储值,最高位默认为0

0-31是不可显示字符

32-127可显示字符

可显示字符好表式,不可显示字符的表示一般使用\xxx,其中xxx用八进制表示,当然可显示字符也可以。

比如\012 是换行,ascii的10

a,可以表示为97,也可以表示为\141

也有部份可用转义字符来表示

\r,表示回车

\n换行

\t 表示tab

也可双使用ascii码或码值(a = 97)来直接表示字符

如char a = 97,与char a = 'a';是一样的效果

char类型的比较操作

2)GB*系列字符集和字符编码

ACSII只有128个字符,对于英文可能够用了,但是对于中文,日文等就不行了,因此计算机在传播中出现了不同的字符集以及字符编码

其中GBK2312是1980年发布的,收录了6000个常用汉字,符号等,以2字节存储,虽然99%都有了,但还是不全

GBK也是两个字节,在GB2312的基础上增加了两万多个汉字和符号,

GBK18030,兼容了GBK和GB2312,收录的汉字也更多,达到7万多,但是占用字节是变动的,有些占1字节,占2字节,有些占4字节

3)Unicode字符集和UTF*系列字符编码

各语言都有了各自的字符集与字符编码,同一串二进制在不同的字符集和字符编码中有不同的意思,这使得我们没法在同一个文档中使用两种及以上的语言。

这时大一统的Unicode出现了,他收集了100多万个字符,每个字符有他自己的编号。

对于Unicode习惯使用前缀 U+ 与16进制对编号来进行表式,范围为U+ 0 ~~ U+10FFFF。

unicode虽然收集的字符很多,但是常用的终归是少数,所以把常用的放在前面,同时也可以使占用的空间更少。unicode分部如下:

U+0000-- U+FFFF(FFFF刚好2字节) 存储常用的字符,其中U+D800 -- U+DFFF 不使用(即这些编号没有unicode字符),

U+ 10000 -- U + 10FFFF,存储其他的字符

其中U+0000 -- U+ 007F 与ASCII的编号一样。

Unicode有三种字符编码,分别为UTF-32,UTF-16,UTF-8

此处注意Unicode的字符编号使用U+方式表示

而字符编码是\uXXXX这样的方式,都是16进制

3-1)UTF-32

utf-32使用4节字定长存储,直接将unicode的编号存入即可,好处是编码简单,读取解码时也简单,坏处就是占用空间较多。

3-2)UTF-16

U+的表示方式是,unicode的字符的编号。

untf-16使用2,4两种字节不定长存储,其中 U+0 ~ U+FFFF(不包括U+D800 -- U+DFFF),直接以两字节存储

对于U+10000及以上的编号如下:

1,将U+10000~U+10FFFF 减去10000,得到U+0000 ~ U+FFFFF的新的范围,同时得到每个字符对应的新的编号,总共20位

2,将20位的高10位取出,放入一个新的二进制的后十位,前面缺失的用1101 10补上,这样得到一个新的二进制位,值的范围为D800 -- DBFF ;这就是四字节的前2字节。

3,将20位的后10位取出,放入一个新的二进制的后十倍,前面缺失的用1101 11补上

这样得到一个新的二进制位,值的范围为DC00 -- DFFF;这就是四字节的后2字节。

上面高二位与低二位的值的范围合一起刚好是D800 -- DFFF,也就是unicode没有字符的编号,同时也是为什么这些编号没有字符(用区来分二进制编码,以有四进制编码的高16位与低16位)。

通过上面步骤得到了一个四字节的数据,因为char是2进制的,如何判断是二字节编码还是四字编码呢:

1,如果值的范围是U+0~ U+FFFF,同时不属于D800 -- DFFF,那么读取的这两个字节就是二字节编码

2,如果值的范围是U+ D800 - U+DBFF(前缀1101 10 允许的值范围),那么读出来的就是四字节编码的高16位

3,如果值的范围是U+DCFF -- U+DFFF(前缀1101 11 允许的值范围),代表的就四字节编码的低16位,其实读出高16位,正常编码后面肯定就是低16位

3-3)UTF-8

utf-8存储也是不定长的,有1,2,3,4四种字节编码,不同范围内的编号使用不同的编码:

U0000 -- U007F 使用1字节存储,与ASCII对应

U0080 -- U07FF 使用2字节存储

U0800 -- UFFFF 使用3字节存储,包括中文,日文等

U10000 -- U10FFFF 使用4字节存储

使用类似于utf-16的前缀补位法,如下:

注意2字节编码第一个为110,3字节编码为1110,四字节编码11110,后面的非第一个字节全为10.

根据这个补位规则可以反推数值范围,如2进制位存储的范围:

110+10占去5位,剩下11位,最大值U07FF,开始位为U0080

三字节同上,余16位,得到最大值为UFFFF。

四字节特殊,收录的不够上限

三,Java的字符编码

1)默认问题

c\c++时ASCii是主流所以是1字节

而当java发起时,unicode已经流行,因此设计了两个字节的char来存储unicode的常用字符,U0000 -- UFFFF。unicode字符会通过utf-16编码后存在于char变量中(UTF-32是四字节,UTF-8是三字节,UTF16是2字节)。

四字节的以两个char存储。

2)java汉字更编码占多少字节

gbk:2字节

gb2312:2字节

gb18030:1,2,4字节

utf-8:3字节

utf-16:2字节

utf-32:4字节

3)unicode与ASCII的表示差异

1,ASCII

ASCII和字符编码也叫ASCII字符编码,且字符的编码与字符编码是一样的都是0-127

直接使用编号数字可以,char a = 97;

直接使用字符也可以 char a = 'a';

使用\xxx格式,xxx是8进制表式,如char a = '\141'(注意单引号)

以上表式的都是 'a'

对于0-31的无显示符号,\xxx方式,有些特殊的有转换符如: \r, \n等

2,Unicode对应的字符编码

Unicode编号为U+ 如U+D800 -- U+DFFF是不用的

而字符编码为\uXXXX模式

一般使用16进制表式, char a = 0x0041;

直接使用符号表式 char a = 'a'

使用\uxxxx方式表式,xxxx为16进制,注意前缀u '\u0041'(注意单引号)

以上都是表式的'a'

4,另外unicode的四字节编码表式,以及证明了char存的是utf-16字节编码

一, String a = "\uD83D\uDF01"

二,char[] a = new char[2]; a[0] = '\uD83D'; a[1] = '\uDF01';

三,char[] a = Charactor.toChars(0x1F701)

以上证明了char存的是utf-16的字符编码(unicode编号经过utf-16编码后存入char变量中)。在UTF-16下超过FFFF以上的就会出现字符编码与unicode不一样了。

一些getByte的方法

static void getStringBytes (String s) {
    byte[] s1Bytes = s.getBytes(StandardCharsets.UTF_8);
    for (int i = 0; i < s1Bytes.length; i++) {
        System.out.println(("中的utf-8第" + (i + 1) + "位编码为:" + Integer.toBinaryString((int)s1Bytes[i])));
    }
}


static void getHexStringBytes (String s) {
    StringBuilder sb = new StringBuilder();
    byte[] s1Bytes = s.getBytes(StandardCharsets.UTF_8);
    for (int i = 0; i < s1Bytes.length; i++) {
        System.out.println(Integer.toHexString(s1Bytes[i]));
        sb.append(Integer.toHexString(s1Bytes[i]), 6, 8);  
        //只有后两位是真正的二进制编码,前面全是补的11111
    }
    System.out.println(sb);
}
5)为什么char是两个字节,能存汉字

1,java的char默认是使用utf-16编码后存入到char变量中。

而汉子基本在0000 - FFFF(排除掉D800 - DFFF)这个范围内,这个范围内的UTF16不需要特殊处理,可以直接存入(也就是字符的编号,与字符编码一致)。

2,参考别人的

看看就好

https://www.cnblogs.com/ddzj01/p/17030838.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值