”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > Unicode、表情符号和一点 Golang

Unicode、表情符号和一点 Golang

发布于2024-11-01
浏览:356

Unicode, Emojis, and a bit of Golang

最近,我的 Fedora Linux 安装在操作系统 UI 和浏览器中显示表情符号时遇到了问题。这个问题促使我对字体配置项目进行了一些调查,但为了测试我的配置和字体,我需要从所有 Unicode 版本生成表情符号,这最终导致我编写了一个 Golang“脚本”来打印所有表情符号和一些表情符号有关其内部结构的信息。

在这次旅行中,我深入研究了表情符号的内部结构、它们的二进制表示形式,以及 Unicode 标准关于表情符号做出的一些奇怪/可爱的决定。

但首先,让我们快速退后一步,总结一些术语表。

编码(或字符编码)

我们可以将编码描述为语言的字母和该字母的二进制表示之间的“映射”或“翻译”。例如,传统的 ASCII 编码将字母 a 映射为十六进制 0x61(二进制 0b01100001)。编码示例包括 Microsoft (Windows 125x) 或 ISO (ISO/IEC 8859) 8 位代码页。

在这些固定的 8 位代码页中,使用的最小信息“量”是 8 位(1 字节),这意味着它们可以包含 256 个不同的字母/字符。通过重用 256 个二进制代码创建不同的代码页来支持多种语言。因此,在一个文本文件上写入这 3 个字节 [0xD0、0xE5、0xF2],使用希腊语 ISO 8859-7 读作“Πες”,或使用西方 ISO 8859-7 读作“Ðåò”(相同字节,解释不同)基于代码页)。

在某些时候,随着技术的进步,拥有许多不同的代码页并不能很好地扩展。因此,我们需要能够适合所有语言(以及更多语言)并且跨系统统一的东西。

[快进,留下大量历史和标准,到现在]

统一码标准

Unicode 标准旨在支持世界上所有可以数字化的书写系统。因此,使用上面的示例,在 Unicode 标准中,希腊字母“Π”的代码为 0x03A0,而拉丁大写字母 eth“Д的代码为 0x00D0,并且不再冲突。 Unicode 标准有多个版本,在撰写本文时,最新版本是 16.0(规范)。

但是等一下,这个“代码点”是什么?

Unicode 代码点

在 Unicode 标准中,每个“字母”、控制字符、表情符号和每个定义的项目通常都有一个唯一的二进制值,称为“代码点”。该标准定义了所有代码点,每个代码点都包含纯代码/二进制信息。每个代码点的十六进制格式通常以 U 前缀编写。例如,希腊小写字母 Omega (ω) 代码点是 U 03C9。

那么我们实际上由谁来编码这些代码点?

Unicode 编码形式和编码方案

将代码点编码为字节的第一部分是编码格式。根据标准:

编码形式指定如何将 Unicode 字符的每个整数(代码点)表示为一个或多个代码单元的序列。

编码形式使用术语“代码单元”来指代用于表示特定编码内的 Unicode 代码点的最小数据单元。

Unicode 标准定义了三种不同的编码形式:

  • UTF-32。每个代码点的固定长度代码单元。每个代码点的大小:一个 32 位代码单元(4 字节)。
  • UTF-16。每个代码点的可变长度代码单元。每个码点的大小:1个或2个16bit码单元(2~4字节)。
  • UTF-8。每个代码点的可变长度代码单元。每个码点的大小:1到4个8bit码单元(1~4字节)。

这意味着根据所使用的编码形式,单个代码点或一系列代码点可能会进行不同的编码。

负责 Unicode 中实际二进制序列化的层称为编码方案,负责所有低级细节(例如字节序)。 Unicode 规范表 2-4:


|Encoding Scheme| Endian Order                | BOM Allowed? |
| ------------- | ----------------------------| ------------ |
| UTF-8         | N/A                         | yes          |
| UTF-16        | Big-endian or little-endian | yes          |
| UTF-16BE      | Big-endian                  | no           |
| UTF-16LE      | Little-endian               | no           |
| UTF-32        | Big-endian or little-endian | yes          |
| UTF-32BE      | Big-endian                  | no           |
| UTF-32LE      | Little-endian               | no           |


注意:几乎所有现代编程语言、操作系统和文件系统都使用 Unicode(及其编码方案之一)作为其本机编码。 Java和.NET使用UTF-16,而Golang使用UTF-8作为内部字符串编码(这意味着当我们在内存中创建任何字符串时,它都会以上述编码形式以Unicode编码)

表情符号

Unicode 标准还定义了表情符号(很多)的代码点,并且(在与版本号混淆之后),表情符号“标准”的版本与 Unicode 标准并行发展。在撰写本文时,我们有表情符号“16.0”和 Unicode 标准“16.0”。

示例:
⛄ 没有雪的雪人 (U 26C4)
?笑脸笑眼三颗心 (U 1F970)

表情符号修饰符和加入

