」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > Unicode、表情符號和一點 Golang

Unicode、表情符號和一點 Golang

發佈於2024-11-01
瀏覽:824

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]刪除
最新教學 更多>
  • 將圖片浮動到底部右側並環繞文字的技巧
    將圖片浮動到底部右側並環繞文字的技巧
    在Web設計中圍繞在Web設計中,有時可以將圖像浮動到頁面右下角,從而使文本圍繞它纏繞。這可以在有效地展示圖像的同時創建一個吸引人的視覺效果。 css位置在右下角,使用css float and clear properties: img { 浮點:對; ...
    程式設計 發佈於2025-04-29
  • 如何從PHP會話數組中選擇性刪除特定變量?
    如何從PHP會話數組中選擇性刪除特定變量?
    在php session中刪除PHP會話陣列中的特定變量,可以存儲各種變量以供以後使用。但是,您可能會遇到只需要刪除特定變量的情況。以下是完成此任務的方法: 開始,您已經定義瞭如何將變量添加到會話中。讓我們專注於您打算使用Unset刪除變量的部分。不幸的是,使用Unsot($ _會話['n...
    程式設計 發佈於2025-04-29
  • 如何使用“ JSON”軟件包解析JSON陣列?
    如何使用“ JSON”軟件包解析JSON陣列?
    parsing JSON與JSON軟件包 QUALDALS:考慮以下go代碼:字符串 } func main(){ datajson:=`[“ 1”,“ 2”,“ 3”]`` arr:= jsontype {} 摘要:= = json.unmarshal([] byte(...
    程式設計 發佈於2025-04-29
  • Go語言垃圾回收如何處理切片內存?
    Go語言垃圾回收如何處理切片內存?
    Garbage Collection in Go Slices: A Detailed AnalysisIn Go, a slice is a dynamic array that references an underlying array.使用切片時,了解垃圾收集行為至關重要,以避免潛在的內存洩...
    程式設計 發佈於2025-04-29
  • 如何使用Python有效地以相反順序讀取大型文件?
    如何使用Python有效地以相反順序讀取大型文件?
    在python 中,如果您使用一個大文件,並且需要從最後一行讀取其內容,則在第一行到第一行,Python的內置功能可能不合適。這是解決此任務的有效解決方案:反向行讀取器生成器 == ord('\ n'): 緩衝區=緩衝區[:-1] ...
    程式設計 發佈於2025-04-29
  • C#中Int與Int32:選哪個整數類型?
    C#中Int與Int32:選哪個整數類型?
    C# 中 int 和 Int32 的使用 在 C# 中處理整數時,您可能會遇到兩個術語:int 和 Int32。這兩個術語代表相同的數據類型,都存儲 32 位整數。然而,一個常見的問題是:應該使用 int 還是 Int32? 雖然 int 和 Int32 的功能完全相同,但某些考慮因素可以指導您...
    程式設計 發佈於2025-04-29
  • 哪種在JavaScript中聲明多個變量的方法更可維護?
    哪種在JavaScript中聲明多個變量的方法更可維護?
    在JavaScript中聲明多個變量:探索兩個方法在JavaScript中,開發人員經常遇到需要聲明多個變量的需要。對此的兩種常見方法是:在單獨的行上聲明每個變量: 當涉及性能時,這兩種方法本質上都是等效的。但是,可維護性可能會有所不同。 第一個方法被認為更易於維護。每個聲明都是其自己的語句,使...
    程式設計 發佈於2025-04-29
  • 對象擬合:IE和Edge中的封面失敗,如何修復?
    對象擬合:IE和Edge中的封面失敗,如何修復?
    To resolve this issue, we employ a clever CSS solution that solves the problem:position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%)...
    程式設計 發佈於2025-04-29
  • 如何使用node-mysql在單個查詢中執行多個SQL語句?
    如何使用node-mysql在單個查詢中執行多個SQL語句?
    Multi-Statement Query Support in Node-MySQLIn Node.js, the question arises when executing multiple SQL statements in a single query using the node-mys...
    程式設計 發佈於2025-04-29
  • MySQL中如何高效地根據兩個條件INSERT或UPDATE行?
    MySQL中如何高效地根據兩個條件INSERT或UPDATE行?
    在兩個條件下插入或更新或更新 solution:的答案在於mysql的插入中...在重複鍵更新語法上。如果不存在匹配行或更新現有行,則此功能強大的功能可以通過插入新行來進行有效的數據操作。如果違反了唯一的密鑰約束。 實現所需的行為,該表必須具有唯一的鍵定義(在這種情況下為'名稱'...
    程式設計 發佈於2025-04-29
  • 您如何在Laravel Blade模板中定義變量?
    您如何在Laravel Blade模板中定義變量?
    在Laravel Blade模板中使用Elegance 在blade模板中如何分配變量對於存儲以後使用的數據至關重要。在使用“ {{}}”分配變量的同時,它可能並不總是最優雅的解決方案。 幸運的是,Blade通過@php Directive提供了更優雅的方法: $ old_section =...
    程式設計 發佈於2025-04-29
  • Entity Framework 5更新記錄的最佳方法
    Entity Framework 5更新記錄的最佳方法
    Entity Framework 5記錄更新的最佳實踐 在Entity Framework 5中更新記錄,開發者常常面臨多種方法的選擇,每種方法都有其優缺點。本文將探討三種常用方法及其局限性,並最終給出最佳方案。 方法一:加載原始記錄並逐個更新屬性 此方法需要先加載原始記錄,然後手動更新每個修改...
    程式設計 發佈於2025-04-29
  • C#靜態變量如何工作及為何不能在方法內聲明
    C#靜態變量如何工作及為何不能在方法內聲明
    C#中的靜態變量 許多開發人員難以理解C#中靜態變量的功能。本文旨在闡明它們的用途和用法,同時解釋為什麼不能在方法內部聲明靜態變量。 什麼是靜態變量? 靜態變量是類級別的變量,在該類的所有實例之間共享。其值在從該類創建的所有對象之間共享。 何時使用靜態變量? 在需要跨類的多個實例維護值的情...
    程式設計 發佈於2025-04-29
  • 如何在JavaScript對像中動態設置鍵?
    如何在JavaScript對像中動態設置鍵?
    在嘗試為JavaScript對象創建動態鍵時,如何使用此Syntax jsObj['key' i] = 'example' 1;不工作。正確的方法採用方括號: jsobj ['key''i] ='example'1; 在JavaScript中,數組是一...
    程式設計 發佈於2025-04-29
  • IACA助力優化Intel CPU代碼性能分析
    IACA助力優化Intel CPU代碼性能分析
    被稱為英特爾體系結構代碼分析儀,IACA是用於評估針對Intel CPU的代碼調度的高級工具。它以三種模式運行: 吞吐量模式: iaca iaca衡量最大的吞吐量,假設它是嵌套循環的主體。 IACA traces the sequence of instructions as they prog...
    程式設計 發佈於2025-04-29

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3