字符集和编码——Unicode(UTFUCS)深度历险

2023-11-07 12:59

本文主要是介绍字符集和编码——Unicode(UTFUCS)深度历险,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  计算机网络诞生后,大家慢慢地发现一个问题:一个字节放不下一个字符了!因为需要交流,本地化的文字需要能够被支持。

  最初的字符集使用7bit来存储字符,因为那时只需要存下一些英文字母和符号。后来虽然扩展到使用8bit来存储一个字符了(这种方式被国际标准化组织收录,成为ISO8859-1。在字符集发展历程中国际标准化组织一直发挥着重要作用。),也还是无法存储诸如中文的字符。

  混乱的年代到来了。为了存储下自己的文字,各个国家和地区(多为非拉丁语系的民族,因为这些语种字符数很庞大)各自使用两个字节即16bit来存放一个字符。他们把首字节的前2^7个位留给一个字节能存下的字符(如英文字母和标点符号),而后的位和后面的字节一起组成适用于本地文字的字符。这中方式一直沿用至今,如GB2312、GBK(此编码为微软为简体中文用户设计的)、GB18030、BIG5等。使用这种方式有一个问题:不同的数值(假如我们把字符换算成数字)在不同的字符集可能有不同的意义,甚至使用不同字体也会呈现出不同的效果!而且从一个字符集到另一个字符集的转化也会非常麻烦!

  标准化一直在进行。为了解决上述麻烦,各种机构都做出了不同的努力。以微软为代表的操作系统可能更多的是提供用户可选择的语言和区域设置,并使用如CodePage(代码页,Windows操作系统对不同地区不同字符集的支持方式。如GBK为CP936)来隔离差异,国际标准化组织(ISO)编纂了ISO10646来规范和整合字符集(被成为通用字符集 Universal Character Set,UCS )。统一码联盟(由各个大型企业及组织共同维护)发布了统一码(Unicode)项目。

  起初,UCS和Unicode各自为政,但1991年前后他们都发现:世界不需要两个不一样的“统一”、“通用”的字符集。所以他们联合起来维护一个字符集,现在他们的差别大概是发布新版本时使用什么字体了-_-。

  UCS和Unicode都使用最大32bit来存储字符,他们(其实是一样的,不过还是区分一下)的码位(字符数)有1114112个,从0x0到0x0x10FFFF。

  大家可能会奇怪,32bit最多可以表示超42亿个字符(即从0x0到0xFFFFFFFF),为什么只使用了其中这么小一部分呢?其实,这里面还有一些其他原因。

 

  使用32bit来存储字符看起来是一件一劳永逸的方式,但如果这32bit是定宽的(即任何字符都要使用完这32bit)的话就不可避免的造成空间的浪费,程序效率也会降低!

  能不能把UCS(Unicode)设计成“变宽”的呢?聪明的设计师想到了一个主意,他们发明了一种名为“统一码转换格式”即UTF的来将字符对应的数字(可能从小于127至大于100万不等)转化为多个字节来进行存储。

  简单说来,UCS或Unicode只是定义了从0到1114112这些数字各自是什么字符(而指示界面上该怎么显示这个字符则是由“字体”来管理,比如Windows下“微软雅黑”字体就是这个样子的(部分):

  

)。而如果从1~4个字节(变宽)还原出这个数字(或字符)就是UTF的事儿了。

  比如“汉”字,对应数字为23383,那么要使用3字节的UTF8格式字节进行存储(原因我们下面再讲),或者1字节的UTF16或1字节的UTF32。

  “汉” UTF8 = {0xE6, 0xB1, 0x89}

    UTF16 = {0x6c49}

    UTF32 = {0x6c49}(高位补0可省略)

  Unicode字符集的划分大概有两种:按“单元(Cell)”划分(Unicode官方文档是这样分的)和按“平面(Plane)”划分。一个单元为128个字符,一个平面有65536个字符。

  我们通常使用的一个平面取值为0x0到0xFFFF,这个平面被成为BMP(Basic Multilingual Plane)即“平面0”。由于这个平面只是用两个字节就可以完整表示,字符集又可以成为UCS-2(即使用2字符的“通用字符集”,UCS-4即为4字节,将UCS-4的高两位去除即为UCS-2)。

  我们常用的27973个汉字都存放与平面0上,整个Unicode共定义了71226个汉字(Unicode5.0.0),平面2的43253个字符都是汉字。

  

  UTF之间的关系和转换。上文说道,“汉”字需要3个UTF8字节来存储,这是因为要符合UTF格式的规范。

  一个模版可以告诉我们UTF8是怎样存储数据的:

Unicode编码(16进制) 
UTF-8 字节流(二进制)
000000 - 00007F
0xxxxxxx
000080 - 0007FF
110xxxxx 10xxxxxx
000800 - 00FFFF
1110xxxx 10xxxxxx 10xxxxxx
010000 - 10FFFF
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

  可以看到,如果使用两个字节来存储数据,UTF8最多可以存储2^11个字符,最大的数字为0x7FF即2047,显然无法存下数字为23383的“汉”字。

  而通过模版我们也可以看出Unicode的最大bit数为21。

 

  数字向UTF的转换也很简单了,把数字换成二进制(不足21位的高位补0),然后填入对应UTF的模版(UTF16和UTF32的模版大家请自行查看,LE和BE区别在于高位和低位的位置,Windows和Linux为LE,MacOS为BE)中替换xxxxxxx就行了!很简单吧。

  UTF在文件中的存储。UTF格式在文件中总有固定文件头:

UTF编码
Byte Order Mark
UTF-8
EF BB BF
UTF-16LE
FF FE
UTF-16BE
FE FF
UTF-32LE
FF FE 00 00
UTF-32BE
00 00 FE FF

 

  如“汉”字在文件中的存储(不包括头):

Unicode编码
UTF-16LE 
UTF-16BE 
UTF32-LE 
UTF32-BE
0x006C49
49 6C
6C 49
49 6C 00 00
00 00 6C 49

  

  各个系统和语言对Unicode的支持:

    Windows NT从底层支持Unicode(不幸的是,Windows 98只是小部分支援Unicode)。先天即被ANSI束缚的C程序设计语言通过对宽字元集的支持来支持Unicode。

    Windows底层使用UTF16,Linux使用UTF32(未考证)。

    C#和Java支持UTF16且是默认行为(如字符串天生为UTF16格式字符数组,Java还可以使用'\uxxxx'格式声明一个字符)。

    XML及其子集HTML对UTF16支持很好,为跨平台你可以使用'&#xxxx;'来声明一个字符。

 

 

 欢迎您移步我们的交流群,无聊的时候大家一起打发时间:Programmer Union

 或者通过QQ与我联系:点击这里给我发消息

 (最后编辑时间2013-09-17 20:59:38)

 

转载于:https://www.cnblogs.com/Johness/p/3322445.html

这篇关于字符集和编码——Unicode(UTFUCS)深度历险的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/363770

相关文章

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

Python动态处理文件编码的完整指南

《Python动态处理文件编码的完整指南》在Python文件处理的高级应用中,我们经常会遇到需要动态处理文件编码的场景,本文将深入探讨Python中动态处理文件编码的技术,有需要的小伙伴可以了解下... 目录引言一、理解python的文件编码体系1.1 Python的IO层次结构1.2 编码问题的常见场景二

MySQL批量替换数据库字符集的实用方法(附详细代码)

《MySQL批量替换数据库字符集的实用方法(附详细代码)》当需要修改数据库编码和字符集时,通常需要对其下属的所有表及表中所有字段进行修改,下面:本文主要介绍MySQL批量替换数据库字符集的实用方法... 目录前言为什么要批量修改字符集?整体脚本脚本逻辑解析1. 设置目标参数2. 生成修改表默认字符集的语句3

Java中字符编码问题的解决方法详解

《Java中字符编码问题的解决方法详解》在日常Java开发中,字符编码问题是一个非常常见却又特别容易踩坑的地方,这篇文章就带你一步一步看清楚字符编码的来龙去脉,并结合可运行的代码,看看如何在Java项... 目录前言背景:为什么会出现编码问题常见场景分析控制台输出乱码文件读写乱码数据库存取乱码解决方案统一使

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3