编码: ASCII(ANSI), GB-2312, UNICODE, UTF-8,UTF-16

2023-11-12

几年前,我在初次接触Unicode时学习过一段时间的编码,当时解决了问题就没有继续下去,我记得当时遗留下来的一个问题就是UTF-8到底是怎样一种编码,和Unicode有什么区别?为什么有了Unicode还要有UTF-8?
    最近又遇到了UTF-8的问题,因此我决定就此机会好好学习一下Unicode相关的知识。
    从前在编程的时候我都不用Unicode字符集的,好像VC6对工程的默认设置是使用多字节字符集。记得那个时候如果要在VC6里使用Unicode字符集,好像C++程序的入口点还要在工程属性里进行特别的设置。
    最近我彻底抛弃了多字节字符集,不仅因为VS2005工程的默认字符集是Unicode,而且如果使用Unicode编译出来的GUI应用程序,当XP的主题变化时,控件的外观也会跟随改变;而如果使用多字节字符集,为了让程序的外观适应主题变化,则必须另附一个manifest文件。不用写一行代码就可以达到这个效果,我只用脚趾头想了一下就决定从此在所有工程里采用Unicode了:)
    
ASCII字符集
    7位的编码方案,总共表示128个字符,其中包括了大小写英文字母、数字、标点符号等常用字符。英语世界已经足够应付。
    
ISO-8859-1字符集
    也称ISO-Latin字符集,它扩展了ASCII字符集,用到了8bit字节里的最高一位,这样它就有256个字符,前128个字符和ASCII字符集相同。有了ISO-Latin字符集,西方世界的一些其它语言,如西班牙语、法语、德语、意大利语都够用了。

GB系列字符集(GB2312,GBK,GB18030)
    由于一个字节是无论如何也表达不了哪怕是最长用的汉字字符集的,所以为了用计算机存储汉字,必须使用多个字节。
    多字节字符集就是使用可变长的编码长度来编码字符,有的字符用一个字节编码,比如ASCII字符,有的字符用两个字节编码,比如汉字。在VC里,多字节字符集等同于双字节字符集,VC不支持多于2个字节编码长度的字符。GB系列的字符集和ISO-Latin字符集一样,前128个字符和ASCII字符集相同。GB系列字符集是兼容的,相同的中文字符在这3个字符集里有相同的编码。GB2312和GBK一个字符最多2个字节表示,GB18030可多达4个字节。在这种编码里表示汉字时,需要一个leading byte,它总是大于127,这个字节的含义是说明它和后面的字节(们)一起表示一个字符。
    这些字符集(ISO-Latin字符集,GB系列字符集)都是以ASCII为基础扩展而来,统称为ANSI字符集。
    记事本在默认情况下(选择ANSI编码)就是使用多字节字符集保存文件的,至于使用的是GB2312,GBK,还是GB18030我不清楚。
    

Unicode字符集:
    每个地区的人都试图扩展ASCII编码来支持本地的语言,最终的结果是导致互不兼容。因为除了最低的128个字符相同以外,其它的字符都使用自己特殊的编码方案。
    当使用与文件保存时的编码方案不同的编码来读取文件时,就会产生错误——比如Windows记事本那个著名的“联通BUG”。
    统一所有字符的编码是Unicode被设计出来的初衷。
    长久以来,Unicode在我心中的概念就是:使用2个字节来编码字符,使用Unicode可以表示世界上所有的字符。但这种理解并不准确!
    其实Unicode可以看成是一种理想:这种理想就是世界上的所有字符都只有一个唯一的标识!至于怎样去实现这种理想,有很多的实现方式:UTF-8,UTF-16,UTF-32,甚至在Unicode标准里还介绍了一种压缩的实现方式。Unicode把这个唯一的标识称之为代码点(code point),字符的代码点以U+XXXX的方式表示,这个可以打开Windows自带的字符映射表看得到。
    Unicode最初被设计出来的时候希望使用2个字节就可以表示世界上的所有字符。因此,实现Unicode最直接的想法就是用两个字节来存储一个字符,如果大家都这么想就好了,这样一个字符就可以用2个字节长的短整形来存储。但是偏偏还有一个叫做大端小端东西存在,这样2个字节的短整型在内存中的表示顺序就有2种可能,这就是为什么当用记事本保存文本文件时可以选择Unicode或者Unicode big endian的原因。
    1个字符=2个字节在现实中却遇到了麻烦。一方面,用2个字节表示一个字符,浪费了大量的空间(如果仅仅用来存储ISO-Latin字符集里的字符的话),而且还会有大端小端的问题,解决的方案是UTF-8编码;另一方面,人们在实践中发现即使用2个字节编码也无法表示所有字符,因此出现了UTF-16。UTF-16除了使用2个字节编码外,还使用一对2个字节来表示Unicode里很少用到的字符;另外还有UTF-32,它使用单独的4个字节来编码所有的Unicode字符。
    
