Java中的一个char采用的是Unicode编码集,占用两个字节,而一个中文字符也是两个字节,因此Java中的char是可以表示一个中文字符的。

但是在C/C++中由于采用的字符编码集是ASCII,只有一个字节,因此是没办法表示一个中文字符的。

解答了上面的浅显易懂的问题之后,下面彻底理清楚字符 字节以及编码的原理。
其实关于编码以及字节的问题,在腾讯实习生一面的时候也问到过,当时搞不懂面试官为什么会问这个问题,现在想想,这个问题还是很考验一个人的思考以及钻研深度的,而且这个问题远远比自己想象的复杂。

字符编码

编码的三个阶段:

单字节字符 – ASCII
多字节字符 – ANSI
宽字节字符 – UNICODE

字符 字节 字符串

从第一个表和第二个表中可以看出来,任何字符再Unicode编码中都是两个字节进行标识的,而且世界上任何语言的字符的Unicode编码都是不一样的,因此不论计算机在什么样的语言环境下,都是可以正常显示字符的。但是ANSI每个字符长度却是不定的,这样一个字符该如何解释是和具体的计算机环境(具体的编码规则)有关的。

字符集与编码

各个国家和地区所制定的不同 ANSI 编码标准中,都只规定了各自语言所需的“字符”。比如:汉字标准(GB2312)中没有规定韩国语字符怎样存储。这些 ANSI 编码标准所规定的内容包含两层含义:

  1. 使用哪些字符。也就是说哪些汉字,字母和符号会被收入标准中。所包含“字符”的集合就叫做“字符集”
  2. 规定每个“字符”分别用一个字节还是多个字节存储,用哪些字节来存储,这个规定就叫做“编码”

各个国家和地区在制定编码标准的时候,“字符的集合”和“编码”一般都是同时制定的。因此,平常我们所说的“字符集”,比如:GB2312, GBK, JIS 等,除了有“字符的集合”这层含义外,同时也包含了“编码”的含义。

“UNICODE 字符集”包含了各种语言中使用到的所有“字符”。用来给 UNICODE 字符集编码的标准有很多种,比如:UTF-8, UTF-7, UTF-16, UnicodeLittle, UnicodeBig 等

“编码”的概念就是把“字符”转化成“字节”但是每一种编码对应着不同的转换规则,如果不遵循规则,后果就是乱码。

针对语言的字符和字节

就像开始所讨论的一个char是否可以表示一个中文字符的时候一样,如果只是把char看做一种类型的话,那是否可以表示中卫字符取决于语言。而按此处的以语言进行划分,就可以很容易看出两种主流语言中字符与字节的差异了 – 在C++中char被看做是字节而在Java中被看做是字符。

编码理解误区

第一种误解,往往是导致乱码产生的原因
即采用每“一个字节”就是“一个字符”的转化方法,实际上也就等同于采用 iso-8859-1 进行转化。因此,我们常常使用 bytes = string.getBytes(“iso-8859-1”) 来进行逆向操作,先得到原始的“字节串”。然后再使用正确的 ANSI 编码,比如 string = new String(bytes, “GB2312”),来得到正确的“UNICODE 字符串”

第二种误解,往往导致本来容易纠正的乱码问题变得更复杂

非 UNICODE 程序中的字符串,都是以某种 ANSI 编码形式存在的。如果程序运行时的语言环境与开发时的语言环境不同,将会导致 ANSI 字符串的显示失败
比如,在日文环境下开发的非 UNICODE 的日文程序界面,拿到中文环境下运行时,界面上将显示乱码。如果这个日文程序界面改为采用 UNICODE 来记录字符串,那么当在中文环境下运行时,界面上将可以显示正常的日文。

乱码场景

一个web开发初学者必定会遇到的乱码问题及原理

当然这个问题在Android开发中也是经常遇到,尤其是在不注意设置UTF-8字符集的情况下,提交的都是GBK但是服务器却按ISO8859-1来解释

当页面中的表单提交字符串时,首先把字符串按照当前页面的编码,转化成字节串。然后再将每个字节转化成 “%XX” 的格式提交到 Web 服务器。比如,一个编码为 GB2312 的页面,提交 “中” 这个字符串时,提交给服务器的内容为 “%D6%D0”。

在服务器端,Web 服务器把收到的 “%D6%D0” 转化成 [0xD6, 0xD0] 两个字节,然后再根据 GB2312 编码规则得到 “中” 字。

在 Tomcat 服务器中,request.getParameter() 得到乱码时,常常是因为前面提到的“误解一”造成的。默认情况下,当提交 “%D6%D0” 给 Tomcat 服务器时,request.getParameter() 将返回 [0x00D6, 0x00D0] 两个 UNICODE 字符,而不是返回一个 “中” 字符。因此需要使用 bytes = string.getBytes(“iso-8859-1”) 得到原始的字节串,再用 string = new String(bytes, “GB2312”) 重新得到正确的字符串 “中”

