Unicode字符集和UTF编码

文章目录

  • 前言
  • 一、字符集和编码方式
  • 二、unicode字符集
    • utf32编码
    • utf8编码
      • utf8编码函数示例
      • utf8解码函数示例
    • utf16编码
      • utf16编码解码函数示例
  • 总结


前言

本文详细介绍 u n i c o d e unicode unicode 字符集和其相关的三种编码方式: u t f 8 utf8 utf8 u t f 16 utf16 utf16 u t f 32 utf32 utf32,并给出一个编码和解码的参考程序。

一、字符集和编码方式

字符集是一些字符的集合,字符集中每一个字符有一个唯一的字符编码表示该字符,编码方式规定了计算机存储该字符集中字符编码的规则,也是计算机解读一串二进制序列的规则。
1: A S C I I ASCII ASCII 码用 7 b i t ( 0 x 00 − 0 x 7 f ) 7bit \ (0x00-0x7f) 7bit (0x000x7f) 存储英文字符,字符集为 128 128 128 个英文字符,即 A S C I I ASCII ASCII 字符集。 A S C I I ASCII ASCII 码的编码方式类似直接映射,字母 A A A 对应的字符编码是 65 65 65,在 A S C I I ASCII ASCII 编码下为 0 x 41 0x41 0x41。字符编码的值也是 A S C I I ASCII ASCII 码值。
2: A S C I I ASCII ASCII 字符集的缺陷非常直观:只包含英文字符。
3: U n i c o d e Unicode Unicode 是国际标准字符集,它将世界各种语言的每个字符定义一个唯一的字符编码,以满足跨语言、跨平台的文本信息转换。 2023 2023 2023 9 9 9 月发表的 15.1 15.1 15.1 版本中定义了 149813 149813 149813 个字符。规定 U n i c o d e Unicode Unicode 字符编码存储方式的规则主要有三种: u t f 8 utf8 utf8 u t f 16 utf16 utf16 u t f 32 utf32 utf32

二、unicode字符集

U n i c o d e Unicode Unicode 为每一个字符分配一个唯一的字符编码,称为在编码空间中的一个码点 ( c o d e p o i n t ) (code \ point) (code point) U n i c o d e Unicode Unicode 标准给定编码空间为 U+0000 - U+10FFFF。码点以 U + U+ U+ 开头,最少用 4 4 4 个十六进制数表示,若有前导 0 0 0 不可省略。例如: U + 00 F 7 U+00F7 U+00F7 表示除法符号 ÷ ÷ ÷

编码空间中有效码点个数为: 2 20 + ( 2 16 − 2 11 ) = 1112064 2^{20} + (2^{16} − 2^{11}) = 1112064 220+(216211)=1112064。其减掉的 2 11 2^{11} 211 主要原因在于 u t f 16 utf16 utf16 编码的编码方式限制,在范围 U+D800 - U+DFFF U n i c o d e Unicode Unicode 并未编码字符。

下表随机列了几个 U n i c o d e Unicode Unicode 码点和其表示的字符之间的对应关系:

码点Value
U+2118 P \huge\mathscr{P} P
U+A015
U+FE18

另外,按照码点范围区分了不同平面,以下为具体平面名称:

码点范围平面
U+0000-U+FFFF基本多文种平面
U+10000-U+1FFFF多文种补充平面
U+20000-U+2FFFF表意文字补充平面
U+30000-U+DFFFF表意文字第三平面
U+E0000-U+EFFFF特别用途补充平面
U+F0000-U+FFFFF保留作为私人使用区域A区
U+100000-U+10FFFF保留作为私人使用区域B区

基本多文种平面包含了绝大部分常用字符,例如: U + 0980 − U + 09 F F U+0980-U+09FF U+0980U+09FF 为孟加拉文, U + 25 A 0 − U + 25 F F U+25A0-U+25FF U+25A0U+25FF 为几何图形, U + 1800 − U + 18 A F U+1800-U+18AF U+1800U+18AF 为蒙古文,等等。具体见:Unicode符号表

utf32编码

u t f 32 utf32 utf32 编码方式非常简单直观:用 32 b i t 32bit 32bit 直接表示一个 U n i c o d e Unicode Unicode 码点,因此其也被称为定长编码。