Unicode 定义了可以遵循表情符号基本代码点的修饰符,例如变体和肤色(我们不会探讨变体部分)。

我们有六种肤色修饰符(遵循 Fitzpatrick 等级),称为 EMOJI MODIFIER FITZPATRICK TYPE-X(其中 x 为 1 到 6),它们会影响所有人类表情符号。

浅肤色 (Fitzpatrick Type-1-2) (U 1F3FB)
中浅肤色 (Fitzpatrick Type-3) (U 1F3FC)
中等肤色 (Fitzpatrick Type-4) (U 1F3FD)
中深肤色 (Fitzpatrick Type-5) (U 1F3FE)
深色肤色 (Fitzpatrick Type-6) (U 1F3FF)

那么,例如,像所有人类表情符号一样,婴儿表情符号? (U 1F476),当不添加皮肤修饰剂时,会呈现中性黄色。相反,当肤色修改器跟随时,它会相应地发生变化。
? U 1F476
?? U 1F476 U 1F3FF
?? U 1F476 U 1F3FE
?? U 1F476 U 1F3FD
?? U 1F476 U 1F3FC
?? U 1F476 U 1F3FB

将表情符号连接在一起

表情符号/Unicode 标准最奇怪但可爱的决定是,一些表情符号是通过使用零宽度连接器将其他表情符号连接在一起而定义的,而不需要独立的代码点。

因此,例如,当我们组合时:
白旗?️ (U 1F3F3 U FE0F)
零宽度连接器 (U 200D)
彩虹 ? (U 1F308)

它显示为彩虹旗?️‍? (U 1F3F3 U FE0F U 200D U 1F308)

或者, ?? ? => ??‍?
甚至,?? ❤️? ?? => ??‍❤️‍?‍??

这就像将表情符号挤压在一起,然后,噗?,一个新的表情符号出现了。多可爱啊?


我想创建一个包含所有表情符号的 Markdown 表,而 Unicode 表情符号序列表是其真相来源。

https://unicode.org/Public/emoji/16.0/emoji-sequences.txt
https://unicode.org/Public/emoji/16.0/emoji-zwj-sequences.txt

因此,我创建了一个 Golang 解析器(此处),它获取并解析这些序列文件,在序列文件中描述范围时生成每个表情符号,并打印一个包含每个表情符号的一些内部信息的 Markdown 表(例如 零件(如果已加入),或基础 肤色等)。

您可以在这里找到降价表。

该表的最后一列的格式为:

Golang、Unicode 和 Rune


str := "⌚"
len([]rune(str)) // 1
len([]byte(str)) // 3


正如我们所讨论的,Golang 内部字符串编码是 UTF-8,这意味着,例如,对于时钟表情符号⌚,字节长度为 3(因为 UTF-8 生成 3 个字节来“写入”此代码点),码位长度为1。

Golang 符文 == Unicode 代码点

但是在连接表情符号的情况下 - 即使它“显示”为一个 - 我们有许多代码点(符文)甚至更多的字节。


str := "??‍❤️‍?‍??"
len([]rune(str)) // 10
len([]byte(str)) // 35


原因是:


??‍❤️‍?‍?? : ??   ZWJ   ❤️   ZWJ   ?   ZWJ   ??

??  : 1F469 1F3FC // ?   skin tone modifier [2 code points]
ZWJ : 200D // [1 code points] * 3
❤️  : 2764 FE0F // ❤   VS16 for emoji-style [2 code points]
?  : 1F48B // [1 code point]
??  : 1F468 1F3FE // ?   skin tone modifier [2 code points]



值得一提的是,我们如何看待表情符号取决于我们的系统字体以及该字体支持哪些版本的表情符号。

我不知道字体渲染的确切内部原理以及它如何正确渲染连接的字体。也许这将是一个未来的职位。

到那时,干杯吗?

