當前位置: 華文問答 > 數碼

電腦內部是如何處理漢字的輸入輸出和儲存過程的?

2018-10-28數碼

要理解漢字是如何被電腦處理的,首先就要理解編碼原理。

其實不光漢字;實質上,電腦內部的一切符號都要借助編碼來輸入輸出。

千字文
作者:周興嗣 南梁
天地玄黃 宇宙洪荒 日月盈昃 辰宿列張 寒來暑往 秋收冬藏 閏餘成歲 律召調陽 雲騰致雨 露結爲霜 金生麗水 玉出崑岡 劍號巨闕 珠稱夜光 果珍李柰 菜重芥薑 海鹹河淡 鱗潛羽翔 龍師火帝 鳥官人皇 始制文字 乃服衣裳 推位讓國 有虞陶唐 弔民伐罪 周發殷湯 坐朝問道 垂拱平章 愛育黎首 臣伏戎羌 遐邇壹體 率賓歸王 鳴鳳在樹 白駒食場 化被草木 賴及萬方 蓋此身髮 四大五常 恭惟鞠養 豈敢毀傷 女慕貞絜 男效才良 知過必改 得能莫忘 罔談彼短 靡恃己長 信使可覆 器欲難量 墨悲絲淬 詩讚羔羊 景行維賢 克念作聖 德建名立 形端表正 空谷傳聲 虛堂習聽 禍因惡積 福緣善慶 尺璧非寶 寸陰是競 資父事君 曰嚴與敬 孝當竭力 忠則盡命 臨深履薄 夙興溫凊 似蘭斯馨 如松之盛 川流不息 淵澄取映 容止若思 言辭安定 篤初誠美 慎終宜令 榮業所基 籍甚無竟 學優登仕 攝職從政 存以甘棠 去而益詠 樂殊貴賤 禮別尊卑 上和下睦 夫唱婦隨 外受傅訓 入奉母儀 諸姑伯叔 猶子比兒 孔懷兄弟 同氣連枝 交友投分 切磨箴規 仁慈隱惻 造次弗離 節義廉退 顛沛匪虧 性靜情逸 心動神疲 守眞誌滿 逐物意移 堅持雅操 好爵自縻 都邑華夏 東西二京 背邙面洛 浮渭據涇 宮殿盤鬱 樓觀飛驚 圖寫禽獸 畫彩仙靈 丙舍傍啟 甲帳對楹 肆筵設席 鼓瑟吹笙 升階納陛 弁轉疑星 右通廣內 左達承明 既集墳典 亦聚羣英 杜稾鍾隸 漆書壁經 府羅將相 路俠槐卿 戶封八縣 家給千兵 高冠陪輦 驅轂振纓 世祿侈富 車駕肥輕 策功茂實 勒碑刻銘 磻溪伊尹 佐時阿衡 奄宅曲阜 微旦孰營 桓公匡合 濟弱扶傾 綺迴漢惠 說感武丁 俊乂密勿 多士寔寧 晉楚更霸 趙魏困橫 假途滅虢 踐土會盟 何遵約法 韓弊煩刑 起翦頗牧 用軍最精 宣威沙漠 馳譽丹青 九州禹跡 百郡秦並 嶽宗恆岱 禪主雲亭 雁門紫塞 雞田赤城 昆池碣石 鉅野洞庭 曠遠緜邈 巖岫杳冥 治本於農 務茲稼穡 俶載南畝 我藝黍稷 稅熟貢新 勸賞黜陟 孟軻敦素 史魚秉直 庶幾中庸 勞謙謹敕 聆音察理 鑑貌辨色 貽厥嘉猷 勉其祗植 省躬譏誡 寵增抗極 殆辱近恥 林臯幸即 兩疏見機 解組誰逼 索居閒處 沈默寂寥 求古尋論 散慮逍遙 欣奏累遣 慼謝歡招 渠荷的歷 園莽抽條 枇杷晚翠 梧桐早雕 陳根委翳 落葉飄颻 遊鵾獨運 淩摩絳霄 耽讀翫市 寓目囊箱 易輶攸畏 屬耳垣牆 具膳餐飯 適口充腸 飽飫烹宰 飢厭糟糠 親戚故舊 老少異糧 妾禦績紡 侍巾帷房 紈扇圓潔 銀燭煒煌 晝眠夕寐 籃筍象牀 弦歌酒讌 接杯舉觴 矯手頓足 悅豫且康 嫡後嗣續 祭祀烝嘗 稽顙再拜 悚懼恐惶 箋牒簡要 顧答審詳 骸垢想浴 執熱願涼 驢騾犢特 駭躍超驤 誅斬賊盜 捕獲叛亡 布射遼丸 嵇琴阮嘯 恬筆倫紙 鈞巧任釣 釋紛利俗 並皆佳妙 毛施淑姿 工顰妍笑 年矢每催 曦暉朗耀 琁璣懸斡 晦魄環照 指薪脩祜 永綏吉劭 矩步引領 俯仰廊廟 束帶矜莊 徘徊瞻眺 孤陋寡聞 愚蒙等誚 謂語助者 焉哉乎也

