中文字符与编码格式占位问题
Posted chwilliam85
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了中文字符与编码格式占位问题相关的知识,希望对你有一定的参考价值。
最近面试,碰到的面试题。
Q:中文字符在UTF-8占几个字节?UTF-8是固定字长编码还是动态字长编码的?
A:UTF-8编码下一个中文所占字节也是不确定的。可能是2个、3个、4个字节。因此可见UTF-8是变长的编码格式的。
扩展讲解
先从字符编码讲起
1、美国人首先对其英文字符进行了编码,也就是最早的ASCII码,用一个字节的低7位来表示英文的128个字符,高1位统一为0
2、后来欧洲人发现尼玛你这128位哪够用,比如我高贵的法国人字母上面的还有注音符,这个怎么区分?得,把高1位编进来吧,这样欧洲普遍使用一个全字节进行编码,最多可表示256位。欧美人就是喜欢直来直去,字符少,编码用得位数少
3、但是即使位数少,不同国家地区用不同的字符编码,虽然0--127表示的符号是一样的。但是128--255这一段的解释完全乱套了,即使2进制完全一样,表示的字符完全不一样。比如135在法语,希伯来语,俄语编码中完全是不同的符号。更麻烦的是,尼玛这电脑高科技传到中国后,中国人发现我们有10万多个汉字,你们欧美这256字塞牙缝都不够。于是就发明了GB2312这些汉字编码,典型的用2个字节来表示绝大部分的常用汉字,最多可以表示65536个汉字字符,这样就不难理解有些汉字你在新华字典里查得到,但是电脑上如果不处理一下你是显示不出来的了吧
4、这下各用各的字符集编码,这世界咋统一?
俄国人发封Email给中国人,两边字符集编码不同,尼玛显示都是乱码啊。为了统一,于是就发明了Unicode,将世界上所有的符号都纳入其中,每一个符号都给予一个独一无二的编码,现在Unicode可以容纳100多万个符号,每个符号的编码都不一样,这下可统一了,所有语言都可以互通,一个网页页面里可以同时显示各国文字
5、然而,Unicode虽然统一了全世界字符的二进制编码,但没有规定如何存储啊,亲。x86和amd体系结构的电脑小端序和大端序都分不清,别提计算机如何识别到底是Unicode还是ASCII了。
如果Unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,文本文件的大小会因此大出二三倍,这对于存储来说是极大的浪费。
这样导致一个后果:出现了Unicode的多种存储方式
6、互联网的兴起,网页上要显示各种字符,必须统一啊,亲。UTF-8就是Unicode最重要的实现方式之一。另外还有UTF-16、UTF-32等。UTF-8不是固定字长编码的,而是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。这是种比较巧妙的设计,如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节
7、注意Unicode的字符编码和UTF-8的存储编码表示是不同的,例如"严"字的Unicode码是4E25,UTF-8编码是E4B8A5,这个7里面解释了的,UTF-8编码不仅考虑了编码,还考虑了存储,E4B8A5是在存储识别编码的基础上塞进了4E25
8、UTF-8 使用一至四个字节为每个字符编码。128 个 ASCII 字符(Unicode 范围由 U+0000 至 U+007F)只需一个字节,带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及马尔代夫语(Unicode 范围由 U+0080 至 U+07FF)需要二个字节,其他基本多文种平面(BMP)中的字符(CJK属于此类-Qieqie注)使用三个字节,其他 Unicode 辅助平面的字符使用四字节编码。
9、最后,常规来看,中文汉字在UTF-8中到底占几个字节,一般是3个字节,最常见的编码方式是1110xxxx 10xxxxxx
几种编码格式的简单介绍
- ASCII 码
学过计算机的人都知道 ASCII 码,总共有 128 个,用一个字节的低 7 位表示,0~31 是控制字符如换行回车删除等;32~126 是打印字符,可以通过键盘输入并且能够显示出来。
- ISO-8859-1
128 个字符显然是不够用的,于是 ISO 组织在 ASCII 码基础上又制定了一些列标准用来扩展 ASCII 编码,它们是 ISO-8859-1~ISO-8859-15,其中 ISO-8859-1 涵盖了大多数西欧语言字符,所有应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符。
- GB2312
它的全称是《信息交换用汉字编码字符集 基本集》,它是双字节编码,总的编码范围是 A1-F7,其中从 A1-A9 是符号区,总共包含 682 个符号,从 B0-F7 是汉字区,包含 6763 个汉字。
- GBK
全称叫《汉字内码扩展规范》,是国家技术监督局为 windows95 所制定的新的汉字内码规范,它的出现是为了扩展 GB2312,加入更多的汉字,它的编码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。
- GB18030
全称是《信息交换用汉字编码字符集》,是我国的强制标准,它可能是单字节、双字节或者四字节编码,它的编码与 GB2312 编码兼容,这个虽然是国家标准,但是实际应用系统中使用的并不广泛。
- UTF-16
说到 UTF 必须要提到 Unicode(Universal Code 统一码),ISO 试图想创建一个全新的超语言字典,世界上所有的语言都可以通过这本字典来相互翻译。可想而知这个字典是多么的复杂,关于 Unicode 的详细规范可以参考相应文档。Unicode 是 Java 和 XML 的基础,下面详细介绍 Unicode 在计算机中的存储形式。
UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。
- UTF-8
UTF-16 统一采用两个字节表示一个字符,虽然在表示上非常简单方便,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的今天,这样会增大网络传输的流量,而且也没必要。而 UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以是由 1~4 个字节组成。
UTF-8 有以下编码规则:
如果一个字节,最高位(第 8 位)为 0,表示这是一个 ASCII 字符(00 - 7F)。可见,所有 ASCII 编码已经是 UTF-8 了。
如果一个字节,以 11 开头,连续的 1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它是双字节 UTF-8 字符的首字节。
如果一个字节,以 10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节
代码示例
public class CharacterDemo { public static void main(String [] args) { String chinese = "中"; String english = "A"; String cha = "0x20001"; int c = 0x20001; CharacterDemo.characterTest(english); System.out.println("=============以上是英文字符输出信息================="); CharacterDemo.characterTest(chinese); System.out.println("=============以上是中文字符输出信息================="); CharacterDemo.characterTest(cha); System.out.println("=========================================="); CharacterDemo.binaryTest(c); System.out.println("=========================================="); } private static void characterTest(String str) { CharacterDemo.getLengthByEncodeing(str,"GB2312"); CharacterDemo.getLengthByEncodeing(str,"GBK"); CharacterDemo.getLengthByEncodeing(str,"GB2312"); CharacterDemo.getLengthByEncodeing(str,"GB18030"); CharacterDemo.getLengthByEncodeing(str,"ISO-8859-1"); CharacterDemo.getLengthByEncodeing(str,"UTF-8"); CharacterDemo.getLengthByEncodeing(str,"UTF-16"); CharacterDemo.getLengthByEncodeing(str,"UTF-16BE"); CharacterDemo.getLengthByEncodeing(str,"UTF-16LE"); } private static void binaryTest(int c) { char[] arr = Character.toChars(c); String s = new String(arr); System.out.println("char array length:" + arr.length); System.out.println("content:| " + s + " |"); System.out.println("String length:" + s.length()); CharacterDemo.getLengthByEncodeing(s, "GB2312"); CharacterDemo.getLengthByEncodeing(s, "GBK"); CharacterDemo.getLengthByEncodeing(s, "GB2312"); CharacterDemo.getLengthByEncodeing(s, "GB18030"); CharacterDemo.getLengthByEncodeing(s, "ISO-8859-1"); CharacterDemo.getLengthByEncodeing(s, "UTF-8"); CharacterDemo.getLengthByEncodeing(s, "UTF-16"); CharacterDemo.getLengthByEncodeing(s, "UTF-16BE"); CharacterDemo.getLengthByEncodeing(s, "UTF-16LE"); } private static void getLengthByEncodeing(String str, String encodingName) { try { System.out.format("编码格式:%s ", encodingName); System.out.format("字节长度:%s ",str.getBytes(encodingName).length); }catch (Exception e) { e.printStackTrace(); } } }
编码格式:GB2312 字节长度:1 编码格式:GBK 字节长度:1 编码格式:GB2312 字节长度:1 编码格式:GB18030 字节长度:1 编码格式:ISO-8859-1 字节长度:1 编码格式:UTF-8 字节长度:1 编码格式:UTF-16 字节长度:4 编码格式:UTF-16BE 字节长度:2 编码格式:UTF-16LE 字节长度:2 =============以上是英文字符输出信息================= 编码格式:GB2312 字节长度:2 编码格式:GBK 字节长度:2 编码格式:GB2312 字节长度:2 编码格式:GB18030 字节长度:2 编码格式:ISO-8859-1 字节长度:1 编码格式:UTF-8 字节长度:3 编码格式:UTF-16 字节长度:4 编码格式:UTF-16BE 字节长度:2 编码格式:UTF-16LE 字节长度:2 =============以上是中文字符输出信息================= 编码格式:GB2312 字节长度:7 编码格式:GBK 字节长度:7 编码格式:GB2312 字节长度:7 编码格式:GB18030 字节长度:7 编码格式:ISO-8859-1 字节长度:7 编码格式:UTF-8 字节长度:7 编码格式:UTF-16 字节长度:16 编码格式:UTF-16BE 字节长度:14 编码格式:UTF-16LE 字节长度:14 ========================================== char array length:2 content:| ?? | String length:2 编码格式:GB2312 字节长度:1 编码格式:GBK 字节长度:1 编码格式:GB2312 字节长度:1 编码格式:GB18030 字节长度:4 编码格式:ISO-8859-1 字节长度:1 编码格式:UTF-8 字节长度:4 编码格式:UTF-16 字节长度:6 编码格式:UTF-16BE 字节长度:4 编码格式:UTF-16LE 字节长度:4 ==========================================
以上是关于中文字符与编码格式占位问题的主要内容,如果未能解决你的问题,请参考以下文章