Java 入门之6:Java中的char类型是怎么存储的以及常见的编码字符集

目录

char 类型:

Java中常用转义字符归纳:

什么是编码?

常见的编码字符集:

ASCII:

IOS8859-1:

GB2312:

GBK:

BIG-5:

ANSI:

Unicode:

Unicode字符集-UTF编码

标准Unicode字符集(全):

UTF-8:(编程人员使用最多的Unicode编码存储方案)

UTF-16:(很少使用)

UTF-32:(基本不会使用)

UTF-16BE、UTF-16LE、UTF-32BE、UTF-32LE(了解):

解析使用Unicode的UTF-8编码方案怎么存储一个字符:

为什么Java 中 char 类型存储的字符都是2个字节,而使用UTF-8存储的时候却是1 ~ 4个字节?

内码的分类(即权威机构制定好的编码字符集):


char 类型:

  1. char类型,就是Java中的字符类型,它的书写格式为 单引号引起来的单个字符,比如: char ch = '中';   那么这个数据就是Java中的字符类型
  2. char类型是用来表示Unicode编码表中的字符,Unicode编码被设计用来处理各种语言的文字,符号,char类型占用两个字节的无符号数来表示Unicode码(HEX),取值范围为 0 ~ 65535,即 2^{16}-1,即 一共可以允许存储65535个字符,或者说可以允许存储0~65535区间的Unicode码,也可以用十六进制(HEX),十进制(DEC),八进制(OCT),二进制(BIN)的数字来表示对应的Unicode码(HEX)和字符!。
  3. char类型包括了普通字符和转义字符。
  4. Java中常用转义字符归纳

    转义字符

    含义

    Unicode值(十六进制)

    八进制

    十进制

    二进制

    拼接字符串的作用

    举例

    \b

    退格

    \u0008

    010

    8

    0b00001000

    会往前退一格,因此离\b左边最近的一个字符会被退格掉

    比如输出:

    "abc\bdef"

     

    结果:

    abdef

    \n

    换行

    \u000a

    012

    10

    0b00001010

    会把\n左右两边的字符串分开换行输出

    比如输出:

    "abc\ndef"

     

    结果:

    abc

    def

    \r

    回车

    \u000d

    015

    13

    0b00001101

    会回车到字符串的开头,在\r右边还有字符的时候,相当于退格掉\r左边的所有字符

    比如输出:"abc\rdef"

     

    结果:

    def

    \t

    制表符(tab)

    \u0009

    011

    9

    0b00001001

    如果是在GBK的编码下编译执行,如果\t前面(左边)有字符,在不是八个或者八个的倍数的情况下,出现的空格就是 8 - 出现的字符的个数,比如abc\tdef,它输出的时候abc和def中间就只隔了5个空格字符,如果前面刚好出现了八个字符或者八的倍数的字符的情况下,中间就会隔开8个空格,在GBK的编码下,一个\t转义字符相当于一个tab键,一个tab键相当于8个空格。

     

    如果是在UTF-8的编码下编译执行,如果\t前面(左边)有字符,在不是四个或者四个的倍数的情况下,出现的空格就是 4 - 出现的字符的个数,比如abc\tdef,它输出的时候中间就只隔了1个空格字符,如果前面刚好出现了四个字符或者四的倍数的字符的情况下,中间就会隔开4个空格,在UTF-8的编码下,一个\t转义字符也是相当于一个tab键,一个tab键相当于4个空格。

    比如输出:

    "abc\tdef"

     

    结果:

    abc     def

     

    比如输出:

    "\tabcdef"

     

    结果:

            abcdef

     

    \"

    双引号

    \u0022

    042

    34

    0b00100010

    会把 " 作为一个字符和字符串中的其他字符组合成一个字符串输出

    比如输出:

    "abc\"def"

     

    结果:

    abc"def

    \'

    单引号

    \u0027

    047

    39

    0b00100111

    会把 ' 作为一个字符和字符串中的其他字符组合成一个字符串输出

    比如输出:

    "abc\'def"

     

    结果:

    abc'def

    \\

    反斜杠

    \u005c

    0134

    92

    0b01011100

    会把 \ 作为一个字符和字符串中的其他字符组合成一个字符串输出

    比如输出:

    "abc\\def"

     

    结果:

    abc\def

    \0

    空字符(nothing)

    \u0000

    00

    0

    0b00000000

    相当于没有添加任何字符,空字符(nothing)什么字符也没有添加

     

    这个 \0 也可以理解成为char类型的默认值表示 空字符(nothing)\u0000

    比如输出:

    "abc\0def"

     

    结果:

    abcdef

    \f换页\u000c014120b00001100会把\f原样输出,只不过这个字符打印不出来,(很少会用到 \f 这个转义字符)

    比如输出:

    "abc\fdef"

     

    结果:

    \uxxxx字符的unicode编码表中的十六进制表示\uxxxx对应的八进制对应的十进制对应的二进制把Unicode字符集中的对应的字符原样输出
    \xxx字符的ASCII、IOS8859-1编码表中的八进制表示\uxxxx对应的八进制对应的十进制对应的二进制把ASCII、IOS8859-1码表中的对应的字符原样输出(基本不会使用,最多能表示到 \377)

 


