本文要点:

  1. java的内码为UTF-16;

  2. char类型无法处理所有的字符,String的length方法和charAt方法也无法处理所有的字符;

  3. MySQL中,使用utf8编码的表无法存储表情,需要使用utf8mb4。

一、字符编码

字符编码有一个发展过程。

  • 最初的ASCII码,使用1个byte,表示出英文中所有需要的字符(缺点:非英文的字符都无法表示)

  • 非英语系的国家,分别做出各种字符编码,满足本国的信息处理需求。例如我国的GB2312,GB18030(缺点:使用错误的编码打开一个文件,就是一堆乱码)

  • 使用统一的编码表示世界上所有的字符,由此产生了unicode

二、Unicode

参考资料:维基百科Unicode词条 https://zh.wikipedia.org/wiki/Unicode

unicode本身也有一个发展历程。最开始的unicode,使用2个byte表示1个字符,整个字符集可以容纳65536个字符。尔后,发现这仍然不够用,于是扩展到了使用4个byte表示一个字符。

在表示一个Unicode字符的时候,会用“U+”开头,后面跟着十六进制的数字,例如“字”的编码就是U+5B57。

每FFFF个编码,称作一个“位面”。U+0000到U+FFFF的这些字符,称作“基本位面”,也称作“0号位面”,简写作BMP-Basic Multilingual Plane。目前启用的位面有17个,编码从U+0000到U+10FFFF。

基本位面中,U+D800 ~ U+DFFF用作“代理字符”,不用来单独使用,下面会说到。

注意:为什么我从苹果手机上发送的表情,到了华为手机上,就有了一些差异呢?

想想,如果苹果手机上用的是“华文黑体”,华为手机上用的是“方正楷体”,一个字传过去,写法必定会产生一些差异,但是这仍然是一个字,只是把这个字“绘制成图像”的过程不一样,这正是字体起到的作用。

可以认为,不同的手机厂商,对每个编码绘制的“表情”也不一样,这也可以看做“表情符号”的字体。

三、Unicode的实现

但是,计算机世界中大部分资料都是英文的,使用1个byte表示就足够了。如果每个字符都使用4个byte表示,浪费存储空间,也浪费传输带宽。

于是,unicode在实际使用中,又发展出了3种编码。这些编码都是基于字符原有Unicode编码的再编码。

(下面的编码,暂时先不考虑大端小端的问题)

UTF-8

UTF-8是互联网界广泛使用的编码,其特点为不定长。

范围

Unicode编码(Binary)

UTF-8编码(Binary)

UTF-8编码byte长度

U+0000 ~ U+007F

00000000 00000000 00000000 0ZZZZZZZ

0ZZZZZZZ

1

U+0080 ~ U+07FF

00000000 00000000 00000YYY YYZZZZZZ

110YYYYY 10ZZZZZZ

2

U+0800 ~ U+D7FF

U+E000 ~ U+FFFF

(排除掉“代理字符”)

00000000 00000000 XXXXYYYY YYZZZZZZ

1110XXXX 10YYYYYY 10ZZZZZZ

3

U+010000 ~ U+10FFFF

00000000 000WWWXX XXXXYYYY YYZZZZZZ

11110WWW 10XXXXXX 10YYYYYY 10ZZZZZZ

4

由上表可见,ASCII范围内的字符,在UTF-8中只占1个byte,编码完全一样。互联网世界大部分资料都是英文,使用UTF-8大大降低了存储、传输需要的字符数量。

UTF-8将“基本位面”内的字符编为1~3个长度,“基本位面”以外的,就需要4个了。随着Unicode字符集的继续扩大,使用5个、甚至6个byte表示一个字符,也不是不可能。

UTF-16

在Unicode编码只占2个byte的时候,UTF-16与Unicode编码是完全相同的。

但是Unicode扩展到4字节的时候,UTF-16也随之做出了一些变化,由“定长码”变成了“变长码”。

范围

Unicode编码(Binary)

UTF-16编码(Binary)

UTF-16编码byte长度

U+0000 ~ U+D7FF

U+E000 ~ U+FFFF

(排除掉“代理字符”)

00000000 00000000 ZZZZZZZZ ZZZZZZZZ

ZZZZZZZZ ZZZZZZZZ

2

U+010000 ~ U+10FFFF

00000000 0000YYYY YYYYYYZZ  ZZZZZZZZ

110110YY YYYYYYYY 110111ZZ ZZZZZZZZ

4

新的UTF-16需要兼容老的UTF-16。