数据库中字符串类型数据的存储

这个乱码场景也比较容易遇到。举个最简单的例子是当字段的值含有中文的时候,如果存的时候与取的时候没有显式的指定编码集,一般都会乱码。比如存的时候按GBK或者GB2312这种默认字符集去存储,但是解析显示的时候却默认的时候8859-1,自然就产生了乱码。一个比较好的习惯当然是在配置环境的时候都设置编码集为UTF-8。

Unicode和UTF-X之间的关系

Unicode的实现方式不同于编码方式。一个字符的Unicode编码是确定的。但是在实际传输过程中,由于不同系统平台的设计不一定一致,以及出于节省空间的目的,对Unicode编码的实现方式有所不同。Unicode的实现方式称为Unicode转换格式(Unicode Transformation Format,简称为UTF)

例如,如果一个仅包含基本7位ASCII字符的Unicode文件,如果每个字符都使用2字节的原Unicode编码传输,其第一字节的8位始终为0。这就造成了比较大的浪费。对于这种情况,可以使用UTF-8编码,这是一种变长编码,它将基本7位ASCII字符仍用7位编码表示,占用一个字节(首位补0)。而遇到与其他Unicode字符混合的情况,将按一定算法转换,每个字符使用1-3个字节编码,并利用首位为0或1进行识别。这样对以7位ASCII字符为主的西文文档就大幅节省了编码长度(具体方案参见UTF-8)。类似的,对未来会出现的需要4个字节的辅助平面字符和其他UCS-4扩充字符,2字节编码的UTF-16也需要通过一定的算法进行转换。

UTF-8就是在互联网上使用最广的一种Unicode的实现方式。其他实现方式还包括UTF-16(字符用两个字节或四个字节表示)和UTF-32(字符用四个字节表示),不过在互联网上基本不用.

UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

public class CharTest {public static void main(String[] args) throws UnsupportedEncodingException {String str = "hello中国";int byteLen = str.getBytes("utf-8").length;int strLen = str.length(); System.out.println(System.getProperty("file.encoding"));System.out.println("byteLen : " + byteLen);System.out.println("strLen  : " + strLen);}}

分别将上述的str.getBytes(“utf-8”)设置为gbk utf-8 utf-16 utf-32就可以看出每种编码方式中,一个中文字符所占用的字节数是不一样的。英文字符所占字节数也是稍微不同的。

总结如下:
在 GB 2312 编码或 GBK 编码中,一个英文字母字符存储需要1个字节,一个汉字字符存储需要2个字节。
在UTF-8编码中,一个英文字母字符存储需要1个字节,一个汉字字符储存需要3到4个字节。
在UTF-16编码中,一个英文字母字符存储需要2个字节,一个汉字字符储存需要3到4个字节(Unicode扩展区的一些汉字存储需要4个字节)。
在UTF-32编码中,世界上任何字符的存储都需要4个字节。

UTF-8的编码规则:

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

Notepad++中进行编码格式(ANSI,Unicode,Unicode big endian 和 UTF-8)的设置,然后使用UltraEdit进行16进制格式查看,就可以看出不同编码方式下,同一个中文字符的编码不同,字节数也不同。涉及到大小端问题还会导致顺序的不同。后面的参考链接二中会有详细的解释

UTF-8 BOM和无BOM的区别
BOM(byte order mark)是为 UTF-16 和 UTF-32 准备的,用于标记字节序(byte order)
还是一样的,按上述的方式在notpad++中设置编码格式然后在ultraedit中使用16进制进行查看就会发现默认utf-8的时候直接是

无bom的时候是:

一般还是直接使用无bom的编码

大小端的问题

既然涉及到了字节,自然就涉及到大小端的问题。
在计算机系统中,以字节为基本存储单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8bit的char之外,还有16bit的short型,32bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。

大小端定义:
1) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
2) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

网络传输一般采用大端序,也被称之为网络字节序,或网络序。IP协议中定义大端序为网络字节序。Berkeley套接字定义了一组转换函数,用于16和32bit整数在网络序和本机字节序之间的转换。htonl,htons用于本机序转换到网络序;ntohl,ntohs用于网络序转换到本机序。
关于网络字节序,Unix高级网络编程中有详细的解释

参考链接:
https://zh.wikipedia.org/wiki/Unicode
http://www.regexlab.com/zh/encoding.htm
http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
http://blog.csdn.net/ce123/article/details/6971544