什么是编码?

百度百科找的一个概念

 编码可以理解为计算机中一种机制,计算机所有的数据都是以二进制来表示存储,运算,传输的,数据当然也包括了比如 音乐、视频、图片、文字等……,它们在传输、存储,运算的时候都会先转成对应的二进制数再进行相关的操作,这个过程就称之为编码,解码也就是编码的逆过程,把对应的二进制数转化成对应的资源数据等……,

比如文字,在存储,传输,运算的时候转成编码的过程中的时候,就需要制定一系列的标准,那么这个编码标准就称之为字符集,或者称之为编码集。


常见的编码字符集:

由权威机构制定的编码表才能被称之为: 字符集

ASCII:

英文字符集(American Standard Code for Information Interchange)美国信息交换标准码

ASCII码表:

编码方式: 用一个字节的 7 位表示

只需记住3个字符的编码就行,(其他不常用)

  1. 新中国成立前一年 48 => 对应字符 '0'
  2. 那年香港回归 97 => 对应字符 'a'
  3. 两个6减一 65 => 对应字符 'A'

 

IOS8859-1:

西欧字符集:

向下兼容了ASCII,此字符集支持部分于欧洲使用的语言,包括 阿尔巴尼亚语、巴斯克语、布列塔尼语、加泰罗尼亚语、丹麦语、荷兰语、法罗语、弗里西语、加利西亚语、德语、格陵兰语、冰岛语、爱尔兰盖尔语、意大利语、拉丁语、卢森堡语、挪威语、葡萄牙语、里托罗曼斯语、苏格兰盖尔语、西班牙语及瑞典语。包括西欧语言,希腊语、泰语、阿拉伯语、希伯来语对应的文字符号。欧元符号 出现的比较晚,没有被收录在ISO-8859-1当中。

编码方式:使用一个字节的8位表示

 

GB2312:

简体中文字符集:

向下兼容了ASCII,这个编码适用于汉字处理、汉字通信等系统之间的信息交换,通行于中国大陆;新加坡等地也采用此编码。中国大陆几乎所有的中文系统和国际化的软件都支持GB 2312。

编码方式:最多使用两个字节编码

 

GBK:

中文字符集:

向下兼容了GB2312,GBK编码,是在GB2312-80标准基础上的 内码 扩展规范,使用了双字节编码方案,其编码范围从8140至FEFE(剔除xx7F),共23940个码位,共收录了21003个汉字,完全兼容GB2312-80标准,支持国际标准ISO/IEC10646-1和国家标准GB13000-1中的全部中日韩汉字,并包含了BIG5编码中的所有汉字。GBK编码方案于1995年10月制定, 1995年12月正式发布,中文版的WIN95、WIN98、WINDOWS NT以及WINDOWS 2000、WINDOWS XP、WIN 7、WIN10等都支持GBK编码方案。

