隨手扎
你可能不知道在JavaScript裡的萬國碼
那時、天下人的口音言語、都是一樣。
他們往東邊遷移的時候、在示拿地遇見一片平原、就住在那裏。
他們彼此商量說、來罷、我們要作甎、把甎燒透了。他們就拿甎當石頭、又拿石漆當灰泥。
他們說、來罷、我們要建造一座城、和一座塔、塔頂通天、爲要傳揚我們的名、免得我們分散在全地上。
耶和華降臨要看看世人所建造的城和塔。
耶和華說、看哪、他們成爲一樣的人民、都是一樣的言語、如今旣作起這事來、以後他們所要作的事、就沒有不成就的了。
我們下去、在那裏變亂他們的口音、使他們的言語、彼此不通。
於是耶和華使他們從那裏分散在全地上.他們就停工、不造那城了。
因爲耶和華在那裏變亂天下人的言語、使衆人分散在全地上、所以那城名叫巴別。〈就是變亂的意思〉
– 創世記11
從一封亂碼郵件開始說起
自己個人很少書寫電子郵件,收到的郵件也多數是某種活動、告知事項或廣告類信件。 有些時候會收到一些需要回信或是主動發信件的情況。絕大多數文字語言上都不是什麼問題。但就是偶爾會有一兩封信件在眼前呈現的是我看不懂得符號。
最近,我就收到過一封信件…是亂碼。
會呈現亂碼的原因,有很大程度是顯示軟體錯誤的解讀了信件內容。這種情況可能可以透過取得原始信件資料,在使用其他編輯器和解碼方式打開顯示。
以信件來說,常見的分成兩種類型:
- 純文字
- HTML文件
特別是HTML文件,會有一段以<meta/>
標記內容使用的編碼方式(charset
)。
我照往例將信件使用其他方式開啟,發現他已經標記著名是使用UTF-8
方式處理了。
嘗試使用其他方式解碼也沒有得到可以閱讀的內容。換言之,這對我而言是已經損毀內容真正意義上的亂碼信件了。
像這樣的文字問題,不是只有編碼發生。至少Uncle Bob每年會遇到一次換行問題。
你最近一次處理文字檔時,因使用了『錯誤』的行末約定而遭遇到問題,是在什麼時候呢?我每年至少要遇到一次。
– The Clean Coder
巧的是,我最近也遇到了換行問題。在過去也遇到過Python程式碼因為空白建(space)和製表鍵(tag)混合使用而造成的問題。
在計算機科學(資訊科學)上,巴別塔的程度不會比人類語言簡單,甚至我個人認為還更為複雜……
萬國碼
語言文字有非常多種。以我比較容易接觸到的就包含中文、英文、日文、韓文等等。甚至有一些語言並沒有完善的文字系統,像是閩南話;或是文字系統又因爲地區習慣有所差異,像中文就有臺灣繁體、香港繁體、大陸簡體等。儘管有些並不是看得懂、能了解意思,但單一個字符來說,對我而言並不是亂碼。
爲什麼在顯式上可以同時顯式這麼多種語言呢?這需要分成兩個部分:字體 和 編碼。
字體
先說一下並非本篇重點的 字體 。 首先對於顯式裝置,如LCD螢幕而言,畫面上的僅僅是一個個發光點而已。並不認識字體,因而需要有字體的描述檔案,讓顯式裝置明白如何顯式文字。
你可能可以在 C:\Windows\Fonts
或是 /usr/share/fonts/
等路徑下看到各式各樣的字體檔案。常見的副檔名有:.ttf
、.otf
、.woff
。你也可以利用FontForge製作自己的文字。
你或許正在使用思源體;或許聽過募資紅極一時的金萱體1;前陣子高中生創作的辰宇落雁體2。
以中文來說,要完成大量的字體是件艱鉅的任務。但種種例子證明這並不是不可能的任務。
編碼
從上面看得到的字體,接著要看一下看不太到的編碼。
資訊在儲存的時候是以0和1的存在方式。有多少個0與1一起被解讀,限制了單位內能夠儲存的類型數量。
比如說只有兩個儲存位置,也就只存在 $2^2 = 4$ 種可能性。也就是:
00
01
10
11
如果有7個位置,也就有 $2^7 = 128$ 種可能性。對於英文算上大小寫和數字共62個字母而言非常足夠了。
但是對於中文五千以上個字,顯然不太足。更不用說多國語言了。
一個問題是: 用不到的字符,保留該位置太浪費空間 。
想想生活上有多少機會是一次同時會看非常多種語言的。多數情況下以1-2種表示。
所以會依照需求設計不同編碼方式。
以中文使用者來說,除了中文外,需要的有英文、阿拉伯數字,以及標點符號可能就夠了。但 還存在地區用語用字習慣差異,也就存在了大五碼Big5
、GB2312
、GBK
、CCCII
、CNS 11643
。
也就是說,同樣的0011010 0110110
在不同需求下可能被表達成不同數量的不同文字。可能可以7個7個解讀成兩個文字,也可能需要14個一起被解讀。
萬國碼
但終究,還是有需要一次顯式多種語言文字的需求存在。因此出現了萬國碼(Unicode)。其中UTF-8
更成了網際網路上最常使用的編碼方式,也是多數較近代出現程式原始碼的儲存方式3。
自2009年以來,UTF-8一直是全球資訊網的最主要的編碼形式(對所有,而不僅是Unicode範圍內的編碼)(並由WHATWG宣布為強制性的「適用於所有事物(for all things)」,截止到2019年11月, 在所有網頁中,UTF-8編碼應用率高達94.3%(其中一些僅是ASCII編碼,因為它是UTF-8的子集)4
0和1到ASCII
如果有7個位置,也就有 $2^7 = 128$ 種可能性。對於英文算上大小寫和數字共62個字母而言非常足夠了。
電腦發展之初,只需要英文即可。可以說 ASCII 是讓電腦進入到有文字的歷史時代。除了0和1,ASCII成爲了最原始的電腦母語。
隨者電腦的發展,主流的位元組(bytes)習慣以8個比特(bits)組合。也有了以現在來說更習慣使用的,也幾乎是ASCII的等義詞的擴充版ASCII(Extended ASCII, EASCII)。
在現在的今天,ASCII幾乎有一個特殊神聖的地位在。許多編碼方式與之相同,如UTF-8。可以說如果你的檔案是以ASCII文字書寫儲存,就幾乎不會存在開起來出現亂碼的問題。
存文字的編碼申明訊息
程式語言原始碼多以純文字形式儲存。有一些純文字檔案會特別在開頭幾行說明使用什麼編碼方式,好讓編譯器、直譯器、編輯器可以正確解讀。譬如Python原始碼可以在開頭寫上# -*- coding: big5 -*-
。
以下面Python程式碼來說,如果使用big5儲存。
print("你好,世界怎麼跟得上臺灣")
然後使用Python3執行,你可能會得到錯誤訊息:
File "hello.py", line 1
SyntaxError: Non-UTF-8 code starting with '\xa7' in file hello.py on line 1, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
不過如果你加上了編碼的申明訊息,這段程式就可以被正確執行了:
# -*- coding: big5 -*-
print("你好,世界怎麼跟得上臺灣")
要注意的是# -*- coding: big5 -*-
也全是以可以被ASCII解讀的方式存在。
ASCII有一個特殊神聖的地位在
JavaScript世界裏的萬國碼
回到Web技術來看,JavaScript又是如何呢?為保險一些,或許原始碼都以ASCII能解讀的方式最好,但勢必有需要顯示ASCII以外的文字,又該如何是好?
在字面文字處理上,可以透過跳脫字元\u
來輸入Unicodea碼。完整方式是\uhhhh
,後面4個h
表示以16進位表達的Unicodea碼。
比如可以先找到「你」的UTF-16編碼號碼。
然後試著執行
console.log('\u4F60')
就會得到
你
依樣畫葫蘆,可以再執行看看:
console.log('\u4F60\u597D\uFF0C\u4E16\u754C')
就可以看到經典的「你好,世界」出現在眼前。
但並非所有的字都可以這樣表示,像是CJK UNIFIED IDEOGRAPH-28117就不行。須要寫成:
console.log("\ud860\udd17")
或是後來ES6支援的語法
console.log("\u{28117}")
不過該字在我使用的系統環境中並沒有對應的字體,因而顯示亂碼。
這也可以用來顯示emoji
console.log("給我一個大大的\u{1F44D}")
// 給我一個大大的👍
另外配和不同字體的使用,甚至可以顯示各式各樣的icon。
不過其實如果使用UTF-8儲存原始碼,在我經驗上多數JavaScript執行環境,包含Google Chrome、Mozilla Firefox、Microsoft Edge和Node.js都不會有什麼問題。
參考資料
- 林信良,Unicode與JavaScript字串。取用時間:2022.08.27。
本系列文參加 iT邦幫忙 的 2022鐵人賽
參賽主題: 這些那些你可能不知道我不知道的Web技術細節
Irvin, 炎上的金萱字型釋出記事 。取用時間:2022.08.27。 ↩︎
高中生自主學習計畫「自創手寫字體」被讚爆。取用時間:2022.08.27。 ↩︎
Python3預設以UTF-8解讀原始碼。(Using UTF-8 as the default source encoding) ↩︎