字符编码--UTF-16

article/2025/11/11 3:55:32

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

第4节 UTF-16

UTF-16是Unicode字符编码五层次模型的第三层:字符编码表(Character Encoding Form,也称为"storage format")的一种实现方式。即把Unicode字符集的抽象码位映射为16位长的整数(即码元)的序列,用于数据存储或传递。Unicode字符的码位,需要1个或者2个16位长的码元来表示,因此这是一个变长表示。

UTF是"Unicode/UCS Transformation Format"的首字母缩写,即把Unicode字符转换为某种格式之意。UTF-16正式定义于ISO/IEC 10646-1的附录C,而RFC2781也定义了相似的做法。

UTF-16描述

Unicode的编码空间从U+0000到U+10FFFF,共有1,112,064个码位(code point)可用来映射字符. Unicode的编码空间可以划分为17个平面(plane),每个平面包含216(65,536)个码位。17个平面的码位可表示为从U+xx0000到U+xxFFFF,其中xx表示十六进制值从0016到1016,共计17个平面。第一个平面称为基本多语言平面(Basic Multilingual Plane, BMP),或称第零平面(Plane 0)。其他平面称为辅助平面(Supplementary Planes)。基本多语言平面内,从U+D800到U+DFFF之间的码位区段是永久保留不映射到Unicode字符。UTF-16就利用保留下来的0xD800-0xDFFF区段的码位来对辅助平面的字符的码位进行编码。

从U+0000至U+D7FF以及从U+E000至U+FFFF的码位

第一个Unicode平面(码位从U+0000至U+FFFF)包含了最常用的字符。该平面被称为基本多语言平面,缩写为BMP(Basic Multilingual Plane, BMP)。UTF-16与UCS-2编码这个范围内的码位为16比特长的单个码元,数值等价于对应的码位. BMP中的这些码位是仅有的可以在UCS-2中表示的码位.

 

从U+10000到U+10FFFF的码位

辅助平面(Supplementary Planes)中的码位,在UTF-16中被编码为一对16比特长的码元(即32bit,4Bytes),称作代理对(surrogate pair),具体方法是:

 

UTF-16解码

lead \ trail

DC00

DC01

   …  

DFFF

D800

10000 10001 … 103FF

D801

10400 10401 … 107FF

  ⋮

⋮ ⋮ ⋱ ⋮

 

DBFF

10FC00 10FC01 … 10FFFF

码位减去0x10000,得到的值的范围为20比特长的0..0xFFFFF.

高位的10比特的值(值的范围为0..0x3FF)被加上0xD800得到第一个码元或称作高位代理(high surrogate),值的范围是0xD800..0xDBFF.由于高位代理比低位代理的值要小,所以为了避免混淆使用,Unicode标准现在称高位代理为前导代理(lead surrogates).

低位的10比特的值(值的范围也是0..0x3FF)被加上0xDC00得到第二个码元或称作低位代理(low surrogate),现在值的范围是0xDC00..0xDFFF.由于低位代理比高位代理的值要大,所以为了避免混淆使用,Unicode标准现在称低位代理为后尾代理(trail surrogates).

上述算法可理解为:辅助平面中的码位从U+10000到U+10FFFF,共计FFFFF个,即220=1,048,576个,需要20位的空间来表示。如果用两个16位长的整数组成的序列来表示,第一个整数(称为前导代理)要容纳上述20位空间的前10位,第二个整数(称为后尾代理)容纳容纳上述20位空间的后10位。还要能根据16位整数的值直接判明属于前导整数代理的值的范围(210=1024),还是后尾整数代理的值的范围(也是210=1024)。因此,需要在基本多语言平面中保留不对应于Unicode字符的2048个码位,就足以容纳前导代理与后尾代理所需要的编码空间。这对于基本多语言平面总计65536个码位来说,仅占3.125%.

由于前导代理、后尾代理、BMP中的有效字符的码位,三者互不重叠,搜索是简单的:一个字符编码的一部分不可能与另一个字符编码的不同部分相重叠。这意味着UTF-16是自同步(self-synchronizing):可以通过仅检查一个码元就可以判定给定字符的下一个字符的起始码元. UTF-8也有类似优点,但许多早期的编码模式就不是这样,必须从头开始分析文本才能确定不同字符的码元的边界.

由于最常有的字符都在基本多文种平面中,许多软件的处理代理对的部分往往得不到充分的测试。这导致了一些长期的bug与潜在安全漏洞,甚至在广为流行得到良好评价的应用软件.

从U+D800到U+DFFF的码位

Unicode标准规定U+D800..U+DFFF的值不对应于任何字符.