编码方式:最多使用两个字节编码

 

BIG-5:

繁体中文字符集:

向下兼容了ASCII,主要使用地区是中国台湾,中国香港,中国澳门……

编码方式:最多使用两个字节编码

 

ANSI:

在有些软件中,会出现ANSI编码,比如Notepad++,它实际上并不是一个真正的编码字符集,当选择了这个所谓的编码集了以后,实际上它就是获取当前系统的编码字符集,比如在中文版的windows系统,获取的则是GBK编码字符集。

……

 

Unicode:

统一码,也叫万国码、单一码(Unicode)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的 二进制 编码,以满足跨语言、跨平台进行文本转换、处理的要求。1990年开始研发,1994年正式发布1.0版本,2020年发布13.0版本。Unicode几乎包含了目前人类所有的字符,Unicode字符集只规定每个字符的编码,并没有规定怎么存储字符。

 

Unicode的3种编码存储方案: UTF-8、UTF-16、UTF-32


Unicode字符集-UTF编码

 

标准Unicode字符集(全)

https://www.ziti163.com/uni/FB00-FB4F.shtml?id=111

 

UTF-8:(编程人员使用最多的Unicode编码存储方案)

UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与 ASCII 相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。因此,它逐渐成为 电子邮件 、 网页 及其他存储或传送文字的应用中,优先采用的编码。

编码方式:它是Unicode的具体实现方案,标准的UTF-8用1到4个字节编码UNICODE字符,修正后的UTF-8用1到6个字节编码UNICODE字符。UTF-8编码方案中,

  1. ·一个US-ASCIl字符只需1字节编码(Unicode范围由U+0000~U+007F)。
  2. ·带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文 等 字母则需要2字节编码(Unicode范围由U+0080~U+07FF) 。
  3. ·其他语言的字符(包括中日韩文字、东南亚文字、中东文字等)包含了大部分常用字,使用3字节编码。
  4. ·其他极少使用的语言字符使用4字节编码。
  5. ·emoji占用3、或者4个字节(emoji就是那些表情包,比如微信和QQ的小黄脸,微软拼音输入法能打出来的表情包),其他极少使用的语言字符使用4字节编码。

UTF-8 的编码规则:(只有两条):

  1. 对于单字节的符号,字节的第一位设为0,后面7位是这个符号的Unicode码,因此这是对于英文字母,UTF-8 编码和 ASCII 编码是相同的
  2. 对应 n 字节的符号(n > 1),第一个字节的前 n 位都设为 1,第 n+1 位设为 0,后面的字节的前两位一律设为 10.剩下的没有体积的二进制位,全部为这个符号的 Unicode码。

UTF-16:(很少使用)

UTF-16是 Unicode 字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为 "storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即 码元)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者2个16位长的码元来表示,因此这是一个变长表示。

UTF-16是 Unicode 的其中一个使用方式。UTF是Unicode Transfer Format的缩写,即把Unicode转做某种格式的意思。它定义于ISO/IEC 10646-1的附录Q,而RFC 2781也定义了相似的做法。在Unicode 基本多文种平面 定义的字符(无论是 拉丁字母 、 汉字 或其他文字或符号),一律使用2字节储存。而在辅助平面定义的字符,会以代理对(surrogate pair)的形式,以两个2字节的值来储存。

UTF-16比起 UTF-8 ,好处在于大部分字符都以固定长度的字节(2字节)储存,但UTF-16却无法兼容于 ASCII 编码。

编码方式:,它是Unicode的具体的实现方案,2个字节,4个字节,这种方案没有兼容ASCII编码,使用较少,但是这个方案对应的是Java中char类型的表示方案,换言之,Java 中的 char 本质上是 UTF-16 编码。

