中文字符与编码格式占位问题

最近面试,碰到的面试题。

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\t\t", encodingName);
            System.out.format("字节长度:%s\n",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
==========================================

原文地址:https://www.cnblogs.com/chwilliam85/p/9370870.html

时间: 2024-08-23 22:42:14

中文字符与编码格式占位问题的相关文章

判断一个字符串中是否含有中文字符:

python中的encode和decode: 首先,在Python中字符串的表示是 用unicode编码.所以在做编码转换时,通常要以unicode作为中间编码. decode的作用是将其他编码的字符串转换成unicode编码,比如 a.decode('utf-8'),表示将utf-8编码的字符串转换成unicode编码 encode的作用是将unicode编码的字符串转换成其他编码格式的字符串,比如b.encode('utf-8'),表示将unicode编码格式转换成utf-8编码格式的字符串

中文字符编码

UTF-8和GBK等中文字符编码格式介绍及相互转换 UTF-8 GBK UTF8 GB2312 之间的区别和关系 字符编码详解

在使用NSArray打印的时候如果遇到中文字符那么会打印出来编码。

在使用NSArray打印的时候如果遇到中文字符那么会打印出来编码,如下代码: - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. NSArray *array = @[@"张三",@"lisi",@"123"]; NSLog(@"%@",array)

Fastdb 之中文字符截取错误的问题

Fastdb C#版本中,如果定义字段类型为  CLI.FieldType.cli_asciiz,使用的过程中插入中文字符集会出现乱码的情况, 追查code发现是在对字符串缓冲区CopyBufferData的过程中直接fastdb直接使用了s.length获取了字符个数,而不是获取字节数,由于中文占位两个字节,所以导致数据copy不全,从而出现乱码. 不多说,修正代码如下: protected int bytelengh(string str) { //使用Unicode编码的方式将字符串转换为

Mysql插入数据里有中文字符出现Incorrect string value的错误

问题:Mysql插入数据里有中文字符出现Incorrect string value的错误 描述:CMD里直接敲代码插入数据 提示的部分截取为:ERROR 1366 (HY000): Incorrect string value 一般都是编码问题,show variables like 'character%' 查看后,发现所有编码都为UTF8,并没有错. 也有一种可能是CMD黑窗口的文字编码问题,试着先设置客户端命令的编码,再插入果然正确!然后百度搜索客户端编码相关的问题也发现有和我出现过同样

jsp与mysql中的中文字符乱码问题

刚开始自学jsp,在练习的过程中遇到了一个很严重的问题,就是中文字符乱码的问题,我用了三天的时间,搜集资料,终于解决了这个问题,现在对学到的东西进行一下总结整理. 1.首先是jsp页面显示乱码的问题,<title></title>标签中有中文有英文,设置<meta charset="utf-8" > 显示乱码,改为gbk则可正常显示,其他页面使用utf-8则显示正常.最终发现是因为文件创建是用了不同的方法,一般情况下,用记事本编写代码,文件另存为*.

笔记:PHP查询mysql数据后中文字符乱码

新建表Clubs CREATE TABLE `Clubs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(30) CHARACTER SET utf8 NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; id name 1 程序员2 架构师3 产品经

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

Java中的一个char采用的是Unicode编码集,占用两个字节,而一个中文字符也是两个字节,因此Java中的char是可以表示一个中文字符的. 但是在C/C++中由于采用的字符编码集是ASCII,只有一个字节,因此是没办法表示一个中文字符的. 解答了上面的浅显易懂的问题之后,下面彻底理清楚字符 字节以及编码的原理. 其实关于编码以及字节的问题,在腾讯实习生一面的时候也问到过,当时搞不懂面试官为什么会问这个问题,现在想想,这个问题还是很考验一个人的思考以及钻研深度的,而且这个问题远远比自己想象

java中向Mysql插入中文字符出现乱码

昨天新建了个Mysql数据库表,在JAVA中插入中文字符时,发现数据库中的插入结果是乱码.实际是字符集不符合问题. 针对这个问题的解决方法有很多,例如这个文章java与mysql乱码的问题不过我建议将表删掉重新建表,建表时默认字符集设置为UTF-8,这样可以避免以后出现各种问题. 可以通过语句show create table table_name来查看该表的编码格式.例如我的是: CREATE TABLE `blog` ( `title` varchar(30) NOT NULL DEFAUL