但是在使用UCS-2的时代,U+D800..U+DFFF内的值被占用,用于某些字符的映射。但只要不构成代理对,许多UTF-16编码解码还是能把这些不符合Unicode标准的字符映射正确的辨识、转换成合规的码元[2].按照Unicode标准,这种码元序列本来应算作编码错误.

 

范例:UTF-16编码程序

假设要将U+64321 (16进位)转成UTF-16编码.因为它超过U+FFFF,所以他必须编译成32位元(4个byte)的格式,如下所示:11

V = 0x64321

Vx = V - 0x10000

= 0x54321

= 0101 0100 0011 0010 0001

 

Vh = 01 0101 0000 // Vx的高位部份的10 bits

Vl = 11 0010 0001 // Vx的低位部份的10 bits

w1 = 0xD800 //結果的前16位元初始值

w2 = 0xDC00 //結果的後16位元初始值

 

w1 = w1 | Vh

= 1101 1000 0000 0000

   |        01 0101 0000

= 1101 1001 0101 0000

= 0xD950

 

w2 = w2 | Vl

= 1101 1100 0000 0000

   |        11 0010 0001

= 1101 1111 0010 0001

= 0xDF21

 

 

所以这个字U+64321最后正确的UTF-16编码应该是:

0xD950 0xDF21

而在小尾序中最后的编码应该是:

0x50D9 0x21DF

因为这个字超过U+FFFF所以无法用UCS-2的格式编码

16进制编码范围 UTF-16表示方法(二进制)10进制码范围 字节数量

U+0000---U+FFFF xxxxxxxx xxxxxxxx yyyyyyyy yyyyyyyy 0-65535 2

U+10000---U+10FFFF 110110yyyyyyyyyy 110111xxxxxxxxxx 65536-1114111 4

 

UTF-16比起UTF-8,好处在于大部分字符都以固定长度的字节(2字节)储存,但UTF-16却无法相容于ASCII编码。

 

UTF-16的编码模式

UTF-16的大尾序和小尾序储存形式都在用。一般来说,以Macintosh制作或储存的文字使用大尾序格式,以Microsoft或Linux制作或储存的文字使用小尾序格式。

 

为了弄清楚UTF-16文件的大小尾序,在UTF-16文件的开首,都会放置一个U+FEFF字符作为Byte Order Mark(UTF-16LE以FF FE代表,UTF-16BE以FE FF代表),以显示这个文字档案是以UTF-16编码,其中U+FEFF字符在UNICODE中代表的意义是ZERO WIDTH NO-BREAK SPACE,顾名思义,它是个没有宽度也没有断字的空白。

 

以下的例子有四个字符:“朱”(U+6731)、半角逗号(U+002C)、“聿”(U+807F)、“?”(U+2A6A5)。

 

 

使用UTF-16编码的例子

 

编码名称

编码次序

编码

 

 

BOM

 

 

,

 

 

?

 

UTF-16LE 小尾序  31 67 2C 00 7F 80 69 D8 A5 DE

UTF-16BE 大尾序  67 31 00 2C 80 7F D8 69 DE A5

UTF-16 小尾序,包含BOM FF FE 31 67 2C 00 7F 80 69 D8 A5 DE

UTF-16 大尾序,包含BOM FE FF 67 31 00 2C 80 7F D8 69 DE A5

 

UTF-16与UCS-2的关系

UTF-16可看成是UCS-2的父集。在没有辅助平面字符(surrogate code points)前,UTF-16与UCS-2所指的是同一的意思。但当引入辅助平面字符后,就称为UTF-16了。现在若有软件声称自己支援UCS-2编码,那其实是暗指它不能支援在UTF-16中超过2bytes的字集。对于小于0x10000的UCS码,UTF-16编码就等于UCS码。

 

Microsoft Windows操作系统内核对Unicode的支持

Windows操作系统内核中的字符表示为UTF-16小尾序,可以正确处理、显示以4字节存储的字符。但是Windows API实际上仅能正确处理UCS-2字符,即仅以2字节存储的,码位小于U+FFFF的Unicode字符。其根源是Microsoft C++语言把wchar_t数据类型定义为16比特的unsigned short,这就与一个wchar_t型变量对应一个宽字符,可以存储一个Unicode字符的规定相矛盾。相反,Linux平台的GCC编译器规定一个wchar_t是4字节长度,可以存储一个UTF-32字符,宁可浪费了很大的存储空间。下例运行于Windows平台的C++程序可说明此点:

 

 

// 此源文件在Windows平台上必须保存为Unicode格式(即UTF-16小尾)

// 因为包含的汉字“?”,不能在简体中文版Windows默认的代码页936(即GBK)中表示。

// 该汉字在UTF-16小尾序中用4个字节表示,

// Windows操作系统能正确显示这样的在UTF-16需用4字节表示的字符

