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

Unicode、表情符號和一點 Golang

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

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]刪除
最新教學 更多>
  • 在細胞編輯後,如何維護自定義的JTable細胞渲染?
    在細胞編輯後,如何維護自定義的JTable細胞渲染?
    在JTable中維護jtable單元格渲染後,在JTable中,在JTable中實現自定義單元格渲染和編輯功能可以增強用戶體驗。但是,至關重要的是要確保即使在編輯操作後也保留所需的格式。 在設置用於格式化“價格”列的“價格”列,用戶遇到的數字格式丟失的“價格”列的“價格”之後,問題在設置自定義單元...
    程式設計 發佈於2025-07-05
  • 如何使用Python理解有效地創建字典?
    如何使用Python理解有效地創建字典?
    在python中,詞典綜合提供了一種生成新詞典的簡潔方法。儘管它們與列表綜合相似,但存在一些顯著差異。 與問題所暗示的不同,您無法為鑰匙創建字典理解。您必須明確指定鍵和值。 For example:d = {n: n**2 for n in range(5)}This creates a dict...
    程式設計 發佈於2025-07-05
  • PHP陣列鍵值異常:了解07和08的好奇情況
    PHP陣列鍵值異常:了解07和08的好奇情況
    PHP數組鍵值問題,使用07&08 在給定數月的數組中,鍵值07和08呈現令人困惑的行為時,就會出現一個不尋常的問題。運行print_r($月份)返回意外結果:鍵“ 07”丟失,而鍵“ 08”分配給了9月的值。 此問題源於PHP對領先零的解釋。當一個數字帶有0(例如07或08)的前綴時,PHP...
    程式設計 發佈於2025-07-05
  • 為什麼使用Firefox後退按鈕時JavaScript執行停止?
    為什麼使用Firefox後退按鈕時JavaScript執行停止?
    導航歷史記錄問題:JavaScript使用Firefox Back Back 此行為是由瀏覽器緩存JavaScript資源引起的。要解決此問題並確保在後續頁面訪問中執行腳本,Firefox用戶應設置一個空功能。 警報'); }; alert('inline Alert')...
    程式設計 發佈於2025-07-05
  • 在Pandas中如何將年份和季度列合併為一個週期列?
    在Pandas中如何將年份和季度列合併為一個週期列?
    pandas data frame thing commans date lay neal and pree pree'和pree pree pree”,季度 2000 q2 這個目標是通過組合“年度”和“季度”列來創建一個新列,以獲取以下結果: [python中的concate...
    程式設計 發佈於2025-07-05
  • C++20 Consteval函數中模板參數能否依賴於函數參數?
    C++20 Consteval函數中模板參數能否依賴於函數參數?
    [ consteval函數和模板參數依賴於函數參數在C 17中,模板參數不能依賴一個函數參數,因為編譯器仍然需要對非contexexpr futcoriations contim at contexpr function進行評估。 compile time。 C 20引入恆定函數,必須在編譯時進...
    程式設計 發佈於2025-07-05
  • 如何使用Depimal.parse()中的指數表示法中的數字?
    如何使用Depimal.parse()中的指數表示法中的數字?
    在嘗試使用Decimal.parse(“ 1.2345e-02”中的指數符號表示法時,您可能會出現錯誤。這是因為默認解析方法無法識別指數符號。 成功解析這樣的字符串,您需要明確指定它代表浮點數。您可以使用numbersTyles.Float樣式進行此操作,如下所示:[&& && && &&華氏度D...
    程式設計 發佈於2025-07-05
  • 我可以將加密從McRypt遷移到OpenSSL,並使用OpenSSL遷移MCRYPT加密數據?
    我可以將加密從McRypt遷移到OpenSSL,並使用OpenSSL遷移MCRYPT加密數據?
    將我的加密庫從mcrypt升級到openssl 問題:是否可以將我的加密庫從McRypt升級到OpenSSL?如果是這樣,如何? 答案:是的,可以將您的Encryption庫從McRypt升級到OpenSSL。 可以使用openssl。 附加說明: [openssl_decrypt()函數要求...
    程式設計 發佈於2025-07-05
  • 如何簡化PHP中的JSON解析以獲取多維陣列?
    如何簡化PHP中的JSON解析以獲取多維陣列?
    php 試圖在PHP中解析JSON數據的JSON可能具有挑戰性,尤其是在處理多維數組時。要簡化過程,建議將JSON作為數組而不是對象解析。 執行此操作,將JSON_DECODE函數與第二個參數設置為true:[&&&&& && &&&&& json = JSON = JSON_DECODE($ ...
    程式設計 發佈於2025-07-05
  • 如何有效地選擇熊貓數據框中的列?
    如何有效地選擇熊貓數據框中的列?
    在處理數據操作任務時,在Pandas DataFrames 中選擇列時,選擇特定列的必要條件是必要的。在Pandas中,選擇列的各種選項。 選項1:使用列名 如果已知列索引,請使用ILOC函數選擇它們。請注意,python索引基於零。 df1 = df.iloc [:,0:2]#使用索引0和1 ...
    程式設計 發佈於2025-07-05
  • JavaScript計算兩個日期之間天數的方法
    JavaScript計算兩個日期之間天數的方法
    How to Calculate the Difference Between Dates in JavascriptAs you attempt to determine the difference between two dates in Javascript, consider this s...
    程式設計 發佈於2025-07-05
  • 表單刷新後如何防止重複提交?
    表單刷新後如何防止重複提交?
    在Web開發中預防重複提交 在表格提交後刷新頁面時,遇到重複提交的問題是常見的。要解決這個問題,請考慮以下方法: 想像一下具有這樣的代碼段,看起來像這樣的代碼段:)){ //數據庫操作... 迴聲“操作完成”; 死(); } ? > ...
    程式設計 發佈於2025-07-05
  • 如何在JavaScript對像中動態設置鍵?
    如何在JavaScript對像中動態設置鍵?
    在嘗試為JavaScript對象創建動態鍵時,如何使用此Syntax jsObj['key' i] = 'example' 1;不工作。正確的方法採用方括號: jsobj ['key''i] ='example'1; 在JavaScript中,數組是一...
    程式設計 發佈於2025-07-05
  • 編譯器報錯“usr/bin/ld: cannot find -l”解決方法
    編譯器報錯“usr/bin/ld: cannot find -l”解決方法
    錯誤:“ usr/bin/ld:找不到-l “ 此錯誤表明鏈接器在鏈接您的可執行文件時無法找到指定的庫。為了解決此問題,我們將深入研究如何指定庫路徑並將鏈接引導到正確位置的詳細信息。 添加庫搜索路徑的一個可能的原因是,此錯誤是您的makefile中缺少庫搜索路徑。要解決它,您可以在鏈接器命令中添...
    程式設計 發佈於2025-07-05
  • 如何使用FormData()處理多個文件上傳?
    如何使用FormData()處理多個文件上傳?
    )處理多個文件輸入時,通常需要處理多個文件上傳時,通常是必要的。 The fd.append("fileToUpload[]", files[x]); method can be used for this purpose, allowing you to send multi...
    程式設計 發佈於2025-07-05

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

Copyright© 2022 湘ICP备2022001581号-3