为什么同一表情‘‘.length==5但‘‘.length==4?本文带你深入理解 String Unicode UTF8 UTF16

news2024/11/25 20:50:06

背景

为什么同样是男人,但有的男人'🧔‍♂️'.length === 5,有的男人'🧔‍♂'.length === 4呢?

这二者都是JS中的字符串,要理解本质原因,你需要明白JS中字符串的本质,你需要理解 String Unicode UTF8 UTF16 的关系。本文,深入二进制,带你理解它!

从 ASCII 说起

各位对这张 ASCII 表一定不陌生:

因为计算机只能存储0和1,如果要让计算机存储字符串,还是需要把字符串转成二进制来存。ASCII就是一直延续至今的一种映射关系:把8位二进制(首位为0)映射到了128个字符上。

从多语言到Unicode

但是世界上不止有英语和数字,还有各种各样的语言,计算机也应该能正确的存储、展示它们。

这时候,ASCII的128个字符,就需要被扩充。有诸多扩充方案,但思路都是一致的:把一个语言符号映射到一个编号上。有多少个语言符号,就有多少个编号。

至今,Unicode 已经成为全球标准。

The Unicode Consortium is the standards body for the internationalization of software and services. Deployed on more than 20 billion devices around the world, Unicode also provides the solution for internationalization and the architecture to support localization.

Unicode 联盟是软件和服务国际化的标准机构。 Unicode 部署在全球超过 200 亿台设备上,还提供国际化解决方案和支持本地化的架构。

Unicode是在ASCII的128个字符上扩展出来的。

例如,英文「z」的Unicode码是7A(即十进制的122,跟ASCII一致)。

Unicode中80(即128号)字符是,这是ASCII的128个字符(0-127)的后一个字符。

汉字「啊」的Unicode码是554A

Emoji「🤔」的Unicode码是1F914

从Unicode到Emoji

随着时代发展,人们可以用手机发短信聊天了,常常需要发送表情,于是有人发明了Emoji。Emoji其实也是一种语言符号,所以Unicode也收录了进来。

Unicode一共有多少

现在,Unicode已经越来越多了,它的编码共计111万个!(有实际含义的编码并没这么多)

目前的Unicode字符分为17组编排,每组称为平面(Plane),而每平面拥有65536(即2^4^4=2^16)个代码点。目前只用了少数平面。

平面始末字符值中文名称英文名称
0号平面U+0000 - U+FFFF基本多文种平面Basic Multilingual Plane,简称BMP
1号平面U+10000 - U+1FFFF多文种补充平面Supplementary Multilingual Plane,简称SMP
2号平面U+20000 - U+2FFFF表意文字补充平面Supplementary Ideographic Plane,简称SIP
3号平面U+30000 - U+3FFFF表意文字第三平面Tertiary Ideographic Plane,简称TIP
4号平面 至 13号平面U+40000 - U+DFFFF(尚未使用)
14号平面U+E0000 - U+EFFFF特别用途补充平面Supplementary Special-purpose Plane,简称SSP
15号平面U+F0000 - U+FFFFF保留作为私人使用区(A区)Private Use Area-A,简称PUA-A
16号平面U+100000 - U+10FFFF保留作为私人使用区(B区)Private Use Area-B,简称PUA-B

以前只有ASCII的时候,共128个字符,我们统一用8个二进制位(因为log(2)128=7,取整得8),就一定能存储一个字符。

现在,Unicode有16*65536=1048576个字符,难道必须用log(2)1048576=20 向上取整24位(3个字节)来表示一个字符了吗?

那样的话,字母z就是00000000 00000000 01111010了,而之前用ASCII的时候,我们用01111010就可以表示字母z。也就是说,同样一份纯英文文件,换成Unicode后,扩大了3倍!1GB变3GB。而且大部分位都是0。这太糟糕了!

因此,Unicode只是语言符号和一些自然数的映射,不能直接用它做存储。

UTF8如何解决「文本大小变3倍问题」

答案就是:「可变长编码」,之前我在文章《太卷了!开发象棋,为了减少40%存储空间,我学了下Huffman Coding》提到过。

使用「可变长编码」,每个字符不一定都要用统一的长度来表示,针对常见的字符,我们用8个二进制位,不常见的字符,我们用16个二进制位,更不常见的字符,我们用24个二进制位。

这样,能够减少大部分场景的文件体积。这也是哈夫曼编码的思想。

要设计一套高效的「可变长编码」,你必须满足一个条件:它是「前缀码」。即通过前缀,我就能知道这个字符要占用多少字节。

而UTF8,就是一种「可变长编码」。

UTF8的本质

  1. UTF8可以把2^21=2097152个数字,映射到1-4个字节(这个范围能够覆盖所有Unicode)。
  2. UTF8完全兼容ASCII。也就是说,在UTF8出现之前的所有电脑上存储的老的ASCII文件,天然可以被UTF8解码。