UTF-8编码
    我想最早提出UTF-8的一定是美国人,“用2个字节来表示一个英语字母这太浪费了!”,他们肯定会这么说的。顾名思义,那个8说明UTF-8编码中最小的单位是8bit的字节。采用UTF-8编码,Unicode代码点中U+007F以下(包含U+007F)的字符用一个字节编码,其它的字符用多个字节编码,最多一个字符用4个字节编码。这样UTF-8兼容ASCII,但是不兼容ISO-Latin字符集。

    Unicode字符集采用UTF-8编码方案时的对照表:
    Unicode代码点区间 UTF-8编码后的结果

U-00000000 - U-0000007F 0xxxxxxx
U-00000080 - U-000007FF 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

    网上的很多文章都有这个表,可以看到一个Unicode代码点采用UTF-8编码时最多可达6个字节。但是从Unicode官方网站上看到的是UTF-8编码的最大字节长度是4个字节。也就是说最下面的两行没有了。并且第四行的范围是从U-00010000到U-00110000。Unicode协会说它们对UTF-8编码的改进工作进行地很快,看来网上的那些文章早就过时了。
    UTF-8编码的实现方式比较好理解:例如“汉”字的Unicode编码是6C49,6C49在0800-FFFF之间,所以最终编码应该是3个字节。6C49的二进制位串是:110110001001001,把这个位串从右向左填充到那3个字节的x部分,高位不够的用0补。最终得到的3个字节是:11100110 10110001 10001001,即E6 B1 89。注意由于UTF-8的最小编码单元是字节,所以不存在大端小端的问题。在各种Unicode编码方案之间转换的标准算法(诸如从UTF-16到UTF-8或者反过来)已经有了,在Unicode的官方网站上可以找到。
       
    这样Unicode至少就有5种编码方案了(UTF-8,UTF-16两种,UTF-32两种),怎么区分它们呢?
    区分各种不同Unicode编码方案的技巧被称为Byte Order Mark(BOM)。将BOM插入到文件的开头,应用程序就能知道接下来应该使用哪种编码来解析文本了,以ANSI编码保存的文件没有BOM。
Byte order mark Description
EF BB BF UTF-8
FF FE UTF-16, little endian(VC的Unicode用的就是这种格式)
FE FF UTF-16, big endian
FF FE 00 00 UTF-32, little endian
00 00 FE FF 00 00 FE FF

