关于UTF编码的codepoint

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

(注:部分内容从豆包ai询问中获取)

1. 关于utf编码

关于utf编码(Unicode Transformation Format编码),也称为unicode编码。
它的核心就是codepoint,对于utf编码来说,无论utf8, utf16, utf32,同一个字或字符,对应的codepoint是唯一的。
这也是utf编码作为通用编码格式,编码到不同的编码字节utf8, utf16, utf32,但又能互相简单的迅速互转的原因。

2. utf编码的codepoint:

Unicode 码点范围是从 U+0000 到 U+10FFFF,总共涵盖了 1,114,112 个码点。
百万个codepoint表达范围,基本涵盖了世界上所有的主流语言文字的编码。
UTF 编码理论上为表示世界上所有文字提供了可能,但实际上并没有完全涵盖世界上的所有文字,仍存在一些文字未被包含其中,主要原因在于这些文字较为生僻、使用范围狭窄,或者是新近发现的文字。
但Unicode 联盟始终致力于扩充编码范围,以涵盖更多的文字和符号。每隔一段时间就会发布新的版本,不断将新发现或有需求的文字添加到编码体系中。所以,Unicode 通过 UTF 编码表示的文字范围在持续扩大,逐渐向涵盖世界所有文字的目标迈进。

3. UTF-8编码

码点范围及字节表示
1 字节编码 码点范围:U+0000 到 U+007F。
二进制表示:以 0 开头,后续 7 位用于表示码点,即 0b0xxxxxxx。这个范围涵盖了标准 ASCII 字符集,共 128 个字符,与 ASCII 编码完全兼容。
2 字节编码 码点范围:U+0080 到 U+07FF。
二进制表示:第一个字节以 110 开头,第二个字节以 10 开头,形式为 110xxxxx 10xxxxxx。这里的 x 代表用于表示码点的位,总共能表示 1,920 个不同的码点。
3 字节编码 码点范围:U+0800 到 U+FFFF。
二进制表示:第一个字节以 1110 开头,后两个字节都以 10 开头,形式为 1110xxxx 10xxxxxx 10xxxxxx。该范围可表示 63,488 个不同的码点,涵盖了绝大多数常见的文字,像汉字、拉丁扩展字符等。
4 字节编码 码点范围:U+10000 到 U+10FFFF。
二进制表示:第一个字节以 11110 开头,后面三个字节都以 10 开头,形式为 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx。此范围能表示 1,048,576 个不同的码点,主要用于表示一些辅助平面字符,例如表情符号、古代文字等。
能表达的文字数量:由于 UTF - 8 可以表示 Unicode 全部码点范围,所以它能表达的文字数量是 1,114,112 个。

4. UTF-16编码

码点范围及字节表示
基本多文种平面(BMP):U+0000 到 U+FFFF,使用 2 个字节表示,共 65,536 个码点,包含了世界上大多数常用语言的字符。
辅助平面:U+10000 到 U+10FFFF,使用 4 个字节(代理对)表示,共 1,048,576 个码点。
能表达的文字数量:同样可以表示 Unicode 全部码点范围,即 1,114,112 个。
UTF - 16 对于 BMP 内的字符使用 2 字节编码,对于辅助平面的字符使用 4 字节编码。
另外字节序的选择会影响字节的存储和传输顺序,使用 BOM 能明确字节序。

UTF-16 存在两种字节序:

大端序(UTF - 16BE):数据的高位字节存于低地址,低位字节存于高地址。
小端序(UTF - 16LE):数据的低位字节存于低地址,高位字节存于高地址。
在文件或者网络传输时,为了明确字节序,有时会在文件开头使用字节序标记(BOM, Byte Order Mark):
UTF - 16BE 的 BOM 是 0xFEFF。
UTF - 16LE 的 BOM 是 0xFFFE。

UTF-16 的四字节编码:

utf总共的codepoint范围是[0, 0x10FFFF]-共1114112个,对于utf16编码来说,2字节分了[0, 0xfffff]共65536个,延长到4字节时,分到了[0x10000, 0x10ffff],共计1048576个,也即2的20次方个。
所以映射范围的话,如果映射到从0开始的范围,就映射到[0, 1048575],也即[0, 0xfffff],不过utf16可能考虑特殊处理的原因吧,是把这个20位拆分成两个10位,分别存到两个双字节中。
并且考虑了字节序的因素:把双字节的前6位设定固定值,后10位留做数据codepoint的值存储。
第一个双字节的前缀使用0b110110(0xD8);
第二个双字节的前缀使用0b110111(0xDC);
这样拆分来看,存储时,首先向下偏移0x10000,把utf16一个双字节的范围偏移走,然后,每个分10位存储:
偏移后的范围 V = (codepoint - 0x10000);
第一个双字节存储值 – 高代理项HS = (V >> 10) + 0xD800;
第二个双字节存储值 – 低代理项LS = (V & 0x3ff) + 0xDC00;

5. UTF-32编码

码点范围及字节表示:UTF - 32 是固定长度编码,每个码点都用 4 个字节表示,其码点范围就是整个 Unicode 范围,从 U+0000 到 U+10FFFF。
能表达的文字数量:也是 1,114,112 个。

6. 综述

综上所述,三种 UTF 编码方式都能够表示 Unicode 定义的全部 1,114,112 个码点,但在不同的应用场景下,它们的使用效率和特点有所不同。例如,UTF - 8 对 ASCII 字符友好,节省存储空间;UTF - 16 在一些系统内部处理时较为方便;UTF - 32 则具有简单直接的特点。