版本声明 本文转载于:https://dev.to/moukoublen/unicode-emojis-and-a-bit-of-golang-3ced?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    The Mystery of "Broken" Two-Phase Template Instantiation in Microsoft Visual C Problem Statement:Users commonly express concerns that Micro...
    编程 发布于2025-03-12
  • UTF-8 vs. Latin-1:字符编码大揭秘!
    UTF-8 vs. Latin-1:字符编码大揭秘!
    [utf-8和latin1 在他们的应用中,出现了一个基本问题:什么辨别特征区分了这两个编码?超出其字符表现能力,UTF-8具有额外的几个优势。从历史上看,MySQL对UTF-8的支持仅限于每个字符的三个字节,这阻碍了基本多语言平面(BMP)之外的字符的表示。但是,随着MySQL 5.5的出现,...
    编程 发布于2025-03-12
  • 大批
    大批
    [2 数组是对象,因此它们在JS中也具有方法。 切片(开始):在新数组中提取部分数组,而无需突变原始数组。 令ARR = ['a','b','c','d','e']; // USECASE:提取直到索引作...
    编程 发布于2025-03-12
  • 如何在Java字符串中有效替换多个子字符串?
    如何在Java字符串中有效替换多个子字符串?
    在java 中有效地替换多个substring,需要在需要替换一个字符串中的多个substring的情况下,很容易求助于重复应用字符串的刺激力量。 However, this can be inefficient for large strings or when working with nu...
    编程 发布于2025-03-12
  • Part SQL注入系列:高级SQL注入技巧详解
    Part SQL注入系列:高级SQL注入技巧详解
    [2 Waymap pentesting工具:单击此处 trixsec github:单击此处 trixsec电报:单击此处 高级SQL注入利用 - 第7部分:尖端技术和预防 欢迎参与我们SQL注入系列的第7部分!该分期付款将攻击者采用的高级SQL注入技术 1。高...
    编程 发布于2025-03-12
  • 为什么PYTZ最初显示出意外的时区偏移?
    为什么PYTZ最初显示出意外的时区偏移?
    与pytz 最初从pytz获得特定的偏移。例如,亚洲/hong_kong最初显示一个七个小时37分钟的偏移: 差异源利用本地化将时区分配给日期,使用了适当的时区名称和偏移量。但是,直接使用DateTime构造器分配时区不允许进行正确的调整。 example pytz.timezone(...
    编程 发布于2025-03-12
  • 如何修复\“常规错误:2006 MySQL Server在插入数据时已经消失\”?
    如何修复\“常规错误:2006 MySQL Server在插入数据时已经消失\”?
    How to Resolve "General error: 2006 MySQL server has gone away" While Inserting RecordsIntroduction:Inserting data into a MySQL database can...
    编程 发布于2025-03-12
  • 我们如何保护有关恶意内容的文件上传?
    我们如何保护有关恶意内容的文件上传?
    对文件上载上传到服务器的安全性问题可以引入重大的安全风险,因为用户可能会提供潜在的恶意内容。了解这些威胁并实施有效的缓解策略对于维持应用程序的安全性至关重要。用户可以将文件名操作以绕过安全措施。避免将其用于关键目的或使用其原始名称保存文件。用户提供的MIME类型可能不可靠。使用服务器端检查确定实际...
    编程 发布于2025-03-12
  • 如何使用JavaScript中的正则表达式从字符串中删除线路断裂?
    如何使用JavaScript中的正则表达式从字符串中删除线路断裂?
    在此代码方案中删除从字符串在JavaScript中解决此问题,根据操作系统的编码,对线断裂的识别不同。 Windows使用“ \ r \ n”序列,Linux采用“ \ n”,Apple系统使用“ \ r。” 来满足各种线路断裂的变化,可以使用以下正则表达式: [&& && &&&&&&&&&&&...
    编程 发布于2025-03-12
  • 为什么使用Firefox后退按钮时JavaScript执行停止?
    为什么使用Firefox后退按钮时JavaScript执行停止?
    导航历史记录问题:JavaScript使用Firefox Back Back 此行为是由浏览器缓存JavaScript资源引起的。要解决此问题并确保在后续页面访问中执行脚本,Firefox用户应设置一个空功能。 警报'); }; alert('inline Alert')...
    编程 发布于2025-03-12
  • 如何使用PHP将斑点(图像)正确插入MySQL?
    如何使用PHP将斑点(图像)正确插入MySQL?
    essue VALUES('$this->image_id','file_get_contents($tmp_image)')";This code builds a string in PHP, but the function call ...
    编程 发布于2025-03-12
  • 我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    我可以将加密从McRypt迁移到OpenSSL,并使用OpenSSL迁移MCRYPT加密数据?
    将我的加密库从mcrypt升级到openssl 问题:是否可以将我的加密库从McRypt升级到OpenSSL?如果是这样,如何?答案:是的,可以将您的Encryption库从McRypt升级到OpenSSL。可以使用openssl。附加说明: [openssl_decrypt()函数要求iv参...
    编程 发布于2025-03-12
  • 在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    For Each Loop vs. Iterator: Efficiency in Collection TraversalIntroductionWhen traversing a collection in Java, the choice arises between using a for-...
    编程 发布于2025-03-12
  • 如何检查对象是否具有Python中的特定属性?
    如何检查对象是否具有Python中的特定属性?
    方法来确定对象属性存在寻求一种方法来验证对象中特定属性的存在。考虑以下示例,其中尝试访问不确定属性会引起错误: >>> a = someClass() >>> A.property Trackback(最近的最新电话): 文件“ ”,第1行, AttributeError: SomeClass...
    编程 发布于2025-03-12
  • Java HashSet/LinkedHashSet随机元素获取方法详解
    Java HashSet/LinkedHashSet随机元素获取方法详解
    在编程中找到一个随机元素,在编程中找到一个随机元素,从集合(例如集合)中选择一个随机元素很有用。 Java提供了多种类型的集合,包括障碍物和链接HASHSET。本文将探讨如何从这些特定集合实现的过程中选择一个随机元素。的java的hashset和linkedhashset a HashSet代表...
    编程 发布于2025-03-12

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3