具体映射方法:

  • 0-127,用0xxxxxxx表示(共7个x)
  • 128-2^11-1,用110xxxxx 10xxxxxx表示(共11个x)
  • 2^11-2^16-1,用1110xxxx 10xxxxxx 10xxxxxx表示(共16个x)
  • 2^16-2^21-1,用11110xxx 10xxxxxx 10xxxxxx 10xxxxxx表示(共21个x)

不得不承认,UTF8确实有冗余,还有压缩空间。但考虑到存储不值钱,而且考虑到解析效率,它已经是最优解了。

UTF16的本质

回到本文开头的问题,为什么'🧔‍♂️'.length === 5,但'🧔‍♂'.length === 4呢?

你需要知道在JS中,字符串使用了UTF16编码(其实本来是UCS-2,UTF16是UCS-2的扩展)。

为什么JS的字符串不用UTF8?

因为JS诞生(1995)时,UTF8还没出现(1996)。

UTF16不如UTF8优秀,因为它用16个二进制位或32个二进制位映射一个Unicode。这就导致:

  1. 它涉及到大端、小端这种字节序问题。
  2. 它不兼容ASCII,很多老的ASCII文件都不能用了。

UTF16的具体映射方法:

16进制编码范围(Unicode)UTF-16表示方法(二进制)10进制码范围字节数量
U+0000 - U+FFFFxxxxxxxx xxxxxxxx (一共16个x)0-655352
U+10000 - U+10FFFF110110xx xxxxxxxx 110111xx xxxxxxxx (一共20个x)65536-11141114

细心的你有没有发现个Bug?UTF16不是前缀码? 遇到110110xx xxxxxxxx 110111xx xxxxxxxx,怎么判断它是1个大的Unicode字符、还是2个连续的小的Unicode字符呢?

答案:其实,在U+0000 - U+FFFF范围内,110110xx xxxxxxxx110111xx xxxxxxxx都不是可见字符。也就是说,在UTF16中,遇到110110一定是4字节UTF16的前2字节的前缀,遇到110111一定是4字节UTF16的后2字节的前缀,其它情况,一定是2字节UTF16。这样,通过损失了部分可表述字符,UTF16也成为了「前缀码」。

JS中的字符串

在JS中,'🧔‍♂️'.length算的就是这个字符的UTF16占用了多少个字节。

我开发了个工具,用于解析字符串,把它的UTF8二进制和UTF16二进制都展示了出来。

工具地址:tool.hullqin.cn/string-pars…

我把2个男人,都放进去,检查一下他们的Unicode码:

发现区别了吗?

长度为4的,是1F9D4 200D 2642;长度为5的,是1F9D4 200D 2642 FE0F

都是一个Emoji,但是它对应了多个Unicode。这是因为200D这个零宽连字符,一些复杂的emoji,就是通过200D,把不同的简单的emoji组合起来,展示的。当然不是任意都能组合,需要你字体中定义了那个组合才可以。

标题中的Emoji,叫man: beard,是胡子和男人的组合。

末尾的FE0F是变体选择符,当一个字符一定是emoji而非text时,它其实是可有可无的。

于是,就有的'🧔‍♂️'长,有的'🧔‍♂'短了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vlan trunk stp攻防

目录 一、VLAN、Trunk面临的安全风险 trunk干道攻击DTP攻击(思科特有) VLAN跳跃攻击 STP根桥攻击 二、攻击防护 一、VLAN、Trunk面临的安全风险 trunk干道攻击DTP攻击(思科特有) 在华为设备中trunk链路是手工指定的&#xf…

A-LEVEL Chemistry考点分析

A-LEVEL化学其实不是一门很难的科目,并没有太多的内容,虽说包含十几章的内容但其实每章都是相互关联,一通百通,掌握元素周期表的实质,基本上就没有什么问题了,重在理解!知识点多而不碎&#xff…

如何一站式管理固定资产的全生命周期?

目前很多企业还在沿用之前传统的Excel表格来管理和盘点固定资产,不但加大了企业固定资产管理员的工作量,而且在实际的操作过程中容易出现错登记、漏盘点、无法查询操作履历等各种小插曲。而且随着企业固定资产数量、种类、分支机构以及人员的不断增加&am…

juc之常用4大并发工具类 (四)

CountDownLatch: 减少计数CyclicBarrier: 循环栅栏Semaphore: 信号量ExChanger: 交换器 文章目录1.CountDownLatch2.CyclicBarrier3.Semaphore4.Exchanger1.CountDownLatch CountDownLatch,俗称闭锁,作用是类似加强版的 Join,是让一组线程等待其他的线程完成工作以后才执行 就…

数仓开发之DWD层(四)

目录 十一:工具域优惠券领取事务事实表 11.1 主要任务: 11.2 思路分析: 11.3 图解: 十二:工具域优惠券使用(下单)事务事实表 12.1 主要任务: 12.2 思路分析: 12.3…

[附源码]Python计算机毕业设计 社区老人健康服务跟踪系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

Java项目实战【超级详细】

软件开发流程 角色分工 开发环境搭建 创建普通Maven项目编写pom.xml导入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instanc…

【MQ简单模式】