由此可见“代理字符”的作用了。大于U+FFFF的字符,在表示为UTF-16编码时,必须使用2个2byte。其中:

  • 第一个必然在D800~DBFF之间,称为“高位代理字符”

  • 第二个必然在DC00~DFFF之间,称为“低位代理字符”

处理程序在处理UTF-16的字节流时,每次读入2个byte。

  • 如果不是“代理字符”,这2个byte就表示一个合法的字符

  • 如果是“高位代理字符”,再读取2个byte,这两个byte应当是“低位代理字符”,合起来才是一个一个合法的字符。

UTF-32

UTF-32不需要多讲,与4字节的Unicode编码完全相同,目前是定长码。

四、java的char类型

程序设计语言中的编码,分为“内码”和“外码”。

  • 内码倾向于使用定长码,便于处理(类似于线性表一样,方便寻址)

  • 外码倾向于使用变长码,变长码将常用字符编为短编码,罕见字符编为长编码,节省存储空间与传输带宽

java中char和byte是两种不同的类型。1个char占用2个byte。java语言的内码为UTF-16。

java被设计出来的时候,unicode使用2个byte表示一个字符,因此char类型可以处理所有的字符。但是现在,有的字符用1个char表示不了,char类型已无法处理所有字符。因此,有文章建议,java中谨慎使用char类型处理字符。

char类型可以这样被赋值:char ch = 'a';  char ch = '\u3456'; 后一种就是将字符的unicode编码赋给char变量。

以下是java在线文档中,对Character(char包装类)的说明:

Unicode Character Representations

The char data type (and therefore the value that a Character object encapsulates) are based on the original Unicode specification, which defined characters as fixed-width 16-bit entities. The Unicode Standard has since been changed to allow for characters whose representation requires more than 16 bits. The range of legal code points is now U+0000 to U+10FFFF, known as Unicode scalar value. (Refer to the definition of the U+n notation in the Unicode Standard.)

The set of characters from U+0000 to U+FFFF is sometimes referred to as the Basic Multilingual Plane (BMP)Characters whose code points are greater than U+FFFF are called supplementary characters. The Java platform uses the UTF-16 representation in char arrays and in the String and StringBuffer classes. In this representation, supplementary characters are represented as a pair of char values, the first from the high-surrogatesrange, (\uD800-\uDBFF), the second from the low-surrogates range (\uDC00-\uDFFF).

A char value, therefore, represents Basic Multilingual Plane (BMP) code points, including the surrogate code points, or code units of the UTF-16 encoding. An int value represents all Unicode code points, including supplementary code points. The lower (least significant) 21 bits of int are used to represent Unicode code points and the upper (most significant) 11 bits must be zero. Unless otherwise specified, the behavior with respect to supplementary characters and surrogate char values is as follows:

  • The methods that only accept a char value cannot support supplementary characters. They treat char values from the surrogate ranges as undefined characters. For example, Character.isLetter('\uD840') returns false, even though this specific value if followed by any low-surrogate value in a string would represent a letter.

  • The methods that accept an int value support all Unicode characters, including supplementary characters. For example, Character.isLetter(0x2F81A) returns true because the code point value represents a letter (a CJK ideograph).

In the Java SE API documentation, Unicode code point is used for character values in the range between U+0000 and U+10FFFF, and Unicode code unitis used for 16-bit char values that are code units of the UTF-16 encoding. For more information on Unicode terminology, refer to the Unicode Glossary.

由标红的语句可见,对于大于FFFF的字符,需要两个char来表示,不然就要借助int类型。接收char类型的函数无法处理补充字符。

String中其实有一个char[]字段,遇到大于FFFF的字符,是用2个char来存储的。

由此来说,一个char就不完全等效于一个“字符”了,因为大于FFFF的字符需要两个char来表示,这其中每一个char都不是一个完整的字符,只是一个“高位代理字符”或者“低位代理字符”。

java中有一个概念,codePoint(代码点),由一个int来表示,目前,一个代码点可以表示一个完整的字符。

举几个例子,如果要处理更多的字符,以下几个地方是需要注意的:

  • String类中的charAt方法,只是从char数组中取出某个char。如果这个char是一个代理字符,可能引起后续的问题。因此,如果需要处理更多的字符,应当使用codePoint方法,返回一个int值。

  • String类中的length方法,返回的仅仅是char数组的长度,这不等于字符长度。如果需要处理更多的字符,应当使用codePointCount方法,得到codePoint的数量。

引申出一个问题:java源代码文件也是有编码的,java编译器是怎样知道根据哪种编码来读取源代码的呢?

查阅资料可得:javac先使用系统默认编码进行编译,也可以通过-encoding参数来指定编码。因此不管是UTF-8源代码中的汉字,还是GBK中的汉字,虽然在源代码文件中的编码不一样,但是经过javac编译之后,都是以UTF-16的格式存储的。