1 1 1 U n i c o d e Unicode Unicode 标准规定的编码空间: U+0000 - U+10FFFF。最长需要 3 3 3 个字节表示, 4 4 4 字节完全够用。

2 2 2:以码点 U + 0041 U+0041 U+0041 字符 A A A 为例,其 u t f 32 utf32 utf32 编码结果为: 0 x 00000041 0x00000041 0x00000041。直观来讲, u t f 32 utf32 utf32 编码方式相当于把码点零扩展到 32 b i t 32bit 32bit。类似的, A S C I I ASCII ASCII 码也是一样的,零扩展到 7 b i t 7bit 7bit 表示。

缺点:
1 1 1 u t f 32 utf32 utf32 编码最大的缺点在于占用空间过大。假设一个文件内容只包含 A S C I I ASCII ASCII 字符集中的字符,那么用 u t f 8 utf8 utf8 来存储所需的空间是用 u t f 32 utf32 utf32 来存储的 1 / 4 1/4 1/4
2 2 2 u t f 32 utf32 utf32 不兼容 A S C I I ASCII ASCII 码。即:同样一个十六进制表示 0 x 41 0x41 0x41,在 A S C I I ASCII ASCII u t f 8 utf8 utf8 两种编码中表示内容一样且都为字符 A A A 的合法编码。

utf8编码

u t f 8 utf8 utf8 编码和 u t f 16 utf16 utf16 都为变长编码。 u t f 8 utf8 utf8 1 − 4 1-4 14 字节来表示一个特定字符。具体编码规则如下所示:

码点范围码点二进制表示编码规则字节数
U + 0000 − U + 007 F U+0000-U+007F U+0000U+007F 0 b x x x x x x x 0bxxxxxxx 0bxxxxxxx 0 b 0 x x x x x x x 0b0xxxxxxx 0b0xxxxxxx1字节
U + 0080 − U + 07 F F U+0080-U+07FF U+0080U+07FF 0 b x x x x x x x x x x x 0bxxx \ xxxx\ xxxx 0bxxx xxxx xxxx 0 b 110 x x x x x 10 x x x x x x 0b110xxxxx \ 10xxxxxx 0b110xxxxx 10xxxxxx2字节
U + 0800 − U + F F F F U+0800-U+FFFF U+0800U+FFFF 0 b x x x x x x x x x x x x x x x x 0bxxxx\ xxxx \ xxxx \ xxxx 0bxxxx xxxx xxxx xxxx 0 b 1110 x x x x 10 x x x x x x 10 x x x x x x 0b1110xxxx \ 10xxxxxx \ 10xxxxxx 0b1110xxxx 10xxxxxx 10xxxxxx3字节
U + 01 0000 − U + 10 F F F F U+01 \ 0000-U+10 \ FFFF U+01 0000U+10 FFFF 0 b x x x x x x x x x x x x x x x x x x x x x 0bx \ xxxx \ xxxx \ xxxx \ xxxx\ xxxx 0bx xxxx xxxx xxxx xxxx xxxx 0 b 11110 x x x 10 x x x x x x 10 x x x x x x 10 x x x x x x 0b11110xxx \ 10xxxxxx \ 10xxxxxx \ 10xxxxxx 0b11110xxx 10xxxxxx 10xxxxxx 10xxxxxx4字节

1 1 1:编码时只需根据码点范围按照码点二进制表示,高位补 0 0 0,填充编码规则中所需的空缺即可。
2 2 2:解码时只需要考虑首字节中最高位 0 b i t 0bit 0bit 左侧 1 b i t 1bit 1bit 的个数,即为当前字符所占用字节数。
例如:编码 U + 00 E 9 U+00E9 U+00E9 对于字符为 e ˊ é eˊ。根据范围知道其需要二字节编码, 0 0 0 扩展到 11 b i t 11bit 11bit 0 b 000 1110 1001 0b000 \ 1110 \ 1001 0b000 1110 1001。依次填充可知该字符 u t f 8 utf8 utf8 编码结果为 0 b 11000011 10101001 = 0 x c 3 a 9 0b11000011 \ 10101001=0xc3a9 0b11000011 10101001=0xc3a9

