--- tags: System Software --- # "字元"其說: 終端(terminal)和字元編碼的前世今生 :::warning 由於這些歷史小弟在下並未親身經歷,只是統整各方的資料。可能收集的資訊有錯,歡迎勘誤 :pray: ::: ## 字元編碼 廣義來說,字元(character)是文字的基本單位,字元集(character set)則是指多個文字(可能是某種語言、數學符號等等)所形成的集合。由於電腦中的數據是由無數電晶體開關所建構成的,而這些開關只能有開/關兩種狀態,習慣上分別以 1 和 0 來表示,因此電腦內部可以看做是二進制數字的世界。為了方便文字在電腦中儲存和通過網路傳遞,我們需要統一的[字元編碼(Character encoding)](https://zh.wikipedia.org/zh-tw/%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81) 標準將文字與二進位數字映射。 ### Baudot code(ITA1) 要談到人類開始廣泛使用二進位編碼文字,以應用於儲存和通訊,可以追溯到印字電報機(printing telegraph)的發明。最著名的印字電報機最早是由法國人 Émile Baudot 所發明。其使用 [Baudot code](https://en.wikipedia.org/wiki/Baudot_code) 來交換信息,採用 5 個 bits 來進行編碼。Baudot 發明的電報裝置如下圖,發報方通過一個類似於鋼琴鍵盤樣的裝置輸入報文。該裝置上面有 5 個鍵,因此可以表示 $2^5 = 32$ 個狀態。然而,當時的需求除了希望能容下拉丁字母表的 26 個字母之外,還有 10 個數字與十幾個標點符號的輸入問題。Baudot 的解決辦法是使用兩套各自含 31 個元素的字元集,第一個字元集主要表示字母表,以及一個切換到第二個字元集的符號;第二個字元集表示 10 個阿拉伯數字及標點符號,以及一個切換到第一個字元集的符號;不共用切換符號。除了切換符號,又共用了兩個空白符號和兩個換行符號。這樣總共能表示 58 多個 Baudot code。收報方的機器自動解碼後,直接在紙帶上印出可供人直接閱讀的報文內容。Baudot code 也被稱作國際電報字母第 1 號(International Telegraph Alphabet No. 1,ITA1)。 ![](https://i.imgur.com/4H6s3Zk.png) :::info 沒錯 「印出在紙帶上」,所以我們會將顯示文字在終端(terminal)上稱為 `print` 並非毫無道理! ::: ### ITA2 在 1899 年,美國人 Donald Murray 發明了類似於標準打字機的電報輸入設備。這就是[電傳打字機(Teleprinter)](https://zh.wikipedia.org/zh-tw/%E9%9B%BB%E5%82%B3%E6%89%93%E5%AD%97%E6%A9%9F)。這使得只要是會用傳統打字機的普通人,就可以用接入電報網路的電傳打字機直接交流。電傳打字機通常由一個鍵盤、一台發送器、一台接收器及一台打印機組成。也有一些電傳打字機會付設打孔機及讀帶機,可以將訊息印製於[打孔帶(punched tape)](https://zh.wikipedia.org/zh-tw/%E6%89%93%E5%AD%94%E5%B8%B6) 上。或是用作讀帶機將已打孔的紙帶中的訊息讀取出來。編碼上還是採取了 Baudot 提出的 5 位元、雙字元集的辦法。但根據字符出現頻率而改進了編碼方案,使得高頻率字符只需要儘可能少的紙帶打孔,而低頻率字符就需要在紙帶上打孔較多。比方說 e 和 t 只需要打一個孔。這套編碼於 1930 年代被 CCITT 標準化為國際電報字母表第2號(the International Telegraph Alphabet No. 2,ITA2),一直使用到 1963 年被 7 位元的 ASCII 碼取代。 ![](https://i.imgur.com/9HHSi45.png) :::info 換句話說,打孔帶其實就相當於是過去電腦的「記憶體」! 打了孔的地方是 1,否則是 0,是不是和二進位的儲存方式一樣呢? ::: ### ASCII 在 1950 年代,電傳打字機開始連接到電腦上,而不是其他電傳打字機。由於 ITA2 是為機械設計的,它在位置下就顯得有些格格不入。於是為了電腦設計的 [ASCII](https://zh.wikipedia.org/zh-hant/ASCII) 出現了,它改為使用 7 bits 來表示文字。與電腦一起使用的電傳打字機被稱為終端(terminal,意涵為"連接的終點"、"終點站"),指的是讓用戶輸入數據至電腦,以及顯示結果的機器。而電傳打字機同時也被稱為 "TeleTYpewriter",簡稱 TTY。沒錯! 在現代系統中你是否有印象看過像是 /dev/tty 的名稱? 即便現代的電腦已經不再使用電傳打字機作為輸入輸出的設備,terminal 和 tty 這樣的單字也被沿用至今。 > 延伸閱讀: [命令行界面 (CLI)、终端 (Terminal)、Shell、TTY,傻傻分不清楚?](https://segmentfault.com/a/1190000016129862) :::info 人類過去真的是使用電傳打字機來寫程式的! 底下這張經典的圖片就是 Dennis Ritchie 和 Ken Thompson 使用 teletype 在 [PDP-11](https://zh.wikipedia.org/zh-tw/PDP-11) 上開發 UNIX 和 C。另外可以看看 [Altair 8800 - Video #7.1 - Loading 4K BASIC with a Teletype](https://youtu.be/qv5b1Xowxdk) 享受一下 ASMR (?),[Programming the PDP11](https://www.youtube.com/watch?v=XV-7J5y1TQc) 這個可愛的小短片也展示了過去人類是如何在 [PDP-11](https://zh.wikipedia.org/zh-tw/PDP-11) 上寫程式。 ![](https://i.imgur.com/YRALRjS.png) ::: ASCII 中除了要編碼拉丁字母或阿拉伯數字等會顯示在終端上的字元,另外還要提供代表特定功能的[控制字元(control characters)](https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%AD%97%E7%AC%A6)。控制字元一般來說不具備可顯示的圖形,主要是作為與電腦端溝通的語言,例如可用來終止資料的傳輸、換行等等。 :::success 雖然沒有特別提起,ITA1/ITA2 中當然也存在控制字元! ::: ### Unicode ASCII 的局限在於只能顯示 26 個基本拉丁字母、阿拉伯數字和英式標點符號,因此只能用於顯示現代美國英語,且處理 naïve、café、élite 這類外來語時,必須去除附加符號。於是 [EASCII](https://zh.wikipedia.org/wiki/EASCII) 從 ASCII 擴充出地 8 個 bits,加入包括表格符號、計算符號、希臘字母和特殊的拉丁符號。然而世界各地各有不同的文化發展,分別都創造自己的文字。比方說,數以萬計的漢字又該怎麼編碼呢? 定義自己國用的字元編碼算是一種辦法,像是在中國使用的 [GBK](https://zh.wikipedia.org/zh-tw/%E6%B1%89%E5%AD%97%E5%86%85%E7%A0%81%E6%89%A9%E5%B1%95%E8%A7%84%E8%8C%83)。在台灣這樣使用繁體中文的地區,我們則習慣用 [BIG5](https://zh.wikipedia.org/zh-tw/%E5%A4%A7%E4%BA%94%E7%A2%BC) (甚至還有[倚天 BIG5](https://zh.wikipedia.org/zh-tw/%E5%80%9A%E5%A4%A9%E4%B8%AD%E6%96%87%E7%B3%BB%E7%B5%B1),聽起來有點意思吧(?))。不過,缺乏一致與系統性的編碼不僅讓資訊領域的從業人員頭痛,實際上不免也造成普通民眾的困擾。 > * [司法話題》網路判決書一堆「□」 司院斥資改善系統](https://news.ltn.com.tw/news/focus/paper/1194776) 為解決編碼系統紊亂的現象,[Unicode](https://zh.wikipedia.org/zh-tw/Unicode) (統一碼,又譯作萬國碼、統一字元碼、統一字符編碼) 終於被推出。其直至今日仍在更新,以支援各國的文字。它是資訊科技領域的業界標準,使得電腦能以一致的字元集來顯示文字。有了 Unicode 以後,我們終於不必為了顯示正確的信息而在編碼系統間切換了。可喜可賀! ## 小結 自此,我們簡單的帶各位讀者一窺終端和字元編碼一路至今的發展。 不過,Unicode 既然可以表示如此多樣的文字([emoji](https://unicode.org/emoji/charts/full-emoji-list.html) 就是由 Unicode 編碼的!),自然也導致我們會在字元處理上衍生許多問題。Unicode 的編碼中可能存在許多你不知道的細節! 作為本世代的工程師自然不能置身事外,也得多多留意這些問題! 這裡列舉幾個問題: * 考慮下段程式碼,這是否可以通過編譯?如果可以,這裡應該要印出多少呢? ```cpp #include <stdio.h> #include <string.h> int main() { char *family = "👪"; char *handsomeguy = "林易緯"; printf("%ld\n", strlen(family)); printf("%ld\n", strlen(handsomeguy)); return 0; } ``` * 考慮下段程式碼,這是否可以通過編譯?如果可以,這裡應該要印出 YES 還是 NO? ```cpp #include <stdio.h> #include <string.h> int main() { char *c1 = "é"; char *c2 = "é"; if (strlen(c1) == strlen(c2)) { printf("YES\n"); } else { printf("NO\n"); } return 0; } ``` 或許有機會我們再聊聊這些有趣的知識吧~ ## Reference * [ASCII table and history](https://bestasciitable.com/) * [認識中文字元碼](https://idv.sinica.edu.tw/bear/charcodes/Section01.htm) * [西門子T100S電傳打字機](http://www.cmm.gov.mo/chi/exhibition/secondfloor/moreinfo/2_5_4_TTY_T100S.html) * [Baudot code](https://en.wikipedia.org/wiki/Baudot_code) * [電傳打字機(Teleprinter)](https://zh.wikipedia.org/zh-tw/%E9%9B%BB%E5%82%B3%E6%89%93%E5%AD%97%E6%A9%9F) * [ASCII](https://zh.wikipedia.org/zh-hant/ASCII) * [The TTY demystified](http://www.linusakesson.net/programming/tty/) * [命令行界面 (CLI)、终端 (Terminal)、Shell、TTY,傻傻分不清楚?](https://segmentfault.com/a/1190000016129862)