关于Unicode编程:
    当然最常见的问题是多字节字符集,与Unicode的各种编码之间怎么转换。Unicode与UTF8之间有一一对应的关系,它们之间可以直接相互转换,多字节字符集和UTF8之间的转换得经过Unicode中转。
    MBCS--Unicode--UTF8
    UTF8--Unicode--MBCS
    在Windows平台上,进行转换的函数是WideCharToMultiByte和MultiByteToWideChar。这样如果要将MBCS转换成UTF8,先调用MultiByteToWideChar,使用CP_ACP代码页(默认的代码页),然后调用WideCharToMultiByte,这次用CP_UTF8作代码页。

    解释一下记事本的那个"联通"的BUG
    当以ANSI编码保存联通两个字时,文件里的内容如下:
    c1 aa cd a8
    1100 0001 1010 1010 1100 1101 1010 1000
    记事本在打开文件的时候会去猜测文本文件的编码方式。由于第一二个字节、第三四个字节的起始部分的都是"110"和"10",正好与UTF8规则里的两字节模板是一致的,记事本猜测文件是以UTF-8的编码方式保存的,所以不能正确的显示。
    而如果使用记事本的打开菜单,强制以ANSI编码的方式打开文件则能够正确得显示联通两个字。而如果你在"联通"之后多输入几个字,其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,而会用ANSI的方式解读之,这时乱码又不出现了。
    奇怪的是:记事本为什么没有使用BOM呢?使用ANSI编码存储的文本文件是没有BOM的啊!既然没有BOM,它应该以ANSI编码读取文件才合理啊!只能解释说记事本在很久以前就在使用“猜”技术来揣度文件的编码了。

    关于编码的一些资源:
    CSDN的blog上有两篇相当不错的文章,汉字编码及相关问题(unicode,ansi,gb2312)和(补充阅读)关于编码: ascii(ansi), gb-2312, unicode, utf8 。
    joelonsoftware上有篇写得浅显易懂的有关字符集的文章:The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)
    如果你想知道所有关于Unicode的细节,官方网站是最权威的地方。关于Unicode的一些FAQ可以很快了解一些常识。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zengkun100/archive/2008/02/23/2115960.aspx

=======================================

在Eclipse 中,如何不乱码?

1.查看 该文本使用的是什么编码格式?

         1.1 使用notepad++ 可以查看该文本的编码格式。

         1.2 使用Editplus 也行的

2.Eclipse 要切换到 但前的文件的编码格式。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编码: ASCII(ANSI), GB-2312, UNICODE, UTF-8,UTF-16 的相关文章