utf8编码函数示例

下面给出编码函数如下所示:

参数buf为待填入编码值的缓冲区,c为32位unicode码点
返回值为该字符所需编码长度

int encode_utf8(char *buf, uint32_t c) {// 一字节编码if (c <= 0x7F) {buf[0] = c;return 1;}// 二字节编码,首字节待填充5位,第二字节待填充6位if (c <= 0x7FF) {buf[0] = 0b11000000 | (c >> 6);buf[1] = 0b10000000 | (c & 0b00111111);return 2;}// 三字节编码,首字节待填充4位,第二字节待填充6位,第三字节待填充6位if (c <= 0xFFFF) {buf[0] = 0b11100000 | (c >> 12);buf[1] = 0b10000000 | ((c >> 6) & 0b00111111);buf[2] = 0b10000000 | (c & 0b00111111);return 3;}// 四字节编码,首字节待填充3位,第二字节待填充6位,第三字节待填充6位,第四字节待填充6位buf[0] = 0b11110000 | (c >> 18);buf[1] = 0b10000000 | ((c >> 12) & 0b00111111);buf[2] = 0b10000000 | ((c >> 6) & 0b00111111);buf[3] = 0b10000000 | (c & 0b00111111);return 4;
}

可通过如下主函数测试该编码函数的正确性:

int main(){char buf[4];int len=encode_utf8(buf,0x000000E9);system("chcp 65001");                // 终端使用utf8编码for(int i=0;i<len;i++)printf("%x",(unsigned char)buf[i]);std::cout<<std::endl;std::cout<<buf<<std::endl;return 0;
}

v s c o d e + m i n g w vscode+mingw vscode+mingw 的环境下有输出如下所示:

在这里插入图片描述

utf8解码函数示例

下面给出解码函数如下所示:

参数buf为给定utf8编码序列
返回值为该字符unicode码点

uint32_t decode_utf8(char *p) {// 单字节编码if ((unsigned char)*p < 128) {return *p;}int len;uint32_t c;if ((unsigned char)*p >= 0b11110000) {        // 四字节编码,起始11110xxx,3bit有效len = 4;c = *p & 0b111;} else if ((unsigned char)*p >= 0b11100000) { // 三字节编码,起始1110xxxx,4bit有效len = 3;c = *p & 0b1111;} else if ((unsigned char)*p >= 0b11000000) { // 二字节编码,起始110xxxxx,5bit有效len = 2;c = *p & 0b11111;} else {std::cout<<"invalid UTF-8 sequence"<<std::endl;}for (int i = 1; i < len; i++) {if ((unsigned char)p[i] >> 6 != 0b10)std::cout<<"invalid UTF-8 sequence"<<std::endl;c = (c << 6) | (p[i] & 0b111111);}return c;
}

可通过如下主函数测试该编码函数的正确性:

int main(){unsigned char buf[4]={0xc3,0xa9,0x00,0x00};uint32_t code=decode_utf8((char*)buf);system("chcp 65001");std::cout<<buf<<std::endl;std::cout<<std::hex<<code<<std::endl;return 0;
}

v s c o d e + m i n g w vscode+mingw vscode+mingw 的环境下有输出如下所示:

在这里插入图片描述

utf16编码

u t f 16 utf16 utf16 为变长编码,采用 2 2 2 字节或 4 4 4 字节编码。不兼容 A S C I I ASCII ASCII 码。

上文提到,码点范围从 U + 0000 U+0000 U+0000 U + F F F F U+FFFF U+FFFF 为基本多文种平面,包括绝大多数常用字符。 u t f 16 utf16 utf16 编码对常用的基本多文种平面直接使用 2 2 2 字节编码,超过这个范围的码点使用 4 4 4 字节编码。
具体编码规则如下所示:

码点范围码点二进制表示编码规则字节数
U + 0000 − U + F F F F U+0000-U+FFFF U+0000U+FFFF 0 b x x x x x x x x x x x x x x x x 0bxxxxxxxx \ xxxxxxxx 0bxxxxxxxx xxxxxxxx 0 b x x x x x x x x x x x x x x x x 0bxxxxxxxx \ xxxxxxxx 0bxxxxxxxx xxxxxxxx2字节
U + F F F F − U + 10 F F F F U+FFFF-U+10FFFF U+FFFFU+10FFFF c o d e p o i n t − 0 x 10000 = 0 b y y y y y y y y y y x x x x x x x x x x code \ point - 0x10000=0byyyy \ yyyy \ yyxx \ xxxx \ xxxx code point0x10000=0byyyy yyyy yyxx xxxx xxxx 0 x D 800 + 0 b y y y y y y y y y y 0xD800+0byyyy \ yyyy \ yy 0xD800+0byyyy yyyy yy
0 x D C 00 + 0 b x x x x x x x x x x 0xDC00+0bxx \ xxxx \ xxxx 0xDC00+0bxx xxxx xxxx
4字节

1 1 1:这里四字节编码中码点需要减去 0 x 10000 0x10000 0x10000 ,最大码点 0 x 10 F F F F − 0 x 10000 = 0 x F F F F F 0x10FFFF-0x10000=0xFFFFF 0x10FFFF0x10000=0xFFFFF
2 2 2:上文提及 u t f 16 utf16 utf16 编码特性使得 U n i c o d e Unicode Unicode 标准中有 2 11 2^{11} 211 个码点未编码实际字符,该未编码字符的码点范围为: U + D 800 U+D800 U+D800 U + D F F F U+DFFF U+DFFF。用来作为 u t f 16 utf16 utf16 四字节编码的范围。

utf16编码解码函数示例

下面给出编码函数如下所示:

参数buf为待填入编码值的缓冲区,缓冲区单元为2字节单元,c为32位unicode码点
返回值为该字符所需编码长度