比如,我們可以用這篇千字文作為編碼依據,寫出數碼1,就對應於天;寫出數碼2,就對應於地……那麽,我們就可以用最多四位數碼把所有這些字表示出來。

然後,如果咱組織個天地會的話,就可以用數碼1 2作為接頭暗號;或者,更中二一點——左手一個指頭指天,右手兩個指頭指地……

更精細點的話,我們可以把每個手指的伸曲作為不同的編碼;那麽兩只手10根手指就能編碼1024個不同文字——左手伸一下拇指,收回;然後再伸一下食指……

接頭人一看:哎呀同誌!可算找到組織了!

咳咳。

總之,我們看到了,文字是可以用其它方式「曲折隱晦」的表達的。

既然手指的伸曲可以作為一個不同的狀態、使得我們一雙手伸20次就能吟詠完這首詩:

白日依山盡,黃河入海流。 欲窮千裏目,更上一層樓。

那麽,用10根電線的不同通電狀態組合,是不是也能編碼這篇千字文?

沒錯。這就是數碼電路原理。

我們把電線有電叫做狀態1,電線沒電叫做狀態0;於是,00000 00001就是一個狀態,而00000 00010是另一個狀態——規定每個位的電訊號權值為2^n,其中n是第n條電線,從0開始編碼:這就是二進制數碼。

二進制拿來記錄處理電路狀態實在太方便了;因此,電腦裏面習慣用二進制描述電路狀態。比如,寄存器當前值是00000 11000、地址線收到一個訊號00100 11011,等等。

請記住,這並不是說電路裏神奇的出現了0和1,而是我們把電路斷開(無電)叫0、電路閉合(有電)叫1,從而可以用0/1組成的數碼串簡潔的把「一把導線裏面每根線的電壓狀態」表示出來。

如果不明白的話,請反復誦讀這句話:00100 11011 應該讀作 斷斷通斷斷 通通斷通通。

二進制用起來還是不夠方便。

事實上,按照每位的權值是2^N的規則(和十進制計數法每位權值是10^N一模一樣),我們可以很容易的把它轉換為十進制表示——註意只是表示。

比如,電路狀態 斷斷通斷斷 通通斷通通,對應的二進制表示為00100 11011,十進制表示為155,十六進制表示為98——表示成十進制、十六進制短了很多,方便記憶了;但卻不再能直觀的看出電路的通斷狀態:必須轉換回2進制,才知道這個狀態是 斷斷通斷斷 通通斷通通。

但對我們要處理漢字的人來說,電路狀態我們並不關心;只要能夠區分它、從而把它和一個漢字對應起來就行了。因此我們一般直接用16進制(方便和二進制互換),不用二進制。

總之,這個狀態是電路上實實在在的物理狀態,拿萬用表可以測出來。我們把這個東西叫做機內碼。

所謂編碼,就是把千字文、阿拉伯數碼以及康熙大字典中的每一個字對應到機內碼的過程。

比如,空格" "對應於十進制的32、十六進制的20以及二進制的00100000;阿拉伯數碼"1"則對應於十進制的49、十六進制的31以及二進制的00110001……以此類推。

可以看出,這個編碼可以是一個極其隨意的過程。

比如,我很隨意的用千字文編碼;但發明電腦的美國佬卻用ASCII碼先編碼了英文字母、阿拉伯數碼以及!@$#^%之類符號 ;其它各國在引進電腦時,也會自說自話的搞出自己的編碼方案、從而把本國語言文字輸入電腦……

比如,中國就有GB2132、GBK、GB18030等漢字編碼;但台灣、日本、南韓以及東南亞國家也用漢字,他們也搞了個另外的好多套編碼……