UTF-32:(基本不会使用)

UTF-32 (或 UCS-4)是一种将Unicode 字符编码 的协定,对每一个Unicode码位使用恰好32位元。其它的Unicode transformation formats则使用不定长度编码。因为UTF-32对每个 字符 都使用4 字节 ,就空间而言,是非常没有效率的。特别地,非 基本多文种平面 的字符在大部分文件中通常很罕见,以致于它们通常被认为不存在占用空间大小的讨论,使得UTF-32通常会是其它编码的二到四倍。虽然每一个码位使用固定长定的字节看似方便,它并不如其它Unicode编码使用得 广泛 。

编码方式:4个字节,使用非常的少,原因:每个字符都占用4个字节,浪费存储空间!

 

UTF-16BE、UTF-16LE、UTF-32BE、UTF-32LE(了解):

UTF16-LE 和 UTF16-BE,这与计算机的 CPU 构架有关。计算机在存储器中排列字节的有方式有两种,LE 指 Little Endian(小端法),而 BE 指 Big Endian(大端法)。由于 UTF16 是双字节编码,所以两个字节保存时哪个在前,哪个在后关系到解析出字符的结果。至于为什么会出现 BE 和 LE 的编码,则是由于历史原因造成的:在 Mac 和 PC 机上,对字节顺序的理解是不一致的。如果一个文件不明确说明 UTF16 使用的是 BE 还是 LE,那么就需要通过 BOM 来指明了。我们一般的 X86 系统都是 Little Endian 的,可以认为 UTF16=UTF16-LE。

UTF-32BE和UTF-32LE,这与UTF-16BE和UTF-16LE的原因类似,UTF-32是用四个字节来表示,如果一个文件不明确指定该文件是高位字节在前还是低位字节在前,是按照UTF-32的BE还是LE来存储,那么就有可能造成解读错误,不同的解释表示的值是不一样的。


解析使用Unicode的UTF-8编码方案怎么存储一个字符:

UTF-8 标准的编码方式:

UTF-8 修正后的编码方式:(注:这种的UTF-8编码方式只是一种扩展,或者理解为是一种备用编码方式,它只是在原来的标准UTF-8编码方式的基础上由扩展了两种编码方式了而已,而Unicode目前为止所收集的字符,能查到的也就0XFFFF个,也就是65536(0 ~ 65535)个,(不包括有的用4个字节才能表示的极少语言字符),如果Unicode字符集在未来收集的字符超过了现在所收集的字符,那么可能就会使用这个修正后的UTF-8编码方式!

比如使用UTF-8编码方案存储中国的 '中' 字。

中 的十进制为 20013 ,Unicode码十六进制为 4E2D,二进制为 0100 1110 0010 1101

怎么查询一个字的Unicode码? 可以自己转成十六进制,可以使用计算器转成十六进制,也可以查询Unicode字符集(这是我找的最全的Unicode字符集汇总) https://www.ziti163.com/uni/FB00-FB4F.shtml?id=111

 

又把这张表拿下来

可以看出, 这个字如果按照 UTF-8来存储的话需要3个字节,怎么看出来的?这很简单,比如可以通过Unicode 符号范围的 十六进制 看区间范围,还可以通过十六进制对应的十进制看区间范围。

即UTF-8存储 字这个字符的格式为 : 的二进制为: 0100 1110 0010 1101

然后,从 字的最后一个二进制位开始,依次从低向高填入格式中的 x, 这样就得到了, 的UTF-8 的编码是 1110 0100 1011 1000 1010 1101 ,转换成十六进制就是 E4B8AD

字的十进制是:20013 ,八进制为:47055 ,二进制为: 0100 1110 0010 1101,Unicode字符集中十六进制码为:4E2D ,而 字在UTF-8编码后的二进制码为 1110 0100 1011 1000 1010 1101,十六进制为 0xE4B8ADh,八进制为 71134255,十进制为 14989485 。占用3个字节!


为什么Java 中 char 类型存储的字符都是2个字节,而使用UTF-8存储的时候却是1 ~ 4个字节?

在知乎上摘自两位大佬的解释,非常的精辟,完美的解决了我的知识盲区,非常感谢他们,

第一位大佬 大概是这样说的(浅显易懂,很好理解):

首先,所谓的 “字符” 具体指的是什么?如果“字符” 就是指Java中的char ,那么它就是占用了 2 字节,即 16位,如果“字符”是指我们用眼睛看到的那些 “抽象的字符”,那么,谈论它占用几个字节是没有任何意义的。

具体的来说, 脱离具体的编码谈某个字符占用几个字节是没有任何意义的。

比如: 有一个抽象的整数 “66”,你说它占用几个字节?,这就得具体看是用 byte,short,int  还是 long来存它,用byte 存就占用一个字节,用short 存就占用两个字节,int 就是四个字节,long 就是 八个字节,当然了,如果用byte ,受限于它有限的字长,有些数它是存不了的, 比如 128 就无法存放在一个byte里。

字符也是同样的一个道理,如果想谈论 它是 “ 占用几个字节 ” ,就得先把编码说清楚, 同一个字符在不同的编码下可能会占用不同的字节。

比如,就拿 ‘中’ 字为例,‘中’ 在GBK 编码下,占用 两个 字节, 在UTF-16 编码下也是占用 两个 字节,在UTF-8 编码下则是占用 三个 字节,而在UTF-32编码下则是占用的4个字节,

不同的字符在同一个编码下也可能占用不同的字节

'中' 字符在UTF-8 编码下占用3个字节,而 'a' 字符 在UTF-8 编码下则是占用 1 个字节。(其根本原因就是 UTF-8 它是变长的编码方案, 1 ~ 4 字节)

Java 中的char 本质上是 UTF-16 编码,而UTF-16 最开始是定长的2字节,但是后来也变成了变长的编码(2字节 ,4字节)。

如果一个抽象的字符在UTF-16 编码下占用 4 个字节,显然它是不能放到 char 中的,换句话说,char 中只能存放 UTF-16 编码下那些值占用 2 个字节的那些字符。

而String.getBytes 实际上是做编码转换,所以应该显式的传入一个 参数来指定编码,否则它就会还是用缺省编码来转换,

比如说: “new String("中").getBytes().length 返回的是 3 ” ,这就说明缺省编码是 UTF-8 ,

如果显示的传入一个编码参数,比如 “ new String("中").getBytes("GBK").length 返回的是 3”,那么它返回的就是 2 。

可以在启动 JVM 是设置一个缺省编码,

比如类叫 CharTest ,那么在命令行中用 java 执行这个类的时候可以通过 file.encoding 参数设置一个缺省编码,比如这样: java -Dfile.encoding=GBK CharTest 这时,在执行不带参数的getBytes() 方法的时候,“new String("中").getBytes().length 返回的是 2 了”,因为现在缺省编码变成了 GBK 了,当然了,如果这时候你显式的指定了编码,new String("中").getBytes("UTF-8").length 返回的则依旧是 3 。否则,会使用所在操作系统环境下的缺省编码。

通常,Windows系统下是GBK,Linux 和 Mac 下是UTF-8 ,但是有一点需要注意的是,在Windows下用IDE来运行时,比如Eclipse,或者Intellij IDEA,如果工程的缺省编码是 UTF-8,在IDE中运行程序的时候,会加上上述的 -Dfile.encoding=UTF-8参数,这时,即便是在Windows下,缺省编码仍然是 UTF-8,而不是GBK !由于受启动参数及其所在操作系统环境的影响,不带参数的getBytes 方法通常是不建议使用的,最好就是显示地指定参数一次获得稳定的预期行为。

显而易见,他的意思就是我接下来的操作。(注意事项:值得一提的是!如果是用命令行运行的话,如果在IDE集成开发环境的默认编码是UTF-8需要转为ANSI编码,如果默认的编码是GBK 就不用!(ANSI 实际上就是获取当前系统的默认编码字符集!比如中文版的 Windows 就是 GBK!比如我这里用Notepad++转换的,,否则编译不会通过的!比如:)都还需要把在IDE 集成开发环境里面写好的Java文件里面的包名去掉),再进行编译运行,否则编译通过,但是运行失败!比如:再运行的就会运行成功了,比如!中文版的 Windows 的命令行的默认编码字符集都是 GBK,(除了中国台湾,中国香港 和 中国澳门的以外,他们的默认编码是 BIG-5),因此不用设置java -Dfile.encoding=GBK CharTest 在执行不带参数的getBytes() 方法的时候 “new String("中").getBytes().length 返回的一样的也是 2 !”,如果是java -Dfile.encoding=utf-8 CharTest , 在执行不带参数的getBytes() 方法的时候 “new String("中").getBytes().length 返回的一样的同样是 3,比如: ,如果是java -Dfile.encoding=utf-16be CharTest , 在执行不带参数的getBytes() 方法的时候 “new String("中").getBytes().length 返回的一样的同样是 2,比如:换言之,就是在windows的命令行中进行运行Java文件的话,编码字符集必须是 GBK 才能编译通过!且这个Java文件不能有包名!(相关释义:1:utf-16be 就是utf-16的一种存储字符的方式,叫大端存储法。而utf-16le就是小端存储法。utf-16默认的就是utf-16le,因此可以理解为 utf-16 等同于 utf-16le:。2:-Dfile.encoding 的意思就是 -DefaultFile.encoding,3:编码字符集的名称不区分大小写! )这种操作再刚开始学习Java的时候可能会用到,但企业级开发这种情况是不会出现的,我不可能一个文件一个文件去执行javac 命令,再执行java命令,这也是为什么 IDE 集成开发环境对于编程的重要性,如果谁拿Notepad++写项目,或者拿记事本写项目,不要多想,那么这个人不是大佬,就是傻子。)

