精华内容
下载资源
问答
  • 字符编码

    千次阅读 2015-12-25 19:25:59
    1、什么是字符编码?  我们知道,计算机数据只能是二进制的,数值类型的数据转换成二进制很简单,我们已经了解了,但字符类型如何转换成二进制呢?这就需要使用字符编码!  在编码表中,每个字符都有对应的编码,...

    1、什么是字符编码?


      我们知道,计算机数据只能是二进制的,数值类型的数据转换成二进制很简单,我们已经了解了,但字符类型如何转换成二进制呢?这就需要使用字符编码!

      在编码表中,每个字符都有对应的编码,编码是整数,最终在计算机中存储的是字符的编码,而不是字符本身(因为计算机数据都是二进制数值,所以字符本身是无法存储的)。

      当我们存储字符’A’时,其实是通过编码表找到’A’字符对应的编码,然后把编码存储在计算机中。即存储的是65。当我们读取字符时,其实读取的也是字符的编码,然后使用编码再去编码表中查找对应的字符显示。如下:


    2、常见的字符编码

    (1)ASCII

      在所有字符集中,最知名的可能要数被称为ASCII的7位字符集了。它是美国标准信息交换代码(American Standard Code for Information Interchange)的缩写, 为美国英语通信所设计。它由128个字符组成,包括大小写字母、数字0-9、标点符号、非打印字符(换行符、制表符等4个)以及控制字符(退格、响铃等)组成。

    (2)ISO-8859-1

      由于ASCII是针对英语设计的,当处理带有音调标号(形如汉语的拼音)的欧洲文字时就会出现问题。因此,创建出了一些包括255个字符的由ASCII扩展的字符集。有一种8位字符集是ISO 8859-1Latin 1,也简称为ISO Latin-1。它把位于128-255之间的字符用于拉丁字母表中特殊语言字符的编码,起初称为拉丁码,后来被ISO(国际标准化组织)命名为ISO-8859-1。

    (3)GB2312、GBK、GB18030

      GB2312是一个简体中文字符集的中国国家标准,全称为《信息交换用汉字编码字符集•基本集》,又称为GB0,由中国国家标准总局发布,1981年5月1日实施。GB2312编码通行于中国大陆;新加坡等地也采用此编码。

      GB2312标准共收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时,GB 2312收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个全角字符。

      对于人名、古汉语等方面出现的罕用字,GB2312不能完全包括,于是对其进行了扩展,增加一些生僻字,形成了GBK。后来在GBK的基础上又增加了一些生僻字,就是GB18030。所以说,GB18030包括GBK,GBK包括GB2312。

      GB2312兼容ASCII码,这部分还是每个字符占1个字节。每个汉字字符占2个字节。GB2312是中国自己的字符集,而其他国家也都有自己的字符集!!!

    (4)Unicode

      很多国家针对自己的语言都拥有自己的字符编码,那么不同编码形式进行交互时就有可能导致不兼容,如果我们打开一份字节序文件,如果不知道其编码规则,就无法正确解析其语义,这也是产生乱码的根本原因。于是有人就想将世界上的字符全部统一在一个编码表中,这就出现了Unicode编码。

      Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的编码,以满足跨语言、跨平台进行文本转换、处理的要求。它通过增加一个高字节(2个字节)对ISO Latin-1字符集进行扩展,当这些高字节位为0时,低字节就是ISO Latin-1字符。UNICODE支持欧洲、非洲、中东、亚洲(包括统一标准的东亚象形汉字和韩国象形文字)。但是,UNICODE并没有提供对诸如Braille, Cherokee, Ethiopic 文字的支持。同时它也不支持如Ahom, Akkadian, Aramaic, Babylonian Cuneiform之类的古老文字。Unicode支持ISO Latin-1(ISO-8859-1),而Latin-1包含了ASCII编码表。

      记住:Unicode是一个统一的编码规范,UTF-8是Unicode的一种实现方式,Unicode的实现方式除了UTF-8还有其它的,比如UCS-2和UTF-16等,它们之间能够互相转换。

    3、Unicode 下的几种实现方式

    (1)UTF-8

      最初的unicode编码是固定长度的,16位,也就是2两个字节代表一个字符,这样一共可以表示65536个字符。显然,这样要表示各种语言中所有的字符是远远不够的。Unicode4.0规范考虑到了这种情况,定义了一组附加字符编码,附加字符编码采用2个16位来表示,这样最多可以定义1048576个附加字符,目前unicode4.0只定义了45960个附加字符。

      事实证明,对可以用ASCII表示的字符使用UNICODE并不高效,因为UNICODE比ASCII占用大一倍的空间,而对ASCII来说高字节的0对他毫无用处。为了解决这个问题,就出现了一些中间格式的字符集,他们被称为通用转换格式,即UTF(Universal Transformation Format)。目前存在的UTF格式有:UTF-7, UTF-7.5, UTF-8, UTF-16, 以及 UTF-32。

      话说当初大牛Ben Thomson吃饭时,在一张餐巾纸上,设计出了UTF-8,然后回到房间,实现了第一版的UTF-8。

      UTF-8是一种8位的unicode字符集,编码长度是可变的,并且是ASCII字符集的严格超集,也就是说ASCII中每个字符的编码在UTF-8中是完全一样的。UTF-8字符集中,一个字符可能是1个字节,2个字节,3个字节或者4个字节长。一般来说,欧洲的字母字符长度为1到2个字节,而亚洲的大部分字符则是3个字节,附加字符为4个字节长。所以说,UTF-8能对不同范围的字符使用不同长度的编码,例如ASCII 编码部分与ASCII一样,都是1个字节,而汉字部分就是3个字节。

      UTF-8的主要优点:

        对于欧洲字母字符需要较少的存储空间。

        容易从ASCII字符集向UTF-8迁移。

    (2)UCS-2

      UCS-2是固定长度为16位的unicode字符集。每个字符都是2个字节,UCS-2只支持unicode3.0,所以不支持附加字符。

      UCS-2的优点:

        对于亚洲字符的存储空间需求比UTF-8少,因为每个字符都是2个字节。

        处理字符的速度比UTF-8更快,因为是固定长度编码的。

        对于windows和java的支持更好。

    (3)UTF-16

      UTF-16也是一种16位编码的字符集。实际上,UTF-16就是UCS-2加上附加字符的支持,也就是符合unicode4.0规范的UCS-2。所以UTF-16是UCS-2的严格超集。UTF-16中的字符,要么是2个字节,要么是4个字节表示的。UTF-16主要在windows2000以上版本使用。

    4、Unicode转换到UTF-8规则

       如果Unicode编码的16位二进制数的前9位是0, 则UTF-8编码用一个字节来表示,这个字节的首位是0,剩下的7位与原二进制数据的后7位相同。例如:

      Unicode编码:\u0061 = 00000000 01100001

      UTF-8编码: 01100001 = 0x61

       如果Unicode编码的16位二进制数的头5位是0,则UTF-8编码用2个字节来表示,首字节用110开头,后面的5位与原二进制数据去掉前5个零后的最高5位相同;第二个字节以10开头,后面的6位与原二进制数据的低6位数据相同。例如:

      Unicode编码: \u00A9 = 00000000 10101001

      UTF-8编码: 11000010 10101001 = 0xC2  0xA9

       如果不符合上述两个规则,则用3个字节表示。第一个字节以1110开头,后四位为原二进制数据的高四位,第二个字节以10开头,后六位为原二进制数据的中间6位,第三个字节以10开头,后6位为原二进制数据的低6位。例如:

      Unicode编码: \u4E2D = 01001110 00101101

      UTF-8编码: 11100100 10111000 10101101 = 0xE4 0xB8 0xAD

    5、解释一下高字节和低字节

      16位是两个字节,前八位是高字节,后八位是低字节。当一个逻辑上长于一个字节的整形数据放置在内存中时(比如16位,32位,和64位的整数),计算机设计者需要考虑这些字节的存储顺序。一些设计者选择了将字节的逻辑顺序与物理顺序一致,即将逻辑上较低的字节放置在物理上较低的字节上;另一些设计者则选择了将字节的逻辑顺序与物理顺序相反,即将逻辑上较低的字节放置在物理上较高的字节上。

      ● Big-Endian 是指高字节存放在高地址,如下:

    内存地址 存放内容
     0x4000 
    0x12
    0x4001
    0x34

      ● littele-Endian 指高字节存放在第地址,如下:

    内存地址 
    存放内容
    0x4000
    0x34
    0x4001
    0x12 

      小结:字符编码就是将字符转换为二进制编码,其中Unicode是统一的编码规范,旗下有UTF-8等多种实现方式,而乱码的出现,就是因为使用了错误的编码表造成的!!!


    展开全文
  • 拨开字符编码的迷雾--字符编码概述

    万次阅读 2017-11-27 20:46:23
    相信不少人在字符编码上面摔过跟头,这篇文章针对开发中需要了解的字符编码知识进行了简要的讲解,希望能够对大家有所帮助。 1. ASCII及其扩展 1.1 什么是ASCII字符集 字符集就是一系列用于显示的字符的...

    为什么这样的字符串{"data":"颸颸"},JSON库(如jsoncpp)会解析失败?
    为什么界面上韩文显示乱码?
    ASCII和ANSI有什么区别?
    相信不少人在字符编码上面摔过跟头,这篇文章针对开发中需要了解的字符编码知识进行了简要的讲解,希望能够对大家有所帮助。

    1. ASCII及其扩展

    1.1 什么是ASCII字符集

    字符集就是一系列用于显示的字符的集合。ASCII字符集由美国国家标准协会(American National Standard Institute)于1968年制定一个字符映射集合。

    ASCII使用7位二进制位来表示一个字符,总共可以表示128个字符(即2^7,二进制000 0000 ~ 111 1111 十进制0~127)。

    ASCII字符集中每个数字对应一个唯一的字符,如下表:
    这里写图片描述

    因为其对应关系非常简单,不需要特殊的编码规则,所以严格来讲ASCII不能算字符编码,因为它没有规定编码规则。我们只是习惯将ASCII字符集称之为ASCII码、ASCII编码。

    1.2 ASCII的扩展

    1.2.1 最高位扩展 - ISO/IEC 8859

    ASCII字符集是美国人发明的,这些字符完全是为其量身定制的。但随着计算机技术的发展和普及,传到了欧洲(如法国、德国)各国。由于欧洲很多国家中使用的字符除了ASCII表中的128个字符之外,还有一些各国特有的字符,于是欧洲人民发现ASCII字符集表达不了他们所要表达的东西呀。怎么办了?他们发现ASCII只使用了一个字节(8位)之中的低7位,于是欧洲各国开始各显神通,打起了那1个最高位(第0位)的主意,将最高位利用了起来,这样又多了128个字符,从而满足了欧洲人民的需要。

    但因为每个国家的需求不一样,各国都设计了不同的方案。为了结束这种混乱的局面,国际标准化组织(ISO)及国际电工委员会(IEC)联合制定了一系列8位字符集的标准,统称为ISO 8859(全称ISO/IEC 8859)。注意,这是一系列字符集的统称。如ISO/IEC 8859-1(也就是常听到的Latin-1)支持西欧语言,ISO/IEC 8859-4(Latin-4)支持北欧语言等。

    完整列表如下(摘自百度百科):
    ISO/IEC 8859-1 (Latin-1) - 西欧语言
    ISO/IEC 8859-2 (Latin-2) - 中欧语言
    ISO/IEC 8859-3 (Latin-3) - 南欧语言,世界语也可用此字符集显示。
    ISO/IEC 8859-4 (Latin-4) - 北欧语言
    ISO/IEC 8859-5 (Cyrillic) - 斯拉夫语言
    ISO/IEC 8859-6 (Arabic) - 阿拉伯语
    ISO/IEC 8859-7 (Greek) - 希腊语
    ISO/IEC 8859-8 (Hebrew) - 希伯来语(视觉顺序)
    ISO 8859-8-I - 希伯来语(逻辑顺序)
    ISO/IEC 8859-9 (Latin-5 或 Turkish) - 它把Latin-1的冰岛语字母换走,加入土耳其语字母。
    ISO/IEC 8859-10 (Latin-6 或 Nordic) - 北日耳曼语支,用来代替Latin-4。
    ISO/IEC 8859-11 (Thai) - 泰语,从泰国的 TIS620 标准字集演化而来。
    ISO/IEC 8859-13 (Latin-7 或 Baltic Rim) - 波罗的语族
    ISO/IEC 8859-14 (Latin-8 或 Celtic) - 凯尔特语族
    ISO/IEC 8859-15 (Latin-9) - 西欧语言,加入Latin-1欠缺的芬兰语字母和大写法语重音字母,以及欧元符号。
    ISO/IEC 8859-16 (Latin-10) - 东南欧语言。主要供罗马尼亚语使用,并加入欧元符号。

    我们在数据库中常见到的Latin-1、2、5、7其实就是上面提到的针对特定语言的ASCII扩展字符集。
    这里写图片描述

    1.2.2 多字节扩展 - GB系列

    前面讲到了,欧洲各国有效利用闲置的最高位,对ASCII字符集进行了扩展。可是欧洲人民没有想到的是(当然他们也不用想这么多),在大洋彼岸有着一个拥有五千年历史的伟大民族,她拥有着成千上万的汉字,1个字节显然不够表达如此深厚的文化底蕴。

    于是当计算机引入到中国之初,国家技术监督局就设计了GB系列编码方案(GB=guo biao)。
    GB编码方案使用2个字节来表达一个汉字。同时为了兼容ASCII编码,规定各个字节的最高位(首位)必须为1,从而避免了和最高位为0的ASCII字符集的冲突。

    GB系列字符集经历下面的几个发展过程:

    编码名称 发布时间 字节数 汉字范围
    GB2312 1980年 变字节(ASCII 1字节,汉字2个字节) 6763个汉字
    GB13000 1993年第一版 变字节(ASCII 1字节,汉字2个字节) 20902个汉字
    GBK Windows95中 2个字节 21886个汉字和图形符号(含GB2312,BIG5中所有字符)
    GB18030 2000年第一版 变字节(ASCII 1字节,汉字2个或4个字节) 27484个汉字

    每一次迭代,支持的字符数量都会增加,而且每一次迭代都会保留之前版本支持的编码,所以做到了向上兼容。

    1.2.3 全角与半角

    因为汉字在显示器上的显示宽度要比英文字符的宽度要宽一倍,在一起排版显示时不太美观。所以GB编码不仅仅加入了汉字字符,而且包括了ASCII字符集中本来就有的数字、标点符号、字母等字符。这些被编入GB编码的数字、标点、字母在显示器上的显示宽度比ASCII字符集中的宽度宽一倍,所以前者称为全角字符,后者称为半角字符。

    2. ANSI

    2.1 ANSI与代码页

    前面说到了世界各国针对ASCII的扩展方案(如欧洲的ISO/IEC 8859,中国的GB系列等),这些ASCII扩展编码方案的特点是:他们都兼容ASCII编码,但他们彼此之间是不兼容的。微软将这些编码方案统称为ANSI编码。故ANSI并不是特指某一种编码方案,只有知道了在哪个国家,哪个语言环境下,才知道它具体表示哪个编码方案。

    在windows操作系统上,默认使用ANSI来保存文件。那么操作系统是如何知道ANSI到底应该表示哪种编码了,是GBK,还是ASCII,或者还是EUC-KR了? windows通过一个叫"Code Page"(翻译为中文就叫代码页)的东西来判断系统的默认编码。
    简体中文操作系统默认的代码页是936,它表示ANSI使用的是GBK编码。
    GB18030编码对应的windows代码页为CP54936。

    可以使用命令chcp来查看系统默认的代码页.

    汉字这里写图片描述(念suì,因为CSDN编辑器不支持该汉字,所以用图片代替,吐槽~~~)只包含在GB18030中,GB2312、GB13000、GBK中均不包含。默认情况下,在Visual Studio中输入该汉字,visual studio会使用CP936(即GBK)来保存代码文件,但如果在代码文件中输入该汉字,visual studio弹出如下提示要求用户选择代码页:
    这里写图片描述
    这里写图片描述

    2.2 更改默认代码页

    2.2.1 chcp命令

    可以使用chcp命令来更改默认代码页,如chcp 437将默认代码页更改为437(美国)。

    2.2.2 控制面板

    在“控制面板”–>“区域和语言”–> “更改系统区域设置”中更改系统默认的代码页为“中文(简体,中国)”。

    2.2.3 代码修改

    也可以通过代码更改默认的代码页:

    char *setlocale(
       int category,
       const char *locale 
    );
    

    3. Unicode

    3.1 Unicode产生背景

    各个国家使用不同的编码规则,虽然他们都是兼容ASCII的,但它们相互却是不兼容的。

    试想法国人Jack写了一封名为"love_you.txt"的信,传给了他的德国朋友Rose,Rose想要在windows系统上打开这个文件,她需要知道德国使用的字符编码是Latin-1,然后还要确保她的计算机上安装了该编码,才能顺利的打开这个文件。
    如果上面这些还能忍受,那么随着网络的发展,你从互联网上获取的文件,你很有可能不知道它来自哪个国家,使用的哪种编码。这也就是Email刚诞生时常常出现乱码的原因,发信人和收信人使用的编码可能是不一样的。

    于是The Unicode Standard(统一码标准)横空出世,它由The Unicode Consortium于1991年发布,我们习惯称它为Unicode字符集。

    Unicode字符集和ASCII字符集一样,也只是一个字符集合,标记着字符和数字之间的映射关系,它不包含任何编码规则和方案。和ASCII不一样的是,Unicode字符集支持的字符数量是没有限制的(具体可以参考Unicode规范)。

    我们通常认为的Unicode字符固定占用2个字节的观点是错误的。如这里写图片描述(念suì)Unicode码为D852 DF62

    那么Unicode字符是怎样被编码成内存中的字节的了?它是通过UTF(Unicode Transformation Formats)实现的,比较常见得有UTF-8,UTF-16。

    在windows系统上汉字默认使用CP936(即GBK编码),占2个字节。而大多数Unicode字符的Unicode码值也占2个字节,所以大多数人误以为汉字字符串在内存中的值就是Unicode值,这是错误的。
    可以从 站长工具-Unicode 查询汉字的Unicode码值。

    3.3 字符集与字符编码的区别

    从ASCII、GB2312、GBK、GB18030、Big5(繁体中文)、Latin-1等采用的方案来看,它们都只是定义了单个字符与二进制数据的映射关系,一个字符在一个方案中只会存在一种表示方式,所以我们说GB2312是字符集还是字符编码方式都无所谓了。但是Unicode不一样,Unicode作为一个字符集可以采用多种编码方式,如UTF-8, UTF-16, UTF-32等。所以自Unicode出现之后,字符集与字符编码需要明确区分开来。

    3.4 UTF-16编码的缺点

    UTF-16编码方式规定用两个或四个字节来表示所有的字符。对于ASCII字符保持不变,只是将原来的7位扩展到了16位,其高9位永远是0。如字符’A’:

    ASCII: 100 0001
    UTF-16: 0000 0000 0100 0001
    

    可以看到对于ASCII字符,UTF-16的存储空间扩大了一倍,UTF-16并不是完全兼容ASCII字符集。这对于那些ASCII字符集已经满足需求的西方国家来说完全是没必要的,而且ASCII字符经过UTF-16编码之后高字节始终是0,导致很多C语言函数(如strcpy,strlen)会将此字节视为字符串的结束符'\0',从而出现错误的计算结果。
    而且,UTF-16还存在大小端的问题,这里写图片描述(念suì)Unicode码在大端系统上为D852 DF62,小端系统上为52D8 62DF
    因此,UTF-16一开始推出的时候就遭到很多西方国家的抵制,影响了Unicode的推行。于是后来又设计了UTF-8编码方式,才解决了这些问题。

    3.5. Unicode字符集常用编码方式:UTF-8

    3.5.1 UTF-8概述

    UTF-8是互联网上使用最广泛的Unicode字符集编码方式。UTF-8编码的最小单位由8位(1个字节)组成,UTF-8使用一个至四个字节来表示Unicode字符。另外,UTF-8是完美兼容ASCII字符集的,这一点可以通过下面的UTF-8的编码规则得到证明。

    3.5.2 UTF-8编码规则

    UTF-8编码规则很简单:
    (1)对于ASCII(单字节字符)字符,采用和ASCII相同的编码方式,即只使用一个字节表示,且该字节第一位为0.
    (2)对于多字节(2~4字节)字符,假设字节数为n(1 < n <= 4),第一个字节:前n位都设为1,第n+1位设为0;后面的n-1个字节的前两位一律设为10。所有字节中的没有提及的其他二进制位,全部为这个符号的unicode码。
    这里写图片描述

    3.5.2 UTF-8 BOM

    BOM(byte order mark)从字面意义来看是标记字节顺序的。最早出现的原因是因为UTF-16和UTF-32编码采用2个或4个字节表示一个字符,面临大小端的问题。为了区分是使用的大端(Big Endian,简称BE)还是小端(Little Endian,简称LE),采用了在串的前面加入指定的字节加以区分,UTF-16大端加入FE FF,小端加入FF FE. 比如, 字符串“ABC”的UTF-16编码为 00 41 00 42 00 43,对应的各种的字节序列如下:
    这里写图片描述

    因为UTF-8和ASCII都是单字节序列,二者不好区分,微软采用在UTF-8编码的字符串前也加入BOM(3个字节EF BB BF)来标记UTF-8编码的串。UTF-8 BOM这一规范大多在windows下被使用,在其他平台下用的很少使用,如:Linux全部采用UTF-8编码,不存在要区分的情况;HTTP协议中可以包含Content-Type:text/html; charset=utf-8这样的说明,也不需要区分。

    展开全文
  • 拨开字符编码的迷雾--字符编码转换

    万次阅读 2017-11-27 21:22:09
    1. Windows API介绍本文介绍使用Windows API进行字符编码的转换,涉及WideCharToMultiByte和MultiByteToWideChar2个API, API接口名中的MultiByte对应着多字节编码,如ASCII、UTF-8等都是多字节编码,而WideChar...

    1. Windows API介绍

    本文介绍使用Windows API进行字符编码的转换,涉及WideCharToMultiByteMultiByteToWideChar2个API,
    API接口名中的MultiByte对应着多字节编码,如ASCII、UTF-8等都是多字节编码,而WideChar字面意思是宽字符,在windows内部宽字符特指UTF-16编码。 原型如下:

    int WideCharToMultiByte(
      UINT CodePage, 
      DWORD dwFlags, 
      LPCWSTR lpWideCharStr, 
      int cchWideChar, 
      LPSTR lpMultiByteStr, 
      int cbMultiByte, 
      LPCSTR lpDefaultChar, 
      LPBOOL lpUsedDefaultChar 
    );
    
    int MultiByteToWideChar(
      UINT CodePage, 
      DWORD dwFlags, 
      LPCSTR lpMultiByteStr, 
      int cbMultiByte, 
      LPWSTR lpWideCharStr, 
      int cchWideChar 
    );
    

    2. 接口封装

    std::string UnicodeToANSI(const std::wstring &str, UINT iCodePage = CP_ACP) {
        std::string strRes;
        int iSize = ::WideCharToMultiByte(iCodePage, 0, str.c_str(), -1, NULL, 0, NULL, NULL);
    
        if (iSize == 0)
            return strRes;
    
        char *szBuf = new (std::nothrow) char[iSize];
        if (!szBuf)
            return strRes;
        memset(szBuf, 0, iSize);
    
        ::WideCharToMultiByte(iCodePage, 0, str.c_str(), -1, szBuf, iSize, NULL, NULL);
    
        strRes = szBuf;
        delete[] szBuf;
    
        return strRes;
    }
    
    std::wstring ANSIToUnicode(const std::string &str, UINT iCodePage = CP_ACP) {
        std::wstring strRes;
    
        int iSize = ::MultiByteToWideChar(iCodePage, 0, str.c_str(), -1, NULL, 0);
    
        if (iSize == 0)
            return strRes;
    
        wchar_t *szBuf = new (std::nothrow) wchar_t[iSize];
        if (!szBuf)
            return strRes;
        memset(szBuf, 0, iSize * sizeof(wchar_t));
    
        ::MultiByteToWideChar(iCodePage, 0, str.c_str(), -1, szBuf, iSize);
    
        strRes = szBuf;
        delete[] szBuf;
    
        return strRes;
    }
    
    std::string UnicodeToUTF8(const std::wstring &str) {
        std::string strRes;
    
        int iSize = ::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, NULL, 0, NULL, NULL);
    
        if (iSize == 0)
            return strRes;
    
        char *szBuf = new (std::nothrow) char[iSize];
        if (!szBuf)
            return strRes;
        memset(szBuf, 0, iSize);
    
        ::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, szBuf, iSize, NULL, NULL);
    
        strRes = szBuf;
        delete[] szBuf;
    
        return strRes;
    }
    
    std::string UnicodeToUTF8BOM(const std::wstring &str) {
        std::string strRes;
    
        int iSize = ::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, NULL, 0, NULL, NULL);
    
        if (iSize == 0)
            return strRes;
    
        unsigned char *szBuf = new (std::nothrow) unsigned char[iSize + 3];
        if (!szBuf)
            return strRes;
        memset(szBuf, 0, iSize + 3);
    
        if (::WideCharToMultiByte(CP_UTF8, 0, str.c_str(), -1, (LPSTR)(szBuf + 3), iSize, NULL, NULL) > 0) {
            szBuf[0] = 0xEF;
            szBuf[1] = 0xBB;
            szBuf[2] = 0xBF;
        }
    
        strRes = (char*)szBuf;
        delete[] szBuf;
    
        return strRes;
    }
    
    std::wstring UTF8ToUnicode(const std::string &str) {
        std::wstring strRes;
        int iSize = ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
    
        if (iSize == 0)
            return strRes;
    
        wchar_t *szBuf = new (std::nothrow) wchar_t[iSize];
        if (!szBuf)
            return strRes;
        memset(szBuf, 0, iSize * sizeof(wchar_t));
        ::MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, szBuf, iSize);
    
        strRes = szBuf;
        delete[] szBuf;
    
        return strRes;
    }
    
    std::string ANSIToUTF8(const std::string &str, UINT iCodePage = CP_ACP) {
        return UnicodeToUTF8(ANSIToUnicode(str, iCodePage));
    }
    
    std::string ANSIToUTF8BOM(const std::string &str, UINT iCodePage = CP_ACP) {
        return UnicodeToUTF8BOM(ANSIToUnicode(str, iCodePage));
    }
    
    std::string UTF8ToANSI(const std::string &str, UINT iCodePage = CP_ACP) {
        return UnicodeToANSI(UTF8ToUnicode(str), iCodePage);
    }
    

    对于只支持简体中文(部分韩文、日文)的系统,iCodePage可以使用CP_ACP,这时API会使用系统当前的代码页(简体中文系统为CP936,即GBK字符集)来进行编码转换。 但遇到如下情况就需要手动指定代码页了:
    1. 需要转换的字符串中的文字是系统当前代码页不支持的。如字符串中含有中文,而当前系统代码页确是英文的;
    2. GBK字符集中只包含了一部分韩文和日文,部分韩文和日文的转换可以正常转换,若遇到不能转换的情况也需要将指定iCodePage为特定的支持韩文或日文的代码页了,特别是中文和韩文、日文等混合的情况下。如韩文“탉”不包含在GBK中,若这时仍然使用CP_ACP就会得到错误的转换结果?,十六进制3F。但GB18030(代码页为54936)支持“탉”,可以手动指定iCodePage为54936。

    如果代码中含有GBK不支持的字符,如“탉”、这里写图片描述(念suì)等,Visual Studio会弹出如下提示:
    这里写图片描述
    选择“以其他编码保存”,选择“Unicode(UTF-8带签名)- 代码页65001”保存。
    虽然“简体中文(GB18030) - 代码页54936”也支持这些字符,但不能选择该选项进行保存,具体原因在拨开字符编码的迷雾–编译器如何处理文件编码 中有详细的介绍。

    展开全文
  • 字符编码那些事--彻底理解掌握编码知识

    千次阅读 多人点赞 2020-05-04 16:42:33
    每一个程序员都不可避免的遇到字符编码的问题,很多人在字符编码方面同样遇到不少问题,而且一直对各种编码懵懵懂懂、不清不楚。这篇文章就是针对字符编码中的一些问题进行了详细的阐述,能从根本上理解字符编码

    一、讲述之前我们先来了解一些概念

    1、字符

    字符指类字形单位或符号,包括字母、数字、运算符号、标点符号和其他符号,以及一些功能性符号。一般来说我们称某个字符集里面的字符,叫xx字符,如ASCII字符集里面的ASCII字符,GB2312字符集里面的GB2312字符。

    2、字符集

    字符集(Character Set、Charset),字面上的理解就是字符的集合,是一个自然语言文字系统支持的所有字符的集合。字符是各种文字和符号的总称,包括文字、数字、字母、音节、标点符号、图形符号等。例如ASCII字符集,定义了128个字符;GB2312字符集定义了7445个字符。而字符集准确地来说,指的是已编号的字符的有序集合(但不一定是连续的,后文有详细介绍)。
    常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集、Unicode字符集等。

    3、码位

    在字符编码术语中,码位(code point)或称编码位置、码点,是组成码空间(或代码页)的数值。 例如,ASCII码包含128个码位,范围是016进制到7F16进制,扩展ASCII码包含256个码位,范围是016进制到FF16进制,而Unicode包含1,114,112个码位,范围是016进制到10FFFF16进制。Unicode码空间划分为17个Unicode字符平面(基本多文种平面,16个辅助平面),每个平面有65,536(= 216)个码位。因此Unicode码空间总计是17 × 65,536 = 1,114,112. —解释来源于维基百科

    4、字符编码

    字符编码(Character Encoding),是把字符集中的字符按一定方式编码为某指定集合中的某一对象的过程(比如将字符编码为由0和1两个数字所组成的位串模式、由0~9十个数字所组成的自然数序列或电脉冲等),亦即在字符集与指定集合两者之间建立一个对应关系(即映射关系)的过程。这是信息处理的一项基础技术。常见的例子包括将拉丁字母表编码成摩斯电码和ASCII码。
    p.s.这里我们计算机这里字符编码肯定是用二进制来编码的
    看完这四个概念,你应该要明白,它们之间的关系,以ASCII为例,下图解释它们之间关系
    在这里插入图片描述
    这里细说一下,码位就是这个字符集里面字符的一个表示位置,通俗来说,码位就是一般跟字符集绑在一起,字符编码是把字符集中的字符编码为特定的二进制数,以便在计算机中存储。这个二进制数就叫xx码。

    二、字符集编码分类总结

    在说字符集编码之前,先明确一个观点,字符集编码与字符集是两个不同层面的概念:
    (1)charset 是 character set 的简写,即字符集。
    (2)encoding 是 charset encoding 的简写,即字符集编码,简称编码
    在这里插入图片描述

    1、ASCII编码

    ASCII(美国信息交换标准代码)是基于拉丁字母(就是我们现在的英文字母)的一套电脑编码系统。它主要用于显示现代英语,而其扩展版本延伸美国标准信息交换码则可以部分支持其他西欧语言,并等同于国际标准ISO/IEC 646。

    ASCII 由电报码发展而来。第一版标准发布于1963年,1967年经历了一次主要修订,最后一次更新则是在1986年,至今为止共定义了128个字符;其中33个字符无法显示(一些终端提供了扩展,使得这些字符可显示为诸如笑脸、扑克牌花式等8-bit符号),且这33个字符多数都已是陈废的控制字符。控制字符的用途主要是用来操控已经处理过的文字。在33个字符之外的是95个可显示的字符。用键盘敲下空白键所产生的空白字符也算1个可显示字符(显示为空白)。

    每个ASCII字符占用1个字节(8bits),共有128位字符或符号,使用7位二进制数(剩下的1位二进制为0,即高位为0)来表示所有的大写和小写字母,数字0 到9、标点符号,以及在美式英语中使用的特殊控制字符。

    缺点:ASCII的最大缺点是只能显示26个基本拉丁字母、阿拉伯数目字和英式标点符号,因此只能用于显示现代美国英语(而且在处理英语当中的外来词如naïve、café、élite等等时,所有重音符号都不得不去掉,即使这样做会违反拼写规则)。而EASCII(即扩展的ASCII码,利用8位的高位设为1进行扩展)虽然解决了部份西欧语言的显示问题,但对更多其他语言依然无能为力。因此现在的操作系统内码(稍后会讲)基本已经抛弃ASCII码而转用Unicode码。

    ASCII码表 :http://www.asciitable.com

    2、GB2312编码

    前面可以看到ASCII码即使进行了扩展也能表示的字符也很少,尤其是当需要计算机显示存储中文的时候,就需要一种对中文进行编码的字符集,GB 2312就是解决中文编码的字符集,由国家标准委员会发布。那个时候当中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存,于是想到把那些ASCII码中127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。这种汉字方案叫做 “GB2312”。GB2312 是对 ASCII 的中文扩展。兼容ASCII。
    在这里插入图片描述
    这里的A GB2312码是0xA3C1,0xA3和0xC1都是高于127的,所以判断它是个全角字符,另外我们可以观察到所有的GB2312编码表中的GB2312码每个字节都是大于0xA0的,这个就是为了保证能区分是否为ASCII码,小于127的字节就按照ASCII码标准,碰到连续两个大于127字节就组合成一个GB2312码。

    GB2312汉字编码字符集对照表:http://tools.jb51.net/table/gb2312

    3、GBK编码

    但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,不得不继续把 GB2312 没有用到的码位找出来用上。后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 “GBK” 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。

    4、GB18030编码

    后来中国的少数民族也要用电脑了,GBK的两万多字也已经无法满足我们的需求了,还有更多可能你自己从来没见过的汉字需要编码。这时候显然只用2bytes表示一个字已经不够用了(2byte最多只有65536种组合,然而为了和ASCII兼容,最高位不能为0就已经直接淘汰了一半的组合,只剩下3万多种组合无法满足全部汉字要求)。因此GB18030多出来的汉字使用4byte编码。当然,为了兼容GBK,这个四字节的前两位显然不能与GBK冲突(实操中发现后两位也并没有和GBK冲突)。通过多年的发展至此,GB18030编码的中文文件已经有七万多个汉字了。

    GB18030包含三种长度的编码:单字节的ASCII、双字节的GBK(略带扩展)、以及用于填补所有Unicode码位的四字节UTF区块。所以我们说GB18030采用多字节编码,每个字符可以由 1 个、2 个或 4 个字节组成。

    其实我们用到的99%以上的汉字,都在GB2312那一块区域内。在实际使用中,GBK编码已经可以满足大部分场景了,GB18030编码中所有汉字都是我们这辈子都不一定能见到的文字,所以平时经常会使用的就是GBK编码。

    这里额外总结一下这四个兼容性关系是GB18030兼容GBK,GBK兼容GB2312,GB2312兼容ASCII。所谓兼容,你可以简单理解为子集、不冲突的关系。例如GB2312编码的文件中可以出现ASCII字符,GBK编码的文件中可以出现GB2312和ASCII字符,GB18030编码的文件可以出现GBK、GB2312、ASCII字符。

    5、Unicode

    友情建议:看Unicode一些概念解释和历史时,建议看维基百科,而且是英文版的,别用中文版,有些地方讲的模棱两可,容易陷入盲区。

    Unicode(中文:万国码、国际码、统一码、单一码)(全称Universal Multiple-Octet Coded Character Set)它伴随着通用字符集(英语:Universal Character Set, UCS)的标准而发展。所以可以看出他是字符集。

    1.Unicode与ISO 10646

    全世界很多个国家都在为自己的文字编码,并且互不相通,不同的语言字符编码值相同却代表不同的符号(例如:韩文编码EUC-KR中“한국어”的编码值正好是汉字编码GBK中的“茄惫绢”)。因此,同一份文档,拷贝至不同语言的机器,就可能成了乱码,于是人们就想:我们能不能定义一个超大的字符集,它可以容纳全世界所有的文字字符,再对它们统一进行编码,让每一个字符都对应一个不同的编码值,从而就不会再有乱码了。如果说“各个国家都在为自己文字独立编码”是百家争鸣,那么“建立世界统一的字符编码”则是一统江湖,谁都想来做这个武林盟主。早前就有两个机构做了这个事:
    (1) 国际标准化组织(ISO),他们于1984年创建ISO/IEC JTC1/SC2/WG2工作组,试图制定一份“通用字符集”(Universal Character Set,简称UCS),并最终制定了ISO 10646标准。(简单来说ISO 10646标准就是UCS)
    (2) 统一码联盟,他们由Xerox、Apple等软件制造商于1988年组成,并且开发了Unicode标准(The Unicode Standard,这个前缀Uni很牛逼哦—Unique, Universal, and Uniform)。

    Unicode与ISO 10646标准的风风雨雨:

    在1984年,喜欢以繁多的编号糊弄群众的国际标准化组织ISO也开始着手制定解决不同语言字符数量太大问题的解决方案,这一方案被称为Universal Character Set(UCS),正式的编号是ISO-10646(记得么,ASCII是ISO-646,不知这种安排是否是故意的)。还是ISO高瞻远瞩,一开始就确定了UCS是一个31位的编码字符集(即用一个大小不超过2的31次方的整数数字为每个字符编号),这回真的足以容纳古往今来所有国家,所有语言所包含的字符了(是的,任何国家,任何小语种都包括)。虽然后来他们意识到,2的31次方个码位又实在太多了……

    天下大势,分久必合。无论Unicode还是UCS,最初的目的都是杜绝各种各样名目繁多形式各异互不兼容老死不相往来的私用扩展编码(好啰嗦的一句话),结果两方确立标准的同时(最初时这两个标准是不兼容的),因为都是个干个的,肯定不可能一模一样,出现标准不同。1991年,Unicode联盟与ISO的工作组终于开始讨论Unicode与UCS的合并问题,虽然其后的合并进行了很多年,Unicode初版规范中的很多编码都需要被改写,UCS也需要对码空间的使用进行必要限制,但成果是喜人的。最终,两者统一了抽象字符集(即任何一个在Unicode中存在的字符,在UCS中也存在),且最靠前的65535个字符也统一了字符的编码。对于码空间,两者同意以一百一十万为限(即两者都认为虽然65536不够,但2的31次方又太大,一百一十万是个双方都可接受的码空间大小,也够用,当然,这里说的一百一十万只是个约数),Unicode将码空间扩展到了一百一十万,而UCS将永久性的不使用一百一十万以后的码位。也就是说,现在再讲Unicode只包含65536个字符是不对的(虽然大家现在都知道Unicode至少都可以囊括几亿个字符)。除了对已经定义的字符进行统一外,Unicode联盟与ISO工作组也同意今后任何的扩展工作两者均保持同步,因此虽然从历史的意义上讲Unicode与UCS不是一回事(甚至细节上说也不是一回事),但现在提起Unicode,指代两者均无不妥,毕竟因为已经统一了。(现在网上基本上把Unicode字符集叫做UCS,Unicoide 的全称是 Universal Multiple-Octet Coded Character Set简写也是UCS,一看也对上了,害,只能说天注定)

    现在Unicode编码点分为17个平面(plane),每个平面包含216(即65536)个码位(code point)。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0016到1016,共计17个平面。

    这第一个位置(当xx是00的时候)被称为BMP(基本多文种平面,BasicMultilingualPlane)。它包含了最常用的码位从U+0000到U+FFFF(常见的65536个字符)

    其余16个平面(从下面的1号平面到16号平面),你可以叫做非BMP,由此这样分的话里面的字符就有两个概念:BMP字符和非BMP字符,后者也被称为补充字符。(偷偷告诉你们,大家喜闻乐见的Emoji表情都是在1号平面,范围是U+1F600-U+1F64F)
    在这里插入图片描述
    2. UCS-2和UCS-4

    ISO 10646标准为“通用字符集”(UCS)定义了一种16位的编码形式(即UCS-2),UCS-2全称Universal Character Set coded in 2 octets,从英文上就可以看出含义,以2字节编码的通用字符集编码,固定占用2个字节,它包含65536个编码空间(可以为全世界最常用的63K字符编码,为了兼容Unicode,0xD800-0xDFFF之间的码位未使用)。例:“汉”的UCS-2编码为6C49。除此之外ISO 10646标准为“通用字符集”(UCS)还定义了一种31位的编码形式(即UCS-4),UCS-4全称Universal Character Set coded in 4 octets,其编码固定占用4个字节,编码空间为0x00000000 ~0x7FFFFFFF(可以编码20多亿个字符)。随着Unicode与ISO 10646合并统一,Unicode就用UCS通用字符集标准,早期的Unicode编码实现也就采用了UCS-2和UCS-4。(准确来说是UCS-2,UCS-4基本上是理论,没付诸实际,毕竟早期65536个字符已经够用了,我两个字节编码能实现的事,脑子笨的人才会用四个字节实现,你以为存储和带宽有多的)
    在这里插入图片描述这里编码最多也就存在UCS-2(big Endian和Little Endian先不管,后面会讲)

    Unicode字符集只规定了码点和文字之间的对应关系,并没有规定码点在计算机中如何存储。UCS-2和UCS-4就规定了具体的实现,后来改进演变为了UTF-16, UTF-32。然后又创造了一种全新的简单粗暴好用的变长编码UTF-8,于是乎这三哥们就形成了现代Unicode字符集编码实现的三剑客

    3. UTF-16与UTF-32

    Unicode与ISO 10646合并统一后,Unicode与 ISO 10646 的通用字符集概念(UCS)相对应。早期实现Unicode用的编码是UCS-2,后来随着发展发现 216(即 65536)个字符不能满足了,Unicode标准本身发生了变化:65536个字符显得不足,引入了更大的31位空间和一个编码(UCS-4),每个字符需要4个字节。前面已经介绍了。但是统一码联盟对此予以抵制(这就是为什么我之前说UCS-4是一种理论编码,根本就没付诸实际),这是因为每个字符4个字节浪费了很多磁盘空间和内存,并且因为一些制造商已经在每个字符2个字节的技术上投入了大量资金。所以最后通过一系列巴拉巴拉讨论规定形成了一种折衷方案,建立了UTF-16编码方案(此时Unicode标准2.0),它替代了原有的UCS-2,并做了改进。它与UCS-2一样,它使用两个字节为全世界最常用的63K字符编码,不同的是,它使用4个字节对不常用的字符进行编码。目的就是为了支持从17个平面编码1,112,064个代码点。UTF-16属于变长编码。我们可以将UTF-16编码看成是UCS-2编码父集。在没有辅助平面字符(surrogate code points)前,UTF-16与UCS-2所指的是同一意思。但当引入辅助平面字符后,就称为UTF-16了。现在应该认为UCS-2已作废,如果有人还用这种,也不必纠结,它就是表达用定长2字节编码,自己心里清楚就行(基本上你查维基百科上UCS-2都是重定向到UTF-16)。另外当时ISO 10646的UCS-4编码并入了Unicode标准,而UCS-4有20多亿个编码空间,但实际使用范围并不超过0x10FFFF,并且为了兼容Unicode标准,ISO也承诺将不会为超出0x10FFFF的UCS-4编码赋值。由此提出了实实在在的UTF-32编码(现在也应该认为UCS-4像UCS-2一样作废,维基百科上UCS-4也重定向到UTF-32页面),它的编码值与UCS-4相同,只不过其编码空间被限定在了0~0x10FFFF之间。因此也可以说:UTF-32是UCS-4的一个子集。

    (现在若有软件声称自己支持UCS-2,那其实是暗指它不能支持在UTF-16中超过2字节的字集。)

    UTF-16(16 位 Unicode转换格式)是一种字符编码,能够对Unicode的所有1,112,064个有效码点进行编码(实际上,此代码点数由UTF-16的设计决定,这个你细品你就知道什么意思,就好像某个班有55个人,根据55个座位确定55个人,而55个座位这个多少是由55个人决定的,两者是相互的,这是一个哲学道理,hh扯远了,所以其中意味自行明白)。
    前面提到过:Unicode编码点分为17个平面(plane),每个平面包含216(即65536)个码位(code point),而第一个平面称为“基本多语言平面”(Basic Multilingual Plane,简称BMP),其余平面称为“辅助平面”(Supplementary Planes)。其中“基本多语言平面”(00xFFFF)中0xD8000xDFFF之间的码位作为保留,未使用。UCS-2只能编码“基本多语言平面”中的字符,此时UTF-16与UCS-2的编码一样(都直接使用Unicode的码位作为编码值),例:“汉”在Unicode中的码位为6C49,而在UTF-16编码也为6C49。另外,UTF-16还可以利用保留下来的0xD800-0xDFFF区段的码位来对“辅助平面”的字符的码位进行编码,因此UTF-16可以为Unicode中所有的字符编码。

    UTF-16和UTF-32也就是如今Unicode编码的标准之二,他们的区别就是UTF-16是变长编码,大部分是2字节和少部分4字节,UTF-32是定长编码,表示任何字符都用 4 字节

    4. UTF-8

    从前述内容可以看出:无论是UCS-2/4还是UTF-16/32,一个字符都需要多个字节来编码,这对那些英语国家来说多浪费带宽啊!(尤其在网速本来就不快的那个年代。。。),而且我们注意到UTF-16最少2字节和UTF-32不变4字节,这肯定是不兼容ASCII码的,由此,UTF-8产生了。在UTF-8编码中,ASCII码中的字符还是ASCII码的值,只需要一个字节表示,其余的字符需要2字节、3字节或4字节来表示。

    UTF-8的编码规则:
    (1) 对于ASCII码中的符号,使用单字节编码,其编码值与ASCII值相同。其中ASCII值的范围为0~0x7F,所有编码的二进制值中第一位为0(这个正好可以用来区分单字节编码和多字节编码)。
    (2) 其它字符用多个字节来编码(假设用N个字节),多字节编码需满足:第一个字节的前N位都为1,第N+1位为0,后面N-1 个字节的前两位都为10,这N个字节中其余位全部用来存储Unicode中的码位值。
    在这里插入图片描述
    现如今UTF-8 是互联网上使用最广的一种 Unicode 的实现方式,是其他两种无可比拟的(详情请看这篇文章)

    5.UTF的字节序和BOM
    字节序就要先补充一点知识:
    码元(code unit):是能用于处理或交换编码文本的最小比特组合。它代表某种编码中最小的可用来识别一个合法字符的最小字节数序列

    UTF-8使用变长的字节序列来表示字符;某个字符(对应一个码点)可能使用1-4个字节才能表示;在UTF-8中一个字符最小可能一个字节,所以我们规定1个字节就是一个码元;

    UTF-16使用也变长字节序列来表示字符;某个字符(对应一个码点)可能使用2个或者4个字符来表示;因为2个字节序列是最小的能够识别一个码点的单位,同理我们规定2个字节就是一个码元;

    UTF-32使用定长的4个字节表示一个字符;一个字符(对应一个码点)使用4个字符来表示,这样4个字节就是一个码元。

    简单来说,就是“码点”经过映射后得到的二进制串的转换格式单位称之为“码元”。“码点”就是一串二进制数,“码元”就是切分这个二进制数的方法。这些编码每次处理一个码元,你可以把它理解为UTF-8每次读码点的8位,UTF-16每次读码点的16位,UTF-32每次读码点的32位,。当然这也是为什么叫这些叫Unicode转换格式的原因。处理的是同一个字符集,但是处理方式不同。

    字节序
    UTF-8一次一个UTF-8码元,即处理一个字节,没有字节序的问题。UTF-16一次处理一个UTF-16码元,对应两个字节,UTF-32一次一个UTF-32码元,对应处理四个字节,所以这就要考虑到一个字节序问题。以UTF-16w为例,在解释一个UTF-16编码文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?这就考虑大小端问题,所以UTF-16编码包括三种:UTF-16BE(Big Endian),UTF-16LE(Little Endian)、UTF-16(类似的名称UCS-2BE和UCS-2LE用于显示UCS-2的版本。)

    UTF-16BE和UTF-16LE好理解,直接指定了字节序(大小端),但是UTF-16怎么处理呢?

    Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:

    在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。

    同样的类比,UTF-32也是这样的。有UTF-32BE、UTF-32LE、UTF-32。前面UTF-32BE和UTF-32LE直接指定了字节序(大小端),后面的UTF-32也是靠BOM。

    UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。

    Windows就是使用BOM来标记文本文件的编码方式的。它就建议所有的 Unicode 文件应该以 ZERO WIDTH NOBREAK SPACE(U+FEFF)字符开头。这作为一个“特征符”或“字节顺序标记(byte-order mark,BOM)”来识别文件中使用的编码和字节顺序。所以用Windows自带的记事本将文件保存为UTF-8编码的时候,记事本会自动在文件开头插入BOM(虽然BOM对UTF-8来说并不是必须的)。但也有一些系统或程序不支持BOM,因此带有BOM的Unicode文件有时会带来一些问题。比如JDK1.5以及之前的Reader都不能处理带有BOM的UTF-8编码的文件,解析这种格式的xml文件时,会抛出异常:Content is not allowed in prolog。Linux/UNIX 并没有使用 BOM,因为它会破坏现有的 ASCII 文件的语法约定。 所以一般我们不建议用Windows自带的记事本编辑UTF-8文件就是这样。

    6. 总结
    (1) 简单地说:Unicode和UCS是字符集,不属于编码UTF-8、UTF-16、UTF-32等是针对Unicode字符集的编码,UCS-2和UCS-4是针对UCS字符集的编码(只是我们习惯把Unicode字符集编码简称为Unicode编码,把UCS字符集编码称为UCS编码)。 Unicode沿用UCS字符集,在UCS-2和UCS-4基础上提出的UTF-16、UTF-32。并发展了UTF-8,发展到现在,就密不可分了,大家基于UCS就把Uniocde维护好就行,发布标准大家统一。以往的UCS-2和UCS-4概念就默认作废了这样一个关系,整个他们的发展长话短说就是这样,懂了吗。
    (2) UTF-8、UTF-16、UTF-32、UCS-2、UCS-4对比:
    在这里插入图片描述
    由于历史方面的原因,你还会在不少地方看到把Unicode称为一种编码的情况,那是因为早期的2字节编码最初称为“ Unicode”,但现在称为“ UCS-2”,这种情况下的 Unicode 通常就是 UTF-16 或者是更早的 UCS-2 编码,只是被一直搞混了,在某些老软件上尤为常见。比如下面editplus里面文件编码设置。
    在这里插入图片描述
    以前的Windows电脑上的记事本(左边)显示的是Unicode,不过现在好像改了变成了UTF-16。
    在这里插入图片描述
    不过由于各种原因,必须承认,在不同的语境下,“Unicode”这个词有着不同的含义。
    它可能指:
    (1)Unicode 标准
    (2)Unicode 字符集
    (3)Unicode 的抽象编码(编号),也即码点、码位(code point)
    (4)Unicode 的一个具体编码实现,通常即为变长的 UTF-16(16 或 32 位),又或者是更早期的定长 16 位的 UCS-2

    所以像我一般有时候非要区分的话都是直接说全,Unicode 标准,Unicode 字符集,Unicode编码等等。你们呢QAQ

    6、ANSI编码

    为使计算机支持更多语言,通常使用0x800~xFF范围的2个字节来表示1个字符。比如:汉字 ‘中’ 在中文操作系统中,使用 [0xD6,0xD0]这两个字节存储。
    不同的国家和地区制定了不同的标准,由此产生了 GB2312、GBK、GB18030、Big5、Shift_JIS 等各自的编码标准。这些使用多个字节来代表一个字符的各种语言延伸编码方式,称为 ANSI 编码。在简体中文Windows操作系统中,ANSI 编码代表 GBK 编码;在繁体中文Windows操作系统中,ANSI编码代表Big5;在日文Windows操作系统中,ANSI 编码代表 Shift_JIS 编码。

    不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。
    在使用ANSI编码支持多语言阶段,每个字符使用一个字节或多个字节来表示(MBCS,Multi-Byte Character System),因此,这种方式存放的字符也被称作多字节字符。比如,“中文123” 在中文 Windows 95 内存中为7个字节,每个汉字占2个字节,每个英文和数字字符占1个字节。

    在非 Unicode 环境下,由于不同国家和地区采用的字符集不一致,很可能出现无法正常显示所有字符的情况。微软公司使用了代码页(Codepage)转换表的技术来过渡性的部分解决这一问题,即通过指定的转换表将非 Unicode 的字符编码转换为同一字符对应的系统内部使用的Unicode 编码。可以在“语言与区域设置”中选择一个代码页作为非 Unicode 编码所采用的默认编码方式,如936为简体中文GBK,950为正体中文Big5(皆指PC上使用的)。在这种情况下,一些非英语的欧洲语言编写的软件和文档很可能出现乱码。而将代码页设置为相应语言中文处理又会出现问题,这一情况无法避免。从根本上说,完全采用统一编码才是解决之道,虽然现在Unicode有了,但由于历史遗留,老软件等等原因,所以系统统一用某种编码格式的Unicode目前尚无法做到这一点。

    代码页技术现在广泛为各种平台所采用。UTF-7 的代码页是65000,UTF-8 的代码页是65001。简体中文上使用的代码页为936,GBK编码。

    以前中文DOS、中文/日文Windows 95/98时代系统内码使用的是ANSI编码(本地化,根据不同地区设置不同的系统内码Windows版本),现在win7,win10等等系统的内码都是用的Unicode,不过微软为了以前的程序兼容性,比如在某些情况下,比如你的程序需要和不支持Unicode的程序交互时,可能还是会需要用到code page,提供代码页服务(就好比微软不能说:“老子支持unicode了,以后不支持Unicode的程序都给我滚粗。”只能撅着屁股让这些老掉牙的程序仍然可以运行,于是只好给他们提供一个“非Unicode默认字符集”) 。可以在cmd下输入chcp查看code page。
    在这里插入图片描述

    • Windows API 的 Wide Char 表达是 UTF-16: Unicode (Windows), L"" 表示是转换为 wide char。
    • Cocoa 的 NSString 和 Core Foundation 的 CFString 内部表达都是 UTF-16,所以其实 OS X 和 iOS 内部处理都用的是 UTF-16。
    • Java String 的内部表达是 UTF-16,所以大量跨平台程序和 Android 程序其实内部也在用 UTF-16。
    • 大部分的操作系统和 UI framework 的内部字符串表达(内码)都是UTF-16,不过Linux系统内使用的内码是UTF-8

    7、Tip:内码和外码

    在计算机科学及相关领域当中,内码指的是“将信息编码后,透过某种方式存储在特定记忆设备时,设备内部的编码形式”。在不同的系统中,会有不同的内码。

    在以往的英文系统中,内码为ASCII。 在繁体中文系统中,当前常用的内码为大五码。在简体中文系统中,内码则为国标码。
    为了软件开发方便,如国际化与本地化,现在许多系统会使用Unicode做为内码,常见的操作系统Windows、Mac OS X、Linux皆如此。许多编程语言也采用Unicode为内码,如Java、Python3。

    外码:除了内码,皆是外码。要注意的是,源代码编译产生的目标代码文件(如果Java可执行文件或class文件)中的编码方式属于外码。

    8、参考文章,引用文章如下:

    必读,真说清的话得结合上面的维基百科上的说法理解:
    https://en.wikipedia.org/wiki/Universal_Coded_Character_Set

    https://en.wikipedia.org/wiki/Unicode

    https://en.wikipedia.org/wiki/UTF-16

    https://en.wikipedia.org/wiki/UTF-32

    除此之外参考引用了下面三篇文章的一些观点:
    http://www.blogjava.net/zhenandaci/archive/2008/12/24/248014.html

    https://www.cnblogs.com/malecrab/p/5300503.html

    https://blog.51cto.com/polaris/377468
    还有一些其他文章的资料就不一一列举了。

    展开全文
  • 本文介绍了MySQL数据库中默认字符编码的设置方法,如何设置与修改mysql默认编码,my.ini设置字符编码的教程,需要的朋友参考下。 本节重点: mysql基础配置之mysql的默认字符编码的设置(my.ini设置字符编码) ...
  • 字符编码详解

    千次阅读 2017-04-03 17:19:29
    看到一篇很用心讲字符编码的文章,另外推荐《编码》一书(英文原名《CODE:The Hidden Language of Computer Hardware and Software》) 原文地址 版本:v2.3.1 CrifanLi 摘要 本文主要介绍了字符...
  • C语言字符编码

    千次阅读 2019-04-11 15:55:02
    以C语言程序在windows控制台中的输入输出为例,阐述程序在执行环境中字符编码的过程: 1.假设用户键入拼音nihao,那么输入法根据用户输入的拼音,给出字符候选列表。 2.用户阅读完候选列表后从中选择词语“你好” 3....
  • 结合Java详谈字符编码和字符集

    万次阅读 多人点赞 2018-07-07 14:04:45
    字符编码和字符集是两个基础性的概念,很多开发人员对其都并不陌生,但是很少有人能将其讲得很准确。当应用出现乱码时,如何分析和定位原因,很多人仍是一头雾水。这篇文章,将从字符编码和字符集的相关概念开始讲解...
  • 中文字符编码问题前言 遇到的问题千奇百怪,在往mysql数据表存储带有中文字符的字符串时显示乱码。经过代码输出测试发现插入语句如下: 在数据库内查询,发现存储的内容为乱码。 尝试在mysql中直接插入语句。如下: ...
  • 字符编码小记

    千次阅读 2016-02-03 14:19:54
    字符编码”分为 字符集 和 编码方案 两个含义。
  • 字符编码杂谈

    千次阅读 2017-07-20 10:40:03
    字符编码杂谈基础知识准备字符编码笔记:ASCII,Unicode和UTF-8准备工具notepad++添加16进制插件Unicode与UTF-8的异同 Unicode只是一个符号集,它只规定了符号的二进制代码,而不是二进制代码的一种存储方式。而utf-...
  • 单字节编码: 双字节编码: 多字节编码
  • 字符集和字符编码

    千次阅读 2016-06-08 15:20:26
    本文将简述字符集,字符编码的概念。以及在遭遇乱码时的一些常用诊断技巧 背景:字符集和编码无疑是IT菜鸟甚至是各种大神的头痛问题。当遇到纷繁复杂的字符集,各种火星文和乱码时,问题的定位往往变得非常...
  • MySQL字符集和校对规则MySQL的字符集是用来定义MySQL存储字符串的方式,校对规则(有的软件叫排序规则)则是用来定义了比较字符串的方式。字符集和校对规则是一对多的关系。每种字符集都有一个默认校对规则。查看...
  • 一. Visual Studio字符集 ...但这个设置项不会对编译器处理字符编码产生直接的影响(注意这里的“直接”二字,第3节会说到),只会在工程属性配置属性-->C/C++-->预处理器加入相应的宏: 使用Unicode字符集 --
  • es6 字符串转码 ...字符串与字符编码互相转换 let c = 'c'.charCodeAt();//根据字符串获取字符编码 console.log(c);// 99  let b = String.fromCharCode(c);//根据编码获取字符串 console.log(b);// c ...
  • C--中文汉字占用字节长度(字符集和字符编码

    千次阅读 多人点赞 2019-01-21 21:59:17
    中文汉字占用字节长度一、字符集和字符编码1、概念2、英文字母和中文汉字在不同字符集编码下的字节数二、环境对应的字符编码1、Ubuntu16.04虚拟机2、Notepad++三、sizeof运算汉字占用字节长度参考 一、字符集和字符...
  • oracle 修改字符编码

    千次阅读 2019-04-03 12:02:56
    1. 问题描述 在安装oracle时设置的编码是ZHS16GBK , ...这时需要修改oracle的字符编码 2. 解决问题 第一步: 查看oracle的字符编码 SQL> select * from v$nls_parameters; PARAMETER VALUE --------...
  • 字符编码研究

    千次阅读 2015-11-20 23:07:48
    字符编码研究  应用开发中,经常会遇到乱码的问题,对于新手尤其如此。为了原理乱码问题带来的困扰,特整理一下字符编码的原理,从根本上杜绝乱码的出现。 一,相关概念  在计算机的世界中,所有的信息都是由01...
  • Python中的字符串与字符编码 本节内容: 前言相关概念Python中的默认编码Python2与Python3中对字符串的支持字符编码转换 一、前言 Python中的字符编码是个老生常谈的话题,同行们都写过很多这方面的文章。...
  • 字符编码与字符集的区别

    万次阅读 多人点赞 2016-10-28 20:21:09
    字库表(character repertoire)、编码字符集(coded character set)、字符编码(character encoding form)。 2、字库表: 字库表是一个相当于所有可读或者可显示字符的数据库,字库表决定了整个字符集能够展现...
  • Python 中的字符串与字符编码

    千次阅读 2018-03-06 11:20:11
    阅读目录: 一、前言 二、相关概念 1. 字符与字节 2. 编码与解码 ...三、Python中的默认编码 1. Python源代码文件的执行过程 ...Python2中的字符串进行字符编码转换过程是: Python3中定义的字符串默认就是unic...
  • springboot全局字符编码设置(解决乱码问题)

    万次阅读 多人点赞 2018-07-22 18:13:15
    有时候我们会发现这种问题,明明已经设置了字符编码过滤器但是还会有乱码的情况出现,这个问题令我们很是头疼,我之前也遇到过这种情况。那怎么解决呢? springboot编码格式设置有三种方式,不管使用哪种方式,总有...
  • 关于字符编码

    千次阅读 2015-10-09 14:31:29
    美国人首先对其英文字符进行了编码,也就是最早的ascii码,用一个字节的低7位来表示英文的128个字符,高1位统一为0; 后来欧洲人发现尼玛你这128位哪够用,...但是即使位数少,不同国家地区用不同的字符编码,虽然0–1
  • Eclipse设置字符编码

    万次阅读 2016-10-22 17:51:15
    设置编辑器字符编码 Window -> Preferences -> General -> Editors -> Text Editors -> Spelling 设置工作空间字符编码 Window -> Preferences -> General -> WorkSpace 设置JSP文件内容默认编码 Window -> ...
  • 字符集,代码页,字符编码

    千次阅读 2017-10-11 22:39:15
    编码:将字符编码为它所对应的字符编码值,如将文字写入文件 解码:将字符编码值解码为相应的字符,如从文件中读取文字标准ascii码:使用7 位二进制数来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 126,994
精华内容 50,797
关键字:

字符编码