// 但是Windows API不能正确处理这样的在UTF-16需用4字节表示的字符,把它判定为2个UCS-2字符

 

#include <windows.h>

int main()

{

              const wchar_t lwc[]=L"?";

 

              MessageBoxW(NULL, lwc, lwc, MB_OK);

 

              int i = wcslen(lwc);

              printf("%d\n", i);

              int j = lstrlenW(lwc);

              printf("%d\n", j);

 

              return 0;

}

 

 

 

 

宽字符

宽字元(Wide character) 是程式设计的术语。它是一个抽象的术语(没有规定具体实现细节),用以表示比8位元字元还宽的资料类型。它不同于Unicode。

 

wchar_t在ANSI/ISO C中是一个资料类型,且某些其它的程式语言也用它来表示宽字元。

 

Unicode标准4.0中提到

"ANSI/ISO C leaves the semantics of the wide character set to the specific implementation but requires that the characters from the portable C execution set correspond to their wide character equivalents by zero extension."

还有

“wchar_t的宽度属于编译器的特性,且可以小到8位元。所以程式若需要跨过所有C和C++ 编译器的可携性,就不应使用wchar_t储存Unicode文字。wchar_t类型是为储存编译器定义的宽字元,在部分编译器中,其可以是Unicode字元。”

在Windows API中,wchar_t是16位元宽。Windows API因不使wchar_t字元类型在单一wchar_t单元中,支援所有系统可表示的字元,而破坏了ANSI/ISO C标准。wchar_t在Windows下,反而表示一个UTF-16小尾字元(或UTF-16的一部分)。

 

在类Unix系统中,wchar_t是32位元宽。

 

在ANSI C程式库表头档中,<wchar.h>和<wctype.h>处理宽字元。

 

字符编码--UTF-32

转载于:https://my.oschina.net/dubenju/blog/823237


http://chatgpt.dhexx.cn/article/Af1rbW0P.shtml

相关文章

蔡勒公式、三角函数

1.蔡勒公式 2.三角函数

蔡勒(Zeller)公式及其推导:快速将任意日期转换为星期数

0. 本文的初衷及蔡勒公式的用处 前一段时间&#xff0c;我在准备北邮计算机考研复试的时候&#xff0c;做了几道与日期计算相关的题目&#xff0c;在这个过程中我接触到了蔡勒公式。先简单的介绍一下蔡勒公式是干什么用的。 我们有时候会遇到这样的问题&#xff1a;看到一个日期…

1185.一周中的几天 四种解法(java),主要新学一下蔡勒公式

题目 给你一个日期&#xff0c;请你设计一个算法来判断它是对应一周中的哪一天。 输入为三个整数&#xff1a;day、month 和 year&#xff0c;分别表示日、月、年。 您返回的结果必须是这几个值中的一个 {“Sunday”, “Monday”, “Tuesday”, “Wednesday”, “Thursday”…

给定日期(年月日)求星期几(蔡勒公式?没那么简单!)

前言 前几日做到一个机试题&#xff0c;给出一个日期&#xff0c;让你输出那天是星期几&#xff0c;这种题无疑两种思路&#xff1a;一是从今天&#xff08;前提是知道今天日期及周几&#xff09;开始推算&#xff0c;计算今天与目标日期差的天数再取模运算&#xff0c;考虑到…

欧拉计划题-19 (蔡勒公式)

欧拉计划题19 前言一 题目描述二 题解分析1.暴力求解&#xff08;低配版解法&#xff09;2.蔡勒公式&#xff08;公式法&#xff09; 三 题解代码 前言 欧拉计划是学习数学、数论选手遨游的海洋&#xff0c;700道题让你我越来越强。 打卡网址链接: link. 一 题目描述 题目链接…

C语言——蔡勒(Zeller)公式:快速将任意日期转换为星期数

蔡勒公式 情景引入公式介绍公式细节代码实现 情景引入 在日常生活中&#xff0c;我们有时候会遇到这样的问题&#xff1a;看到一个日期想知道这一天是星期几。对于这个问题&#xff0c;如果用编程的方式&#xff0c;应该怎么实现呢&#xff1f;你可能已经有思路了&#xff0c;比…

自用笔记58——蔡勒(Zeller)公式

请你编写一个程序来计算两个日期之间隔了多少天。 日期以字符串形式给出&#xff0c;格式为 YYYY-MM-DD&#xff0c;如示例所示。 示例 1&#xff1a; 输入&#xff1a;date1 “2019-06-29”, date2 “2019-06-30” 输出&#xff1a;1 示例 2&#xff1a; 输入&#xff1…

蔡勒公式与Python