那麽,這一大堆編碼方案,誰說了算?

不然的話,按你的編碼,99 67 54是520,結果到法國佬那裏成了250……你們還不要打起來啊?

所以,現在搞了一個全球統一的Unicode碼,一網打盡全球所有語言。

編碼完成之後,漢字就可以和羅馬字母、法文字母、德文字母、俄文字母、英文字母一樣,以電訊號有無/磁化方向/光碟上的凹坑等等不同狀態儲存起來了。

其中,在光碟上時,它是一串凹坑的組合;在硬碟上,它是一組磁域的磁化方向;在記憶體條裏,它是場效應管的充電狀態;在並列總線上,它是一把金屬導線的電壓高低組合;在序列通訊線纜上,它是高高低低的一組方波……

所有這些狀態,都可以用二進制表示;所有這些二進制,都可以轉換成更方便閱讀和記憶的十進制/十六進制數碼——但想要直觀的理解電路狀態,都必須還原到二進制。

我們知道,鍵盤上面只有26個英文字母以及十個數碼和!@#$% 等特殊符號,以及tab、shift、enter等控制鍵(但也可以用來輸入)……

你看,沒有漢字啊?這怎麽辦?

事實上,鍵盤輸入,歸根結底也只是電訊號而已。

既然我們已經用abcd、1234等等編碼了這些電訊號;那麽,把這些電訊號轉譯回12345、然後直接用12345這些按鍵上的字元、拼湊出每個漢字的Unicode碼——這,不就解決了輸入問題嗎?

這個自動把鍵盤敲的字母組合轉譯成對應的漢字的程式,就是我們熟悉的輸入法。

嗯。別急,別急。我知道你記不住。沒人能記住超過六萬個漢字的Unicode編碼。所以並沒有這樣的輸入法。

雖然這個辦法的確行不通;但這個思路是成立的,對吧?

既然思路成立,那麽不要急,一步一步來。先找出問題,再一步步解決它。

現在的問題在於,用這種方法敲漢字,我們第一記不住編碼表,第二只能利用數碼小鍵盤,輸入效率太低。

怎麽辦呢?

沒錯。拼音。我們把二十六個英文字母當拼音,把每個漢字的讀音敲進去,不就好了?

沒那麽簡單。普通話一共才幾百個音,漢字六萬多……同音字實在太多了。這怎麽輸入?

沒錯。筆畫輸入。漢字歸根結底有五種基本筆畫,橫豎撇捺折對應12345,妥了——比如王字怎麽寫?橫橫豎橫,1121,哈哈搞定。

仍然不行。遇到工字和土字,你怎麽辦?

所以現在的問題是重碼,對吧?

好辦。把重碼字列出來,給使用者選擇。這下妥了吧?

沒錯。解決了。

甚至於,拼音輸入也能這樣解決。

不僅如此,既然普通話只有幾百個音,那沒必要敲全eng、ang吧?

給eng/ang也來個編碼,敲兩下,一個音就進去了——哈哈,雙拼!

但這樣仍然不好用。每敲一個字都要在候選字裏面找半天,這實在太慢了。

還得繼續最佳化。

現在有兩個思路。

一是王永民先生搞的五筆輸入法,思路是從編碼漢字的偏旁部首入手設計一套字根表,一方面減少了每次輸入的擊鍵次數,另一方面減少乃至杜絕了重碼。以至於五筆高手可以輕易做到每分鐘輸入超過200個漢字。

但五筆輸入法需要專門的訓練。背字根表,反復練習直到形成肌肉記憶……

因此,另一條思路獲得了更多支持,以致於現在只有專業錄入人員才會學習五筆。

這個思路就是:繼續使用拼音輸入法;但盡量讓使用者一次輸入一個詞、一句話;那麽借助軟件強大的分析能力,就可以「猜」出他最可能想說什麽。

打個比方的話,你說「lin」,沒人知道是哪個字;但你說「樹林」「林黛玉」「臨安」「臨潁」「淋雨」,這個字就唯一確定了。

當然,單字仍然無法確定;有些詞也有同音。但無論如何,這個策略使得重碼少了很多。尤其借助概率論的馬爾科夫鏈理論,整句輸入可以得到極高的準確率,幾乎不用選字,從而使得熟練的拼音使用者也能一分鐘輸入一百字以上。

