您現在的位置是:首頁 > 棋牌

了不起的 Unicode!

  • 由 CSDN 發表于 棋牌
  • 2023-01-31
簡介每個Unicode程式碼點都表示一個字元

百分之u是幾進位制

本文精心挑選了許多優秀的Unicode小技巧、軟體包和資源。

了不起的 Unicode!

譯者 | 彎月,責編 | 郭芮

以下為譯文:

Unicode非常了不起!在Unicode出現之前,國際交流是一團糟——每個人都在ASCII碼錶的後半部分割槽域(稱為“內碼表”)定義了自己的擴充套件和字符集,從而導致各種衝突。想想就知道,德國人要與韓國人只使用127個字元組成的內碼表進行交流會有多麼困難。

——幸虧有了Unicode標準和統一的交流規範。

Unicode 8。0根據129多種書寫體系,標準化了超過120,000個字元,其中包括現代字元、古代字元,甚至還包括人類尚未解密的文字。Unicode能處理從左到右和從右到左兩種書寫方式,支援組合標記,還支援多種文化、政治、宗教方面的字元,甚至還有表情符號。

Unicode太了不起了,我們對它的崇拜猶如滔滔江水綿綿不絕。

Unicode的背景

Unicode標準支援什麼字元?

Unicode標準定義了今日所有主流的書寫語言中用到的字元。Unicode支援的書寫體系包括歐洲的語系、中東的從右至左書寫的語系,以及亞洲的多種語系。

Unicode標準還包含了標點符號、聲調符號、數學符號、科技符號、箭頭、各種圖形符號、表情符號,等等。Unicode為聲調符號(用來改變其他字元的符號,如波浪線~)單獨提供了程式碼,這些程式碼可以與基礎字元組合使用,來表示有聲調的字元(如)。Unicode標準9。0版總共提供了128,172個字元的程式碼,其中包括了全世界的字元、圖形和符號。

絕大部分的常用字元都能對映到最前面的64K個程式碼點上,這一區域叫做基本多文種平面(basic multilingual plane,簡稱為BMP)。還有十六個補充平面用來編碼其他字元,目前尚有850,000個未使用的程式碼點。人們還在考慮在以後的版本中新增更多的字元。

Unicode標準還保留了一些程式碼點供私人使用。供應商或終端使用者可以在內部利用這些程式碼點表示他們自己的字元和符號,或者透過特殊的字型來使用。BMP上有6,400個私有程式碼點,如果不夠的話,補充平面上還有131,068個私有程式碼點可供使用。

Unicode字元編碼

字元編碼標準不僅定義了每個字元的唯一標識(即字元的數字值,或者叫做程式碼點),也定義了怎樣用位元來表示這個值。

Unicode標準定義了三種編碼形式,允許同一個資料以一位元組、兩位元組或四位元組的格式來傳輸(即每個程式碼單元可以是8位元、16位元或32位元)。同一個字符集可以使用所有三種編碼形式,它們之間可以互相轉換,而不會丟失資料。Unicode聯盟建議根據實際需要,選擇任何一種方便的編碼方式來實現Unicode標準。

UTF-8在HTML和類似協議上非常常用。UTF-8使用變長編碼。它的優點是,對應於ASCII字符集的那些Unicode字元的位元組值與它們在ASCII中的值完全相同,因此使用UTF-8編碼的Unicode字元可以在絕大多數已有軟體上使用,無需對軟體做出任何修改。

UTF-16在許多需要平衡效能和儲存效率的環境中非常常用。它足夠緊湊,所有常用的字元都可以用一個16位元的程式碼單元來表示,其他字元可以使用一對16位元程式碼單元來表示。

UTF-32在無需顧慮記憶體空間的情況下使用,它是定長編碼,每個字元只有一個程式碼單元。每個Unicode字元編碼成一個32位元程式碼單元。

在所有三種編碼中,每個字元最多需要4個位元組(32位元)表示。

數字問題