蔡勒公式 &#xff08; Zeller formula&#xff09; 作用&#xff1a;从年月日推算星期几 来源&#xff1a;罗马教皇格里高利十三世在1582年组织了一批天文学家&#xff0c;根据哥白尼日心说计算出来的数据&#xff0c;对儒略历作了修改。将1582年10月5日到14日之间的10天宣布…

蔡勒(Zeller)公式理解Get(√)

Preface 偶然做到日期相关题目&#xff0c;了解到Zeller公式。不甘心停留在使用阶段&#xff0c;便想掌握其推导过程。 只适用于格利戈里历法&#xff0c;也就是现今的公历。 1. Zeller公式 标准形式 计算1582年10月4日或之前日期 (罗马教皇决定在1582年10月4日后使用格利戈里…

C语言——蔡勒(Zeller)公式的使用

C语言——蔡勒公式的使用 蔡勒公式简介&#xff1a; 蔡勒&#xff08;Zeller&#xff09;公式&#xff0c;是一个计算星期的公式&#xff0c;随便给一个日期&#xff0c;就能用这个公式推算出是星期几。 计算公式&#xff1a; 核心公式&#xff1a; w(y[y/4][c/4]-2c[26(m1…

c++ operator百样操作符重载(详解)

目录 一、operator &#xff1a;等号判断重载 二、operator &#xff1a; 等号赋值重载 三、operator ! : 不等于重载 四、operator> &#xff1a; 大于号 或者 小于号 重载 五、operator << &#xff1a;输入重定向重载 六、operator &#xff1a;加号重载 …

操作符重载!看这篇就够了!

实现一个操作符重载的方式通常有两种情况&#xff1a; 将操作符重载实现为类的成员函数。操作符重载实现为非类的成员函数(即全局函数)。 将操作符重载实现为类的成员函数 在类体中声明(定义)需要重载的操作符&#xff0c;声明方式跟普通的成员函数一样&#xff0c;只不过操作符…

C++基本操作符重载

基本操作符重载 基本操作符重载reference 基本操作符重载 操作符重载指的是将 C 提供的操作符进行重新定义&#xff0c;使之满足我们所需要的一些功能。 在 C 中可以重载的操作符有&#xff1a; - * / % ^ & | ~ ! < > - * / % ^ & | <…

【Groovy】map 集合 ( map 集合操作符重载 | + 操作符重载 | 代码示例 )

文章目录 一、map 集合 " " 操作符重载二、代码示例 一、map 集合 " " 操作符重载 对 map 集合使用 " " 操作符 , 操作符两侧都是 map 集合 , 调用的是 map 集合的 plus 方法 , plus 函数有 2 2 2 个参数 : 第一个参数 , Map<K, V> l…

【Groovy】map 集合 ( map 集合操作符重载 | - 操作符重载 | 代码示例 )

文章目录 一、map 集合 " - " 操作符重载二、完整代码示例 一、map 集合 " - " 操作符重载 对 map 集合 使用 " - " 操作符 , 相当于调用了 map 集合的 minus 方法 , 该方法传入 2 2 2 个参数 : Map<K,V> self 参数 : 相当于 " - &…

【C++】操作符重载

注意&#xff1a;操作符重载可以当做“自定义类的运算” 1 为什么需要操作符重载&#xff1f; 对于基础的变量&#xff0c;int等&#xff0c;不需要重载就知道如何做&#xff0c;但是对于自定义类&#xff0c;就无法进行运算&#xff0c;比如复数类。 2 操作符重载总结 1&…

C++ 操作符重载

输出操作符"<<" 和输入操作运算符">>" 操作符重载&#xff0c;也叫运算符重载&#xff0c;是C的重要组成部分&#xff0c;它可以让程序更加的简单易懂&#xff0c;简单的运算符使用可以使复杂函数的理解更直观。 操作符重载可对 已有的运算…

C++-操作符重载

定义&#xff1a; Salesitem.h /* * This file contains code from "C Primer, Fifth Edition", by Stanley B. * Lippman, Josee Lajoie, and Barbara E. Moo, and is covered under the * copyright and warranty notices given in that book: * * "Copyrig…

c++操作符重载

转自https://www.cnblogs.com/xudong-bupt/p/3557525.html 1.什么是操作符重载 可以使用分词将操作符重载理解为&#xff1a;操作符重载。 C中的操作符很多&#xff0c;如&#xff0c;-&#xff0c;*&#xff0c;\等等。 C中的重载也是C中面向对象多态的体现。 简单说操作符重…

操作符重载——C/C++学习笔记

此篇文章来自于网上&#xff0c;作为自己学习中的笔记&#xff0c;若有侵权行为&#xff0c;请告之&#xff0c;24小时之内必删除&#xff01;下面就转入正题吧&#xff01; 一、什么是操作符重载&#xff1f; 一看到重载&#xff0c;很容易就让人联想到成员函数重载&#xff0…