總之,漢字的輸入是一個編碼的巢狀過程:先是擊鍵產生一個鍵盤碼,鍵盤碼被鍵盤內建的CPU轉換成對應的ASCII機內碼(並列路線是通斷錯落的一組電平,序列路線是一組方波);然後這些ASCII碼的組合(比如ABCD)又被轉換到漢字機內碼(Unicode或者GB2312/GBK/GB18030)——最終,這些機內碼被保存到磁盤/光碟等儲存介質,漢字從輸入到儲存這一條龍過程就算走完了。

註意這裏面反復進行了多次編碼和解碼;但編碼/解碼過程各自獨立,互不影響。

比如,你可以用拼音或者五筆編碼漢字、然後以這個編碼輸入;然後輸入法解碼你敲入的鍵盤字元序列,再自動幫你編碼到Unicode或者GB系列。

從哪個途徑轉換我們不需要關心。只要最終得到的Unicode正確。

編碼和儲存搞定了,怎麽輸出呢?

比如,見了12354這串數碼,你怎麽知道它應該是哪個漢字?這個漢字該怎麽寫?

在回答這個問題前,我們應該先想一想:螢幕上是如何顯示漢字的?

我們知道,螢幕是由一個個像素構成的。正如我對著自己顯視器拍的這張照片,像素的不同取值就構成了漢字的筆畫,最終組成我們見到的漢字。

那麽,像素的黑白,豈不是也可以用0和1編碼嗎?

沒錯,這就是點陣字型:

正字的Unicode編碼是十六進制的6B63,而6B63這個機內碼關聯到一副bitmap(位圖),這張位圖也可以用二進制編碼,就是上圖右側那那一堆數碼——按順序存下來,就是正字的16位元點陣。

類似的,還可以搞32位元或更大的點陣,從而把字型顯示的更精確一些。

總之,六萬多漢字就對應著6萬張不同的點陣圖片。

我們知道,漢字有不同的字型。比如楷書、隸書、宋體、仿宋,等等。這些就對應著不同的小圖片。

因此,每個不同字型又對應著不同的六萬張小圖片,你裝了100種字型,電腦裏就有了六百萬張不同的圖片。

將來,當需要顯示漢字時,從Unicode找到對應的圖片(也就是那一堆二進制串),再按照指示給顯視器不同的像素通電/斷電,於是顯視器上就黑白分明的顯示了不同漢字。

當然,顯視器的顯示原理也各不相同。比如,CRT是電子束轟擊熒光粉,沒有電子轟擊的地方就不發光;而LCD呢,哪裏通電「攪亂」的液晶,哪裏就混濁不透光;LED,哪個發光管通電哪個發光……

因此,具體顯示時,顯視器也還需要「轉譯」一下。比如CRT遇到0就改變電子槍柵極電壓,不讓電子飛出去;而LCD遇到0就切斷對應像素的電壓輸出,讓液晶恢復透明……

不僅如此。

不同顯示器尺寸不同、每英寸像質數也大相徑庭。32點陣的漢字,在某些螢幕上占地太大,一屏顯示不了幾個;但在視網膜屏上又顯得太小,拿放大鏡才看得見……

怎麽辦呢?

的確可以透過插值的方式縮放;但這樣效果並不好(插值是一種透過演算法自動添加像素的方式,比如正字放大一倍顯示時,就要把橫從一排像素改成兩排;但如果縮放不是整數倍、或者筆畫彎折處就很難確定該不該插入像素)。

另一種方式是,不記錄像素,記錄「寫字過程」。比如線條從哪裏起始、在哪裏轉折、到哪裏結束,以及哪裏下筆重、哪裏下筆輕,等等。

這就是所謂的「向量字型」,它記錄的是筆畫線條以及轉折、粗細等資訊,因此可以非常方便的縮放。

不僅如此。你可能註意到,我拍攝的實際螢幕照片裏,「評論」二字筆畫邊緣顏色有些發暗。

這是clear type處理後的結果。經過這個處理後,字型看起來筆畫更光滑、更銳利、更美觀。

換句話說,字元輸出並不像想象那麽簡單。它可能需要向量圖縮放、點陣圖轉換、cleartype/truetype之類技術處理點陣圖(註意這裏是彩色點陣,這種點陣裏,每個像素又分為紅綠藍三個分量,每個分量又有256級亮度),然後才能照圖施工、把文字輸出到螢幕上。

這就是漢字輸入輸出的全過程。