随机推荐

  • ZVM Bugs (持续更新)

    问题1 Cmake配置问题 CMake Error at CMakeLists txt 5 find package Could not find a package configuration file provided by Zephy
  • can通道采样频率_CAN总线基础(上)

    概述 汽车电子设备的不断增多 对汽车上的线束分布以及信息共享与交流提出了更高的要求 传统的电气系统往往采用单一连接的方式通信 这必将带来线束的冗余以及维修的成本的提高 传统的单一通信的对接方式 已经不能满足现代汽车电子发展的需求 采用更为先
  • 软件设计师——多媒体基础

    文章目录 音频相关概念 图像相关概念 媒体的种类 多媒体相关计算 常见多媒体标准 数据压缩 有损压缩与无损压缩 题目举例 软件设计师中该部分分值为 1 3 分 音频相关概念 次声波 小于20Hz 超声波 大于20kHz A D转换 采样 g
  • dockerfile使用报错记录

    使用centos镜像默认是8 报错 解决 修改源 RUN cd etc yum repos d RUN sed i s mirrorlist mirrorlist g etc yum repos d CentOS RUN sed i s b
  • pytorch预训练模型加载与使用(以AlexNet为例)

    目录 1 概况 2 代码讲解 2 1 加载必要的包 2 2 设置GPU和transform 2 3 数据预处理 2 4 引入模型 2 5 训练模型 2 6 测试模型 2 7 保存模型 3 完整代码 4 结果 本文主要是提供过程 不要在意结果
  • 前沿技术,目前为止功能最全最强大的PLC智能远程模块,物联网模块

    前沿技术 目前为止功能最全最强大的PLC智能远程模块 物联网模块 如下图 巨控PLC智能远程控制终端不同应用场合的不同型号 巨控GRM模块分为以下4大类 GRMOPC GRM530 GRM230 GRM110 一 巨控GRMOPC系列的PL
  • 线程池有几种创建方式?

    总体来说线程池的创建可以分为以下两类 通过 ThreadPoolExecutor 手动创建线程池 通过 Executors 执行器自动创建线程池 而以上两类创建线程池的方式 又有 7 种具体实现方法 这 7 种实现方法分别是 Executo
  • Lua 表(table)

    介绍 表 Table 是Lua语言中最主要 事实上也是唯一的 和强大的数据结构 使用表 Lua语言可以以一一种简单 统一且高效的方式表示数组 集合 记录和其他很多数据结构 Lua语言也使用表来表示包 package 和其他对象 当调用函数m
  • 我的Linux学习历程:那些我看过的Linux书籍们

    来北京工作已经一个多月 大都市的生活比起读大学要忙碌得多 尤其是出行 基本以小时为基本的计时单位 有时茫然看着窗外车水马龙 会有些迷茫自己选择的是对还是错 题外话不多说 回归这次的主题 扒一扒我看过的那些Linux相关的书籍 对于Linux
  • 部署 Exchange Server后不能登录到 OWA 或 ECP

    1 问题现象 症状 安装 Microsoft Exchange Server 2019 2016 或 2013 的 2021 年 7 月安全更新或任何更高版本的更新后 用户无法登录 Outlook 网页版 OWA 或 Exchange 控制
  • 离散制造与流程制造的区别

    导 读 nbsp 文 1878 nbsp 对于产品制造 我们常常听到离散制造 流程制造这样的分类 相信大部分读者在面对实际的生产时 也能很好地凭借经验 感觉区分出产品的制造类别 本文旨在进一步帮助读者梳理清晰离散制造与流程制造准确定义 详细
  • layui框架和vue哪个好_目前流行的9大前端框架

    1 Vue https cn vuejs org Vue 读音 vju 类似于 view 是一套用于构建用户界面的 渐进式框架 与其它大型框架不同的是 Vue 被设计为可以自底向上逐层应用 Vue 的核心库只关注视图层 不仅易于上手 还便于
  • 报名倒计时|码住 2023 Google 开发者大会精彩活动!

    2023 Google 开发者大会 9 月 6 日正式开幕 即刻抓住报名机会 亲身体验大会精彩 仅出于确认身份 请填写与身份证件一致的姓名 并确认邮箱地址及联系电话准确无误 本次大会亮点多多 精彩纷呈 来到现场之前 让我们一起先睹为快 大会
  • Hive中常见的错误

    小编用的Cloudera Manager搭建的集群 因为服务器升级 集群停掉之后再启动的时候遇到了各种问题 Hive是常用的大数据组件 在此小编特意整理了Hive在使用过程中常见的错误如下 以供大家参考 由于造成错误的原因是多方面的 下面列
  • 根据经纬度获取当地时区,设置时区

    时区除了我们经常用的东八区 新需求是根据经纬度获取当地时区 并且将设备上的时区更新到当前时区 获取当前位置的经纬度是基本要求 本项目中有获取全球任意城市的天气需求 所以两个功能我就结合起来做了 1 获取天气信息 Api地址 Current
  • 开源ETL工具:Talend系列

    2019独角兽企业重金招聘Python工程师标准 gt gt gt Talend Open Studio 拓蓝开放工作室 Talend的旗舰产品 Talend Open Studio 提供了迄今市场上最为开放 最具效力并最有创造力的数据集成
  • VUE实现box-shadow生成器

    最终效果 Html结构 一层结构 div class body div class container div class result div div class setting div div class code container
  • SQL 子查询

    查询 query 任何SQL语句都是查询 但此术语一般指SELECT语句 SQL还允许创建子查询 subquery 即嵌套在其他查询中的查询 为什么要这样做呢 理解这个概念的最好方法是考察几个例子 利用子查询进行过滤 订单存储在两个表中 每
  • c++中的左值与右值

    c 中的左值与右值
  • 编码: ASCII(ANSI), GB-2312, UNICODE, UTF-8,UTF-16

    几年前 我在初次接触Unicode时学习过一段时间的编码 当时解决了问题就没有继续下去 我记得当时遗留下来的一个问题就是UTF 8到底是怎样一种编码 和Unicode有什么区别 为什么有了Unicode还要有UTF 8 最近又遇到了UTF