7. 编码样例

下面是 “我” 字在不同编码格式下的具体表示:

Unicode 码点(Code Point)

“我” 字的 Unicode 码点是 U+6211,对应的十进制数值为 25105。

UTF-8 编码

UTF - 8 编码采用三字节来表示 “我” 字,具体字节内容如下:
第一个字节:11100110(十六进制表示为 E6,十进制为 230)
第二个字节:10001000(十六进制表示为 88,十进制为 136)
第三个字节:10010001(十六进制表示为 91,十进制为 145)
完整的 UTF - 8 编码,用十六进制表示是E6 88 91,二进制表示为11100110 10001000 10010001

UTF - 16 编码

UTF - 16 编码使用双字节(即一个代码单元)来表示 “我” 字,具体内容为:
高字节:01100010(十六进制表示为 62,十进制为 98)
低字节:00010001(十六进制表示为 11,十进制为 17)
完整的 UTF - 16 编码,用十六进制表示是62 11,二进制表示为01100010 00010001

需要注意的是,UTF - 16 存在大端序(BE)和小端序(LE)两种格式:
UTF - 16BE 的十六进制表示为6211
UTF - 16LE 的十六进制表示为1162

UTF - 32 编码

UTF - 32 编码固定使用四字节来表示 “我” 字,具体内容如下:
字节 1:00000000(十六进制表示为 00,十进制为 0)
字节 2:00000000(十六进制表示为 00,十进制为 0)
字节 3:01100010(十六进制表示为 62,十进制为 98)
字节 4:00010001(十六进制表示为 11,十进制为 17)
完整的 UTF - 32 编码,用十六进制表示是00006211,二进制表示为00000000 00000000 01100010 00010001

同样,UTF - 32 也有大端序(BE)和小端序(LE)之分:
UTF - 32BE 的十六进制表示为00006211
UTF - 32LE 的十六进制表示为11620000

对照表
编码方式字节表示(十六进制)字节数量
UTF - 8E6 88 913
UTF - 1662 11(BE)或 11 62(LE)2
UTF - 3200 00 62 11(BE)或 11 62 00 00(LE)4

8. CodePoint与utf8码转换代码样例

codepoint 转 utf8

std::string CodePointToUtf8(unsigned int codepoint)
{
  // 范围校验
  if (codepoint > 0x10ffff){return "";}
  std::string str;
  if (codepoint <= 0x7f){
    str.push_back((unsigned char)codepoint);
  }
  else if (codepoint <= 0x7ff){
    unsigned char ch1 = (codepoint & 0x3f) | 0x80;
    unsigned char ch2 = ((codepoint >> 6) & 0x1f) | 0xc0;
    str.push_back(ch2);
    str.push_back(ch1);
  }
  else if (codepoint <= 0xffff){
    unsigned char ch1 = (codepoint & 0x3f) | 0x80;
    unsigned char ch2 = ((codepoint >> 6) & 0x3f) | 0x80;
    unsigned char ch3 = ((codepoint >> 12) & 0x0f) | 0xe0;
    str.push_back(ch3);
    str.push_back(ch2);
    str.push_back(ch1);
  }
  else {
    unsigned char ch1 = (codepoint & 0x3f) | 0x80;
    unsigned char ch2 = ((codepoint >> 6) & 0x3f) | 0x80;
    unsigned char ch3 = ((codepoint >> 12) & 0x3f) | 0x80;
    unsigned char ch4 = ((codepoint >> 18) & 0x07) | 0xf0;
    str.push_back(ch4);
    str.push_back(ch3);
    str.push_back(ch2);
    str.push_back(ch1);
  }
  return str;
}

codepoint 转 utf16

std::string CodePointToUtf16(unsigned int codepoint, bool isBigEndian = true)
{
  // 范围校验
  if (codepoint > 0x10ffff){return "";}
  std::string str;
  if (codepoint <= 0xffff){
    unsigned char ch1 = codepoint & 0xff;
    unsigned char ch2 = (codepoint >> 8) & 0xff;
    str.push_back(ch2);
    str.push_back(ch1);
  }
  else {
    unsigned int ajust = codepoint - 0x10000;
    unsigned char ch1 = (ajust & 0x3ff) & 0xff;
    unsigned char ch2 = ((ajust & 0x3ff) >> 8) | 0xdc;
    unsigned char ch3 = (ajust >> 10) & 0xff;
    unsigned char ch4 = ((ajust >> 10) >> 8) | 0xd8;
    str.push_back(ch4);
    str.push_back(ch3);
    str.push_back(ch2);
    str.push_back(ch1);
  }
  if (isBigEndian == false){
    std::reverse(str.begin(), str.end());
  }
  return str;
}

codepoint 转 utf32

std::string CodePointToUtf32(unsigned int codepoint, bool isBigEndian = true)
{
  // 范围校验
  if (codepoint > 0x10ffff){return "";}
  std::string str;
  {
    unsigned char ch1 = codepoint & 0xff;
    unsigned char ch2 = (codepoint >> 8) & 0xff;
    unsigned char ch3 = (codepoint >> 16) & 0xff;
    unsigned char ch4 = (codepoint >> 24) & 0xff;
    str.push_back(ch4);
    str.push_back(ch3);
    str.push_back(ch2);
    str.push_back(ch1);
  }
  if (isBigEndian == false){
    std::reverse(str.begin(), str.end());
  }
  return str;
}

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春夜喜雨

稀罕你的喜欢!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值