【Java基础】Java中的char是否可以存储一个中文字符之理解字符字节以及编码集相关推荐

  1. Java的char数据类型存储一个中文字符

    对于Java语法以及JVM框架,实际上是用之,而不是先掌握,所以对一些基础性概念总是存在偏颇认识. 比如对于这个char类型来说,一直以为是一个字节的变量,那自然不能存储一个中文字符(2个字节),这是 ...

  2. Java里的char类型能不能存储一个中文字符?

    对于这道题,绝大多数的答案都是"可以存储".给出的原因包括: java中的char是unicode存储,unicode编码字符集中包含了汉字,所以可以存储中文: java内部其实是 ...

  3. char型变量能不能存储一个中文汉字重写和重载的规则

    char型变量能不能存储一个中文汉字(为什么) char类型可以存储一个中文汉字 因为java中使用的编码是Unicode格式,而一个char类型占2个字节(16比特),所以当一个中文汉字是没有问题的 ...

  4. Java基础-Java中的堆内存和离堆内存机制

    Java基础-Java中的堆内存和离堆内存机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 转载于:https://www.cnblogs.com/yinzhengjie/p/9 ...

  5. Java基础-JAVA中常见的数据结构介绍

    Java基础-JAVA中常见的数据结构介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是数据结构 答:数据结构是指数据存储的组织方式.大致上分为线性表.栈(Stack) ...

  6. Java基础-Java中的内存分配与回收机制

    Java基础-Java中的内存分配与回收机制 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一. 二. 转载于:https://www.cnblogs.com/yinzhengji ...

  7. [重学Java基础][Java IO流][Exter.2]IO流中几种不同的读写方法的区别

    [重学Java基础][Java IO流][Exter.2]IO流中几种不同的读写方法的区别 Read 读入方法 read(): 一般是这种形式 public int read() 1.从流数据中读取的 ...

  8. Java基础-Java中常用的锁机制与使用

    Java基础-Java中常用的锁机制与使用 锁lock或互斥mutex是一种同步机制,主要用于在存在多线程的环境中强制对资源进行访问限制.锁的主要作用为强制实施互斥排他以及并发控制策略.锁一般需要硬件 ...

  9. Java基础——Java多继承的三种实现方式

    Java基础--Java"多继承"的三种实现方式 Java语言本身只支持单继承(每个类只能有一个父类),但单继承的局限性很大,因此,可以通过以下的方式实现"多继承&quo ...

最新文章

  1. pandas使用drop函数删除dataframe中指定索引列表对应位置的数据行(drop multiple rows in dataframe with integer index)
  2. linux 下mysql的管理,Linux下 MySQL安装和基本管理
  3. Hadoop系列一:Hadoop集群分布式部署
  4. CPU实模式和保护模式、全局描述符表GDT、Linux内核中GDT和IDT的结构定义
  5. 经纬度坐标系与UTM MGRS坐标系之间的转换 c# 版本
  6. 实战 MDT 2012(六)---基于MAC地址的部署
  7. thinkphp 3.2 unionall
  8. Dubbo2.6.5入门——管控台的安装
  9. 随想录(qemu仿真linux kernel)
  10. python实现语义分割_如何用PyTorch进行语义分割?一文搞定
  11. 通过cacti+nagios监控服务器的运行—nagios nrpe
  12. 澳大利亚通信软件服务公司 Whispir 完成1175万美元 A 轮融资
  13. ORACLE的数据类型的长度合集
  14. python实现bt下载器_使用Python实现BT种子和磁力链接的相互转换
  15. 优科网络:WiFi价值正在回归
  16. java中国象棋棋子走法,中国象棋的规则及各种棋子的走法介绍
  17. 2019很艰难,2020会更好吗
  18. at89c2051 定时器用法 c语言编程资料,用AT89C2051单片机制作的数字电容表.doc
  19. linux创建文件内容三行,Linux 文本处理三剑客
  20. oracle cux,EBS增加客制应用CUX:Custom Application

热门文章

  1. 计算机专业活动简报,计算机系辩论赛活动简报
  2. 观念什么意思_俗语“女怕午时生,男怕子夜临”是啥意思?古人的忌讳有道理吗?...
  3. C++ fscanf函数分割读取文本文件
  4. pycharm配置python解释器_Python大佬手把手教你进行Pycharm活动模板配置
  5. OPENCV已知内参求外参
  6. 虚拟服务器目录,服务器虚拟主机目录
  7. 如何在geth中创建genesis.json_如何在Photoshop中应用“通道混合器”创建一个复古韵味色调...
  8. pyqt5 qscrollarea到达_在PYQT5中QscrollArea(滚动条)的使用方法
  9. 一种轻量级的C4C业务数据同步到S/4HANA的方式:Odata通知
  10. hihocoder1513 小Hi的烦恼