char类型与字符编码相关推荐

  1. 解析JavaScript中的字符串类型与字符编码支持

    JavaScript中的字符串也像Python那样支持反斜杠的转移,并且字符集方面默认为Unicode,下面就来详细解析JavaScript中的字符串类型与字符编码支持 定义 字符串就是零个或多个排在 ...

  2. Java中char类型与Unicode编码

    char是字符型,在Java中规定为2个字节.同样地,目前全球通用的字符集编码Unicode码,最常见的也是用2个字节表示字符. 其实,Java中可以通过字符型和整型的相互强制类型转换,实现由字符得出 ...

  3. C++ char 类型:字符型和最小的整型

    C++ 中没有 byte,Java 中有 byte. 但是 C++ 有 char,char 是可用来放整数的最小字节类型. #include <iostream>int main() {u ...

  4. 2.6. 字符类型与字符编码

    字符常量或字符型变量也可以当作整数参与运算,例如: printf("%c\n", 'a'+1); 执行结果是b. 符号在计算机内部也用数字表示,每个字符在计算机内部用一个整数表示, ...

  5. Python02-数据类型、字符编码、文件处理

    一 引子 二 数字 三 字符串 四 列表 五 元组 六 字典 七 集合 八 数据类型总结 九 字符编码 十 作业 一 引子 1 什么是数据? x=10,10是我们要存储的数据 2 为何数据要分不同的类 ...

  6. Day2-数据类型、字符编码、购物车

    一.引子 1.什么是数据? x=10,10是我们要存储的数据. 2.为何数据要分不同的类型? 数据是用来表示状态的,不同的状态就应该用不同的类型的数据去表示. 3.存在哪些数据类型 数字(整形,长整形 ...

  7. New-Python-数据类型、字符编码、文件处理

    一.字符串的一些应用: #strip name='*egon**' print(name.strip('*')) print(name.lstrip('*')) print(name.rstrip(' ...

  8. Oracle bug 使用max或min函数into到一个char类型报字符缓冲区太小的错误

    这个BUG出现会报错如下: selectto_char(max(RENEWAL_DATE)) intoM_YEAR_MONTH fromt_renewal_schedule; ORA-06502: P ...

  9. 各种字符编码与Char字符

    目录 一.字符集与字符编码 二.常见的字符集与其对应的字符编码 三.Char与Unicode 一.字符集与字符编码 1.字符集:各种字符的集合. 2.字符编码:字符集中每个独立的字符对应的编号 二.常 ...

最新文章

  1. mysql中char和text的区别_mysql中text与varchar与char的区别
  2. C++学习笔记-----operator=函数处理自赋值
  3. PCB设计检查表( 布局后检查一次 ; 布线完再检查一次 )
  4. Android打开jsp页面,如何确定是通过jsp页面上的Android设备还是IOS设备访问网页
  5. 【南京】.Net 开源基础服务线下技术交流会
  6. 线上服务器内存分析及问题排查
  7. 【Leetcode | 49】230. 二叉搜索树中第K小的元素
  8. linux下根据端口查进程,linux根据进程查端口,根据端口查进程
  9. Kubernetes中Pod生命周期
  10. 移动开发day4_京东移动页面
  11. MyBatis 拦截器(4)
  12. 机器学习sklearn中决策树模型参数释义
  13. Steam DS4手柄
  14. 一个简单的项目Java后端框架(springboot+maven包括shiro,验证码的生成与使用,异常处理,跨域处理)
  15. JavaScript文档注释JSDoc注释
  16. 在线matlab,web版,还是挺好用的
  17. 什么是实名域名?域名必须进行实名认证吗?
  18. Swing入门级项目全程实录第7讲
  19. java判断标签闭合_html转PDF(java)非常奇怪的错误,标签没闭合
  20. spring boot 使用redis作为cache 出现:A cannot be cast to A.使用fastJson序列化

热门文章

  1. JavaSE、JavaEE和JavaME
  2. linux看网卡百兆千兆,查看网卡是百兆还是千兆
  3. 便宜实惠的移动自动快充:50元仅需48.90元
  4. java面试题(记录与分享)二
  5. Word处理控件Aspose.Words功能演示:使用 Java 将 Word 文档转换为 Markdown
  6. shp文件中polyline是什么_shp文件的读取
  7. 日本口音英语发音规则
  8. 深度学习项目实战——木薯叶图像识别与分类项目
  9. MAC-MAC-MAC-MAC
  10. oracle语句查询时间范围