(这个设置默认的缺省编码,在IDE 智能集成开发环境可以通过settings选项设置的,这样所有的Java文件的默认编码就都是你设置的缺省编码了!)比如我用的Intellij IDEA 集成开发环境设置的

所以我的缺省编码就是 UTF-8 ,故所有的 字符都会按照 UTF-8 的编码格式进行编码存储!

下面是关于 Create UTF-8 files: 几个选项的说明:

注意事项: 如果设置了默认的缺省编码为 UTF-8 的时候,千万不要设置 Create UTF-8 files 的选项为 with BOM,它是为了支持 UTF-16和 UTF-32才选择的选项!如果当缺省编码为UTF-8的时候,却设置了 Create UTF-8 files with BOM ,编译就会报错!要清楚的是 UTF-8 的编码方式没有 采用 BOM 这种编码方式,即(大端法存储和小端法存储!),它是变长的编码方式,其中每种编码方式都是有前缀的!


第二位大佬大概是这样说的:(感觉他的文字能给人一种 "服了" 的感觉,我看了之后刚开始也是懵了,后来慢慢理解了,简直就是醍醐灌顶!)

主要区分清楚 内码(internal encoding) 和 外码 (external encoding)就好了。

内码:是程序内部使用的 字符编码,特别是某种语言在实现其 char 或 String 类型在内存里用的内部编码,

外码:是程序与外部交互是外部使用到的字符编码,“外部” 相对 “内部” 而言;不是char 或 String 在内存里用的内部编码的地方 都可以认为是 “外部”, 比如,外部可以是序列化之后的char 或者 String, 或者外部的文件,命令行参数之类的。

Java 语言规范规定,Java 中的 char 类型是 UTF-16 的 code unit ,也就是一定是 16 位 (2字节);

char, whose values are 16-bit unsigned integers representing UTF-16 code units (§3.1).