Unicode字符集被分成17個核心段,稱為“平面”,每個平面又被分成若干區塊。每個平面的空間足夠容納65,536(216)個程式碼點,因此總共有1,114,112個程式碼點。還有兩個“私有區域”平面(#16和#17),可以按照使用者的意願定義。這兩個私有平面共包含131,072個程式碼點。

了不起的 Unicode!

第一個平面叫做“基本多文種平面”,或者稱為BMP。它包含程式碼點U+0000到U+FFFF,這個範圍內包含了絕大部分常用字元。另外16個平面(U+010000到U+10FFFF)稱為補充平面。

UTF-16代理對

“BMP之外的字元,例如U+1D306 tetragram for centre (),在UTF-16編碼中只能編碼成兩個16位元程式碼單元:0xD834 0xDF06。這種情況稱為代理對(surrogate pair)。注意代理對只表示一個字元。 “代理對的第一個字元永遠在0xD800到0xDBFF的範圍內,稱為高位代理,或者叫起始位元組代理。代理對的第二個程式碼單元永遠在0xDC00到0xDFFF的範圍內,稱為低位代理,或者叫末端代理。”

——Mathias Bynens

“代理對:一種表示方式,用於表示由兩個16位元程式碼單元組成的單個抽象字元,其中第一個值稱為高位代理程式碼單元,第二個值稱為低位代理單位。代理對僅在UTF-16中使用。”

——Unicode 8。0 第3。9章,代理對(參見Unicode編碼)

計算代理對

代理字元 Pile of Poo (U+1F4A9) 在UTF-16中必須編碼成代理對,即兩個代理。要將程式碼點轉換成代理對,可以使用以下演算法(用JavaScript編寫)。注意我們使用的是十六進位制表示。

var High_Surrogate = function(Code_Point){ returnMath。floor((Code_Point - 0x10000) / 0x400) + 0xD800 };

var Low_Surrogate = function(Code_Point){ return (Code_Point - 0x10000) % 0x400 + 0xDC00 };

// Reverses The Conversion

var Code_Point = function(High_Surrogate, Low_Surrogate){

return (High_Surrogate - 0xD800) * 0x400 + Low_Surrogate - 0xDC00 + 0x10000;

};

> var codepoint = 0x1F4A9; // 0x1F4A9 == 128169

> High_Surrogate(codepoint)。toString(16)

“d83d”// 0xD83D == 55357

> Low_Surrogate(codepoint)。toString(16)

“dca9”// 0xDCA9 == 56489

> String。fromCharCode( High_Surrogate(codepoint) , Low_Surrogate(codepoint) );

“”

> String。fromCodePoint(0x1F4A9)

“”

> ‘\ud83d\udca9’

“”

組合和解組合

Unicode包括了一種修改字元形狀的機制,大幅擴充套件了Unicode支援的字元量。使用聲調符號進行組合就是其中一種方式。聲調符號寫在主字元的後面。多個聲調符號可以疊在同一個字元上。對於絕大部分常用的字母聲調組合,Unicode還包括了預先組合好的版本。

特定的字元序列也可以用單個字元表示,稱為“預組合字元”(或者叫組合字元,可以解組合的字元)。例如,字元“ü”可以編碼成單個程式碼單元U+00FC “ü”,也可以編碼成基本字元U+0075 “u”後接無空白字元U+0308 “¨”。Unicode標準中設定的預組合字元是為了相容Latin 1等標準,後者包含了許多預組合字元,如“ü”和“”。

預組合字元可以進行接組合,以保持一致性,或用於分析。例如,需要將一組名稱轉換為英文字母時,可以將字元“ü”解組合為“u”後接非空白字元“¨”。解組合後的結果很容易處理,因為該組合字元可以處理成“u”後接一個修飾字符。這樣很容易進行按字母順序排序等,因為修飾字符不會影響字母順序。Unicode標準為所有預組合字元定義瞭解組合方式(https://unicode。org/versions/Unicode8。0。0/ch03。pdf#page=44)。它還定義了正規化的方式,以便為字元提供唯一的表示方法。

Unicode之謎

來自Mark Davis的《Unicode之謎》幻燈片(https://macchiato。com/slides/UnicodeMyths。pdf)。

Unicode只不過是16位元編碼。一些人誤認為Unicode只不過是16位元編碼,每個字元佔用16位元,因此一共有65,536個可能的字元。實際上這是不正確的。這樣是關於Unicode的最大誤解,所以也難怪一些人會這麼想。

任何未分配的程式碼點都可以用於內部用途?錯。最終,那些未分配的地方都會被某個字元使用。你應該使用私有用途程式碼點,或非字元程式碼點。

每個Unicode程式碼點都表示一個字元?錯。有許多非字元程式碼點(FFFE,FFFF,1FFFE,……)還有許多代理程式碼點、私有程式碼點和未分配的程式碼點,還有控制和格式“字元(RLM,ZWNJ,……)

字元對映是一對一的?錯。對映關係也可能是:

一對多:( → SS )

上下文相關:(…Σ … 和 …ΣΤ… …στ… )

語言相關:( I 和 i )

實用Unicode編碼手冊

了不起的 Unicode!

編碼型別編碼

了不起的 Unicode!

神奇的字元列表

了不起的 Unicode!

特殊字元

詳情可以參照Unicode聯盟釋出的《通用標點符號表》(https://www。unicode。org/charts/PDF/U2000。pdf)。

了不起的 Unicode!

了不起的 Unicode!

了不起的 Unicode!

等等,你說什麼?

變數識別符號可以包含空白!

U+3164 HANGUL FILLER 字元顯示為佔據空間的空白字元。如果渲染器不支援,則會渲染成完全不可見(也不會佔據任何空間,即“零寬度”)。這就是說,永遠不會看到醜陋的字元替代符號()。

我不知道為什麼U+3164被設計成這種行為。有意思的是,U+3164是在Unicode 1。1版本(1993年)加入的,所以聯盟一定是花了很多時間思考它。下面是幾個例子:

> var = ‘foo’;

undefined

>

‘foo’

> var = alert;

undefined

> var foo = ‘bar’

undefined

> if ( foo ===`baz` ){} // alert

undefined

> varvarfoo\u{A60C}π = ‘bar’;

undefined

> varfooπ

‘bar’

注意:我在Ubuntu和OSX下測試了下述程式的渲染結果:Node,PHP,Ruby,Python3。5,Scala,Vim,Cat,Chrome+GitHub gist。Atom是唯一無法正確渲染,將其顯示成空方塊的程式。我還沒有測試Emacs和Sublime。據我的理解,Unicode聯盟不會改變或重新命名字元或程式碼點,但有可能會改變字元屬性,如ID_Start或ID_Continue等。

修飾符

零寬度連線符(ZWJ)是個不可列印字元,用於某些複雜語系的計算機排版系統中,如阿拉伯語系、印度語系等。將ZWJ放在兩個本來不會連線的字元之間,將會導致它們以連線的形式列印。

零寬度不連線符(ZWNJ)是個不可列印字元,那些使用連線的書寫系統的計算機化。將ZWNJ放在兩個本來會連線在一起的字元之間,會導致它們以本來的形式列印。空格字元也有同樣的效果,但ZWNJ的作用是它能保證輸出的兩個字元儘可能靠近,或者連線一個詞及其語素。

> ‘a’

“a”

> ‘a\u{0308}’

“a”

> ‘a\u{20DE}\u{0308}’

“a”

> ‘a\u{20DE}\u{0308}\u{20DD}’

“a”

// Modifying Invisible Characters

> ‘\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}’

“”

> ‘\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}’。length

10

大寫變換衝突

了不起的 Unicode!

小寫變換衝突

奇怪現象和排查方法

字串長度通常由統計程式碼點的個數來計算。這就是說,代理對會被統計成兩個字元。多個聲調符號會疊放在同一個字元上,例如a + == a,從而增加長度,但它們只會產生一個字元。

類似地,字串翻轉通常非常困難。同樣,代理對和帶有聲調符號的字元必須作為整體進行翻轉。ES Reverser提供了一個非常好的解決方法。

字元對映是一對一的?錯。對映關係也可能是:

一對多:( → SS )

上下文相關:(…Σ … 和 …ΣΤ… …στ… )

語言相關:( I 和 i )

一對多對映

絕大多數字符,在大寫的時候表示一對多關係;另一些字元小寫的時候表示一對多關係。

優秀的軟體包和庫

PhantomScript::ghost: :flashlight: 不可見的JavaScript程式碼執行和社會工程工具。

ESReverser:用JavaScript編寫的支援Unicode的字串翻轉。

mimic:Unicode的惡作劇。

python-ftfy:輸入Unicode文字,輸出更一致、更不容易出現顯示錯誤的表現形式。

vim-troll-stopper:防止Unicode的搗亂字元搞亂你的程式碼。

表情符號

Unicode聯盟的表情符號表(https://www。unicode。org/emoji/charts/full-emoji-list。html)

Emojipedia(https://emojipedia。org/):關於特定表情符號的資訊,新聞部落格。

World Translation Foundation(https://www。emojifoundation。com/):宣傳、探索,還可以將文字翻譯成用表情符號表示的形式。

Can I Emoji? (https://caniemoji。com/android-2/):顯示當前iOS、Android和Windows對於表情符號的原生支援情況。

怎樣註冊一個表情符號URL(https://www。name。com/blog/how-tos/2015/12/want-an-emoji-url-this-is-how-you-register-one/)

多樣性

Unicode聯盟在支援人類的多樣性和多元文化方面做出了很多努力。這裡是聯盟提供的多樣性報告(https://unicode。org/reports/tr51/#Diversity)。

現在表情符號已經支援混合型別,比如同性家庭、握手、接吻等。真正轟動的是表情符號組合序列。基本上來說:

了不起的 Unicode!

此外,表情符號現在還支援膚色修飾字符了。

“有五個符號修飾字符可以為Unicode 8。0版(2015年中期)中釋出的人類的表情符號提供一系列的膚色。這些字元基於Fitzpatrick度量(面板學上的著名標準,網上也有許多例子,比如FitzpatrickSkinType。pdf)定義的六種膚色。不同的實現的精確顏色可能不同。”——Unicode聯盟的多樣性報告

只需要在所需的表情符號後面接上膚色修飾字符 \u{1F466}\u{1F3FE} 即可。

了不起的 Unicode!

有創意的變數名和方法名

示例採用JavaScript(ES6)編寫。

一般而言,帶有ID_START屬性的字元可以用在變數名開頭,而帶有ID_CONTINUE屬性的字元可以用在變數名中除了首字元之外的其他位置。

functionrand(μ,σ){ 。。。 };

String。prototype。reverse = function(){。。};

Number。prototype。isTrue = function(){。。};

var WhatDoesThisDo = 42

下面是Mathias Bynes(https://mathiasbynens。be/notes/javascript-identifiers#examples)提供的一些極富創意的變數名:

// How convenient!

var π = Math。PI;

// Sometimes, you just have to use the Bad Parts of JavaScript:

var _ = eval;

// Code, Y U NO WORK?!

var _益_ = 42;

// How about a JavaScript library for functional programming?

var λ = function() {};

// Obfuscate boring variable names for great justice

var \u006C\u006F\u006C\u0077\u0061\u0074 = ‘heh’;

// …or just make up random ones

var = ‘huh’;

// While perfectly valid, this doesn’t work in most browsers:

var foo\u200Cbar = 42;

// This is *not* a bitwise left shift (`<<`):

var = 2;

// This is, though:

<< ; // 8

// Give yourself a discount:

var price_99_89 = ‘cheap’;

// Fun with Roman numerals

var Ⅳ = 4;

var Ⅴ = 5;

Ⅳ + Ⅴ; // 9

// Cthulhu was here

var HE_OMET = ‘Zalgo’;

下面是David Walsh提供的一些Unicode CSS類名(https://davidwalsh。name/unicode-css-classes):

<!—— place this within the document head ——>

<!—— error message ——>

You do not have access to this page。

<!—— success message ——>

Your changes have been saved successfully!

。_ {

border: 1px solid #f00;

}

。 {

background: lightgreen;

}

遞迴的HTML標籤重新命名指令碼

如果你想把所有HTML標籤重新命名,使之看上去像什麼都沒有,那麼可以使用以下的指令碼。

但要注意,HTML並不會支援所有的Unicode字元。

// U+1160 HANGUL JUNGSEONG FILLER

transformAllTags(‘’);

// An actual HTML element node designed to look like a comment node, using the U+01C3 LATIN LETTER RETROFLEX CLICK

// <—— name=“viewport” content=“width=device-width”>

transformAllTags(‘——’);

// or even <

transformAllTags(‘\u{1160}\u{20dd}’);

// and for a bonus, all existing tag names will have each character ensquared。 html

transformAllTags();

functiontransformAllTags(newName){

// querySelectorAll doesn‘t actually return an array。

Array。from(document。querySelectorAll(’*‘))

。forEach(function(x){

transformTag(x, newName);

});

}

functionwonky(str){

return str。split(’‘)。join(’\u{20de}‘) + ’\u{20de}‘;

}

functiontransformTag(tagIdOrElem, tagType){

var elem = (tagIdOrElem instanceof HTMLElement) ? tagIdOrElem : document。getElementById(tagIdOrElem);

if(!elem || !(elem instanceof HTMLElement))return;

var children = elem。childNodes;

varparent = elem。parentNode;

var newNode = document。createElement(tagType||wonky(elem。tagName));

for(var a=0;a

newNode。setAttribute(elem。attributes[a]。nodeName, elem。attributes[a]。value);

}

for(var i= 0,clen=children。length;i

newNode。appendChild(children[0]); //0。。。always point to the first non-moved element

}

newNode。style。cssText = elem。style。cssText;

parent。replaceChild(newNode,elem);

}

下面是確定能夠支援的字元:

functiontestBegin(str){

try{

eval(`document。createElement( ’${str}‘ );`)

returntrue;

}

catch(e){ returnfalse; }

}

functiontestContinue(str){

try{

eval(`document。createElement( ’a${str}‘ );`)

returntrue;

}

catch(e){ returnfalse; }

}

下面是一些基本的結果:

// Test if dashes can start an HTML Tag

> testBegin(’-‘)

< false

> testContinue(’-‘)

< true

> testBegin(’-‘) // Prepend dash with U+1160 HANGUL JUNGSEONG FILLER

< true

Unicode字型

單一的TrueType / OpenType 字型格式無法支援所有UTF-8字元,因為字型檔案有最大65535個字形的限制。UTF-8的字形超過了110萬,因此你需要一個font-family才能覆蓋所有字型。

https://en。wikipedia。org/wiki/Unicode_font#List_of_Unicode_fonts

https://www。unifont。org/fontguide/

Unicode標準的原則

Unicode標準設定了下述基本原則:

通用原則——曾經出現過的一切書寫系統都應該在標準中體現。

邏輯順序——在雙向文字中,字元以邏輯順序儲存,而不是表現順序儲存。

效率——文件必須是高效的、完整的。

統一——不同文化或語言使用同一個字元時,應該僅儲存一次。

記錄字元而不是字形——應當對字元進行編碼,而不是字形。字形就是字元的實際圖形表示。

動態組合——新的字元可以與已有的標準化後的字元進行組合。例如,字元“”可以用字元“A”和分音符“¨”組合而成。

語義——包含的字元必須有明確定義,必須與其他字元有明確的區別。

穩定——字元一旦被定義,就永遠不能被移除,其程式碼點也不能被挪作他用。如果出錯,則應該將程式碼點標記為棄用。

純文字——標準中的字元應當是純文字,永遠不應該包含標記或元字元。

可轉換——每一種編碼都應該可以用Unicode編碼表示。

原文:https://wisdom。engineering/awesome-unicode/

本文為 CSDN 翻譯,轉載請註明來源出處。

【End】

Python系列學習成長課來了!15年經驗專家、CSDN特級講師親自授課,還等什麼?立即掃碼報名學習:

上一篇:分手了還聯絡你是什麼意思?

下一篇:消費元宇宙尚欠火候,工業元宇宙很可能先獲成功

Top