int encode_utf16(uint16_t *buf, uint32_t c) {int len=0;if (c < 0x10000) {// 2字节编码buf[len++] = c;return 2;} else {// 4字节编码c -= 0x10000;buf[len++] = 0xd800 + ((c >> 10) & 0x3ff);buf[len++] = 0xdc00 + (c & 0x3ff);return 4;}
}

下面给出解码函数如下所示:

参数buf为填入编码值的缓冲区,缓冲区单元为2字节单元
返回值为该字符的unicode码点

uint32_t decode_utf16(uint16_t *buf) {uint32_t code;if ((*buf) >= 0xD800 && (*buf) <= 0xDBFF) {code = ((*buf)-0xD800)&0x3ff;buf++;if (!(*buf) >= 0xDC00 && (*buf) <= 0xDFFF){std::cerr<<"error utf16 code"<<std::endl;return 0;}code = (code<<10)|(((*buf)-0xDC00)&0x3ff);return code+0x10000;} else {return *buf;}
}

可通过如下主函数测试该编码解码函数的正确性:

int main(){uint16_t buf[2];int len=encode_utf16(buf,0x10ABC);for(int i=0;i<len/2;i++)printf("%x",buf[i]);printf("\n");uint32_t code=decode_utf16(buf);printf("0x%08x",code);printf("\n");return 0;
}

v s c o d e + m i n g w vscode+mingw vscode+mingw 输出如下图所示:

在这里插入图片描述

注:关于 u t f 32 utf32 utf32 编码到 U n i c o d e Unicode Unicode 码点的转换则不需要程式,直接通过无符号扩展到 32 b i t 32bit 32bit 即可,不再给出。

总结

完结撒花!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/697072.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【RAG 论文】BGM:为 LLM 和 Retriever 的偏好 gap 搭建一个 Bridge

论文&#xff1a;Bridging the Preference Gap between Retrievers and LLMs ⭐⭐⭐ Google Research, arXiv:2401.06954 论文速读 LLM 与 Retriever 之间存在一个 preference gap&#xff1a;大多数 retriever 被设计为 human-friendly&#xff0c;但是 LLM 的偏好与人类的却…

基于Idea搭建Android开发环境

文章目录 下载SDK ManagerAndroid SDK Platform-toolsAndroid SDK Build-toolsAndroid SDKAndroid SDK Extras IDEA设置创建TestApp导入Android Studio创建的项目 下载SDK Manager SDK Manager是Google提供的&#xff0c;专门用于下载/管理&#xff0c;安卓开发中需要用到的工…

selenium UI自动化中文件上传的两种方式

前言 文件上传是自动化中很常见的一个功能&#xff0c;那么对于文件上传你又有多少了解呢&#xff1f;请往下看 1、被测产品中文件上传的功能非常普遍&#xff0c;一般情况下需要将准备好的文件放在预定的路径下&#xff0c;然后在自动化测试的脚本中&#xff0c;去预置的路径…

vue3 antd-vue 超简单方式实现a-table跨页勾选

效果如下&#xff1a; 一、第一种方法 定义一个变量 selectKeysBack 用于维护所有勾选的数据value, 然后用 onSelect 拿到当前操作的数据&#xff0c;以及时候勾选。 这个方法便于拿到后端的数据之后用于回显 1、template <a-tableclass"custom-table-data":da…

Docker 部署 Prometheus 实现一个极简的 QPS 监控

背景 : Prometheus 是近年来最流行的开源监控框架, 其功能强大且易于使用, 拥有各种主流后端语言(Java/Go/Python/Node.js等)与各种场景(如web handler/ k8s/Nginx/MySQL等)的客户端, 并自带图形化显示页面。分享一个快速入门Prometheus 的教程, 实现一个极简的, 后端开发需要特…

深度解析 | PagerDuty Copilot - 运维领域大模型应用场景

【本文作者&#xff1a;擎创科技资深产品专家 布博士】 最近一年多的时间里&#xff0c;生成式人工智能&#xff08;我们通常称为大语言模型&#xff09;已经成为了各行各业提升效率的、降低成本的强大工具。PagerDuty Copilot&#xff0c;作为一款为pagerduty cloud用户提供的…

通俗易通解读Restaking,潜力如何?(bitget钱包玩转)

关于 Restaking 再质押&#xff1a; 1. Restaking 在功能上&#xff0c;将以太坊安全性标准化&#xff0c;并将其“货币化”&#xff0c;Restaking 出售的就是以太坊的安全性&#xff0c;同时&#xff0c;将安全性用质押后的通证数量直观表现出来 2. Restaking 在经济机制上的逐…

下载npm I就包错解决方案

npm i xxxx -S --legacy-peer-deps 如果包错就执行以上命令

UTONMOS:真正的“游戏元宇宙”还有多遥远?

元宇宙来源于科幻小说的概念&#xff0c;已成为真实世界中的流行语。围绕这一新兴概念&#xff0c;一场产、学、研的实践正在展开。 数字化转型中&#xff0c;元宇宙能否担当大任&#xff1f;这些新概念在中国语境下如何落地&#xff1f;本文将深入挖掘国内元宇宙游戏产业的发…

基于百川大语言模型的RSS新闻过滤应用【云服务器+公网网页,随时随地看自己DIY订阅的新闻内容】

背景 目前从公众号、新闻媒体上获得的新闻信息,都是经过算法过滤推荐的,很多时候会感到内容的重复性和低质量,因为他们也要考虑到自己的利益,并非完全考虑用户想要的、对用户有价值的信息。这时,如果要获取自己认为重要的信息,定制化开发自己的筛选算法更好。 效果 素材…

经验分享打开keil工程下载按钮是灰色的解决办法

问题背景 打开一个工程发现download的按钮是灰色的&#xff0c;这种是怎么回事呢&#xff1f; 调研问题 工程中有使用.lib的文件库&#xff0c;而且是一个私有的库&#xff0c;类似这种祖传的工程&#xff0c;一般是能用则用&#xff0c;不能用则弃之不用。 解决问题 在网络…

打印机 ansible配置dhcp和打印机

部署dhcp服务器 主机发送Discover报文 目标为广播地址 同一网段的dhcp收到报文后&#xff0c;dhcp响应一个offer报文 offer报文&#xff1a;dhcp自己的ip地址。和客户端ip以及使用周期&#xff0c;和客户端ip网络参数 最后主机单独发一个request报文 给那个选择的dhcp服务器 &…