翻译过来就是: char,其值是表示UTF-16代码单元的16位无符号整数。

 

然后字符串是UTF-16 code unit 的序列:

The Java programming language represents text insequences of 16-bit code units,using the UTF-16 encoding.

翻译过来就是:Java编程语言表示16位代码单元的文本序列,使用UTF-16编码。

 

这样,Java规定了字符的内码要用UTF-16编码,或者至少要让用户无法感知到String内部采用了非UTF-16的编码。“感知” 可以是多方面的,例如随机访问某个下标(索引)的code unit (String.charAt())应该是 O(1) 操作,这只有使用UTF-16或者别的“定长”编码才可以做到,注意这里说的“定长” 特指code unit定长,而不是说code point定长。

 

String.getBytes() 是一个用于将String的内码转换为指定的外码的方法,无参数版使用平台的默认编码作为外码,有参数使用参数指定的编码作为外码;将String的内容用外码编码好,其结果放在一个新的byte[] 数组中返回。

比如,

new String("中").getBytes().length;  

返回的是 3,  那么外码显然就是 UTF-8,那么调用了String.getBytes()之后得到byte[] 只能表明该外码的性质,而无法触碰到String内码的任何性质。

另举一例:

Java 标准库实现的对char与String的序列化规定使用UTF-8作为外码,Java的Class文件中的字符串常量与符号名字也都规定用UTF-8编码,这大概是当时设计者们为了平衡运行时的时间效率(采用定长编码的UTF-16)与外部存储的空间效率,(采用变长的UTF-8编码),而做的取舍。

题外话one:

可惜UTF-16在Java设计之初还是真的定长编码,后来Unicode 涵盖的字符变多了之后UTF-16就变成了坑爹的变长编码(一个完整的“字符”,是一个code point;一个code point可以对应 1 到 2个code unit;一个code unit是16位),Java 也只好跟进,为了实现UTF-16的变长编码语义,Java规定char 仍然只能是一个16位的code point,也就说 Java 的 char 类型不一定能表示一个 UTF-16 的“字符”,—— 只有只需1个code unit的code point才可以完整的存在char里但是String 作为char的序列,可以包含由两个code unit组成的 “surrogate pair”(代理对)来表示需要2个code unit表示的UTF-16 code point,为此Java的标准库新加了一套用于访问code point的API,而这套API就表现出了UTF-16的变长特性。

 

题外话two:

前面说Java 的内码是说的比较松,留下了“不总是使用UTF-16做为内码,但是用户无法感知区别”的余地,在 Sun JDK6中有个“压缩字符串”(-XX:+UseCompressedString)的功能,启用后,String内部存储字符串内容可以用byte[],也可能用char[] ;当整个字符串所有字符都在ASCII编码范围内时,就使用byte[] (ASCII序列)来存储,此时字符串就处于“压缩”状态;反之,只要有任何一个字符超出了ASCII的编码范围,就退回到用char[] (UTF-16序列)来存储。ASCII编码也是一种定长编码,而且其涵盖的字符是UTF-16的真子集;用户在对一个“压缩”的字符串访问其内容是(例如String.charAt()),只需对ASCII 字符做无符号扩展就可以得到对应的UTF-16 code unit,这样用户也就无法感知到Java String的内码不是UTF-16的情况。

Sun JDK6 对“压缩字符串”的实现不够理想,实现太复杂而效果未如预期的好,所以没有包含在OpenJDK6,Oracle JDK7/OpenJDK7中。现在Oracle在重新审视“压缩字符串”的功能,有可能在JDK9重新实现出来。

题外话three:

同样规定使用UTF-16作为内码的JavaScript语言,其实现广泛应用了“压缩字符串”的思想,现在主流的JavaScript V8引擎都会尽可能的用ASCII内码的字符串,不过用户能接触的API只能看到UTF-16的code unit

 