14天阅读挑战赛 MQ简单模式 1、模式介绍 需求&#xff1a;使用简单模式完成消息传递 步骤&#xff1a; ① 创建工程&#xff08;生成者、消费者&#xff09; ② 分别添加依赖 ③ 编写生产者发送消息 ④ 编写消费者接收消息 在上图的模型中&#xff0c;有以下概念&#xff1a; ⚫…

数据库索引的基本操作(sql语句)

表索引的增删改查&#xff08;sql语句&#xff09; 概念&#xff1a; 索引由表中的一列或多列组合而成&#xff0c;目的是优化数据库的查询速度。向索引表中插入数据时&#xff0c;数据库系统需要按照索引进行排序。有时&#xff0c;可以先将索引删除&#xff0c;然后向表插入…

Docker学习(1)—— 安装Docker

一. 安装Docker 1. 查看CentOS是否是7以上的版本 cat /etc/redhat-release 2. 安装gcc yum -y install gcc yum -y install gcc-c 3. 安装需要的软件包 yum install -y yum-utils 4. 设置stable镜像仓库 yum-config-manager --add-repo http://mirrors.aliyun.com/doc…

p38 MAPK调控酶及转录因子

MAPK 与 p38 MAPK 作为对外界物理和化学性质变化的响应&#xff0c;哺乳动物细胞激活有丝分裂原激活蛋白激酶 (MAPK) 的四个特征性亚家族&#xff1a;ERK1/2、JNK、p38 和 ERK5。其中 p38 MAPKs 是一类保守的丝氨酸-苏氨酸蛋白激酶&#xff0c;可被多种细胞外炎症因子 (如TNF-α…

语义分割及DeeplabV3+模型

一、基本概念 将视觉输入分为不同的语义可解释类别&#xff1b;通俗点说就是区分不同部分。 分类&#xff1a; b)语义分割 c)实例分割 d)全景分割 语义分割一般以平均IOU(Intersection Over Union,并交比)来评价性能&#xff0c;其公式如下&#xff1a; 目的是使…

Python学习----Demo(pyecharts)

PyEcharts 就是百度的Echarts&#xff0c;针对Python有一款专门的&#xff0c;所以就叫PyEcharts 官方网站&#xff1a; 文档&#xff1a; https://pyecharts.org/#/zh-cn/ 示例&#xff1a; https://gallery.pyecharts.org/#/README 通过pip安装 pip install pyecharts 或者…

关于Docker中容器之间互相访问问题

背景&#xff1a; 在学习 Docker 过程中&#xff0c;自己先开启了一个 mysql 容器&#xff0c;并且通过端口映射Navicat可以远程连接 后来在部署 nacos 过程中&#xff0c;对于其中参数 MYSQL_SERVICE_HOST 配置 mysql 的 IP 时 首先配置的是 localhost&#xff0c;结果 nacos…

Opencv项目实战:16 虚拟拖拽系统

0、项目介绍 这次使用cvzone模块&#xff0c;制作一个虚拟拖拽系统&#xff0c;我们根据索引可以知道食指与中指的索引为8和12&#xff0c;当两指间合并时可以对虚拟方块进行拖拽&#xff0c;张开时解除拖拽&#xff0c;方块停在此处。虽然目前仍然存在一定的bug&#xff0c;即…

网站seo怎么优化?

一、网站搭建 1、选择合适的建站软件。 2、网站模板的选择。 3、title的选择至关重要。 4、长尾关键词的选择。 二、站内优化 1、首页title keywords description等布局好要优化的关键词&#xff0c;页面中间和底部也要布局关键词且用标签加粗&#xff01; 2、网站 URL静态。…

Go runtime剖析系列(一):内存管理

Go 的内存管理总览 程序在运行过程中会不断的调用分配器分配堆内存&#xff0c;同时也会通过赋值器更新内存数据&#xff0c;如果我们将内存看做为一个个小的对象集合&#xff0c;那程序的运行其实就是更改这个对象图&#xff0c;其中分配器在这个对象图中创建节点&#xff0c…

Flutter 的缓存策略

Flutter 的缓存策略 原文 https://medium.com/romaingreaume/implementing-a-cache-strategy-in-your-flutter-app-5db3e316e7c9 前言 在移动应用程序中&#xff0c;缓存管理是一件非常重要的事情。 在本文中&#xff0c;我将告诉您如何在我的公司 Beapp 中设置策略缓存。 正文…

ES-分词器

简介 分词器是es中的一个组件&#xff0c;通俗意义上理解&#xff0c;就是将一段文本按照一定的逻辑&#xff0c;分析成多个词语&#xff0c;同时对这些词语进行常规化的一种工具&#xff1b;ES会将text格式的字段按照分词器进行分词&#xff0c;并编排成倒排索引&#xff0c;…

Vben Admin 的介绍以及使用

Vben Admin 的介绍以及使用初识 Vben Admin一、安装vue-vben-admin二、vben admin 目录介绍1.运行项目好用的插件启动项目打开终端三 项目分析1. 路由配置新增路由四 : vben admin 的使用初识 Vben Admin Vben Admin官方文档地址 好像官网挂了 !! 嘤嘤嘤嘤嘤 因为最近业务需要…