上文中所述的名词释义:

  1. 内码:内码是指计算机汉字系统中使用二进制字符编码,是沟通输入,输出与系统平台之间的交换码,通过内码可以达到通用和高效率传输文本的目的。是汉字在计算机内部存储,处理和传输用的信息编码,它必须与ASCII码兼容但是又不能冲突!所以把国标码两个字节的最高位置“1”,以区分西欧文,这就称之为内码。
  2. 外码:外码是相对于内码而言的词汇,在计算机科学及相关领域汇总,外码指的是“外在的”,经过学习之后,可直接了解的编码形式(比如:文字和语言符号)中文输入法对汉字的编码即属于外码,常见的中文外码有仓颉码,行列码,大易码,呒虾米码,注音码,拼音码(这些中文外码都有对应的输入法的),汉字的输入码即称之为“外码”。输入码就是值我们输入汉字是所用的编码,常见的外码分为数字编码(比如:Unicode的区位码),拼音编码和字形编码(比如:五笔)
  3. code unit(码元):Unicode使用16位二进制来存储文字,我们将一个16位的二进制编码叫做一个码元(code unit)
  4. code point(码点):后来,Unicode对文字编码进行了扩展,将某些文字扩展到了32位(占用两个码元),并且,将某个文字对应的二进制数字叫做码点(code point),一个码点可以拥有一个码元(code unit),也可以拥有两个码元(code unit)

 

内码的分类(即权威机构制定好的编码字符集):

  1. 字符编码:字符编码就是以二进制的数字来对应字符集的字符,用得最普遍的字符集是ANSI,对应ANSI字符集的二进制编码就称为ANSI码,DOS和Windows系统都使用了ANSI码,但在系统中使用的字符编码要经过二进制转换,称为系统内码。
  2. 字符编码:字符编码就是以二进制的数字来对应字符集的字符,用得最普遍的字符集是ANSI,对应ANSI字符集的二进制编码就称为ANSI码,DOS和Windows系统都使用了ANSI码,但在系统中使用的字符编码要经过二进制转换,称为系统内码。这些编码使用单字节来表示ANSI的英文字符(即兼容ANSI码),使用双字节来表示汉字字符。由于一个系统中只能有一种汉字内码,不能识别其它汉字内码的字符,造成了交流的不便。
  3. GB码:GB码是1980年国家公布的简体汉字编码方案,在大陆、新加坡得到广泛的使用,也称国标码。国标码对6763个汉字集进行了编码,涵盖了大多数正在使用的汉字。
  4. GBK码:GBK码是GB码的扩展字符编码,对多达2万多的简繁汉字进行了编码,简体版的Win95和Win98,Win10都是使用GBK作系统内码。
  5. BIG5码:BIG5码是针对繁体汉字的汉字编码,在台湾、香港的电脑系统中得到普遍应用。
  6. HZ码:HZ码是在Internet上广泛使用的一种汉字编码。
  7. ISO-2022CJK码:ISO-2022是国际标准组织(ISO)为各种语言字符制定的编码标准。采用二个字节编码,其中汉语编码称ISO-2022 CN,日语、韩语的编码分别称JP、KR。一般将三者合称CJK码。CJK码主要在Internet网络中使用。
  8. Unicode:Unicode只是一种字符集,它是规定了字符的编码是多少,并没有规定字符应该如果存储,它的编码方案有 UTF-8,UTF-16,UTF-32,其中UTF-16和UTF-32与ANSI码不兼容。Unicode字符集在网络、Windows系统和很多大型软件中得到应用。而内码在编程语言中使用得最多的就是UTF-16这个编码方案了。

 

这篇文章对你有帮助吗?作为一名程序工程师,在评论区留下你的困惑或你的见解,大家一起来交流吧!

如果能想到 Unicode的 UCS-2、UCS-4 这种古老的字符集,可以参考我转载的其他博主的优秀文章:https://blog.csdn.net/flagyili/article/details/116312325

 

 

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值