You are on page 1of 10

3/10/2018 演算法筆記 - Code

Code

Code

資訊替換成代號,以代號記載資訊。這些代號稱做「碼」。

99,3qㄋ姑力i讀豬,偶會+ Uㄉ!
口以ㄇ?ㄅ口以!
(゚д゚)

程式設計就是將「電腦的工作資訊」替換成「程式碼」。

int main() {
int n = 1 + 1;
return 0;
}

數學式子也是一種碼。大部分人類都不清楚原本的資訊為何。

eiπ + 1 = 0

「二元碼 Binary code 」,二進位字串,電腦設備所用的碼。本


章節的主角。

011000100110100101101110011000010111001001111001

「摩斯電碼 Morse Code 」,戰爭通訊所用的碼。

-- --- .-. ... .

「點字 Braille 」,盲人所用的碼。

⠃ ⠗ ⠁ ⠊ ⠇ ⠇ ⠑

「條碼 Barcode 」,包裝標示所用的碼。知名的條碼如:商品


的 Code39 、書籍的 ISBN 、導覽的 QR Code 。

「 International Code of Signals 」,船舶所用的碼。

http://www.csie.ntnu.edu.tw/~u91029/Code.html 1/10
3/10/2018 演算法筆記 - Code

「外星人編碼」,一根棒子,精準地刻一刀,就能記載世上所有
知識。找到刻記的高度,占筷子全長的幾分之幾,計算小數點後面所
有位數,就得到了碼。無論要談什麼,只要刻一刀就好。

UVa 508

Encode / Decode

「編碼」,資訊轉碼。「解碼」,碼轉資訊。

編碼 「cat」--->「011000110110000101110100」
解碼 「cat」<---「011000110110000101110100」

資訊和碼最好一一對應,讓編碼與解碼不生歧義。只要知道每種
碼的意義,就能獲知原本的資訊。火星文就是一種很不好的碼。

文字編碼

文字、聲音、圖像、動作、感受,通通可以編碼。以下我們只討
論文字編碼 ── 最簡單、最基礎的編碼。

英文變成二元碼,是參照「美國資訊交換標準碼 ASCII 」的規


定。 ASCII 的設計理念是:拆散英文文字,成為單獨的字母、符
號,各種不同的字符都有固定的二元碼。

ASCII
英文字母a變成二元碼01100001,符號=變成二元碼00111101。

繁體中文變成二元碼,是參照現時流行的「萬國碼 Unicode 」
或者逐漸落寞的「大五碼 Big5 」。

Unicode
中文字「大」變成二元碼0101100100100111。(十六進位數字5927)
Big5
中文字「大」變成二元碼1010010001101010。(十六進位數字a46a)
GB2312
中文字「大」變成二元碼1011010011110011。(十六進位數字b4f3)

文字編碼是以字符為基本單位,從頭到尾掃描。

http://www.csie.ntnu.edu.tw/~u91029/Code.html 2/10
3/10/2018 演算法筆記 - Code

編碼。從頭到尾掃描文字,每當發現一段文字有其對應的碼,就
馬上換成碼。持續掃描下去,讓碼越接越長。

編碼cat
掃描c,換成01100011
掃描a,換成01100001
掃描t,換成01110100
最後得到011000110110000101110100

解碼。從頭到尾掃描碼,每當發現一段碼有其對應的文字,就馬
上換成文字。持續掃描下去,讓文字越接越長。

解碼011000110110000101110100
掃描0
掃描1
掃描1
……
掃描第8個位元1,發現01100011是字母c的碼,換成c
……
掃描第16個位元1,發現01100001是字母a的碼,換成a
……
掃描第24個位元0,發現01110100是字母t的碼,換成t
最後得到cat

Fixed-length Code / Variable-length Code

設計碼,有兩種策略。

「固定長度編碼」令每個字符的碼一樣長。比如 ASCII 。

A 00001
B 00010
C 00011
D 00100
⋮ ⋮
Z 11010

「可變長度編碼」令各個字符的碼不等長。比如 UTF-8 。

A 000
B 001
C 0100
D 0101
⋮ ⋮
Z 11111

Fixed-length Code :兩個碼千萬不能相同

當兩個碼相同,解碼將產生歧義。

A 00001
B 00010
C 00001

解碼00001...
掃描前五個位元,得到00001,可能是A,也可能是B。

Variable-length Code :一個碼千萬不能是另一個碼的前綴


http://www.csie.ntnu.edu.tw/~u91029/Code.html 3/10
3/10/2018 演算法筆記 - Code

當一個碼是另一個碼的開頭,解碼將產生歧義。

A 011
B 0111
C 00111

解碼011111...
掃描前三個位元,得到011,可能是A。
掃描前四個位元,得到0111,也可能是B。

也許你馬上聯想到 0/1 Knapsack Problem 、 Sardinas-


Patterson Algorithm ,不過這不是本篇主旨。

碼的長度

二元碼的情況下, N 種字符,至少需要 ceil(log₂N) 個位元,才


能讓每個字符擁有獨一無二的碼,讓解碼不生歧義。比如 26 個大寫
英文字母, 26 ≤ 2⁵ ,至少需 5 個位元。

UVa 444 10878 213 740 10851 739 10921 10415


1590

ASCII

American Standard Code for Information Interchange

ASCII Table
NUL 00000000 00100000 @ 01000000 ` 01100000
SOH 00000001 ! 00100001 A 01000001 a 01100001
STX 00000010 " 00100010 B 01000010 b 01100010
ETX 00000011 # 00100011 C 01000011 c 01100011
EOT 00000100 $ 00100100 D 01000100 d 01100100
ENQ 00000101 % 00100101 E 01000101 e 01100101
ACK 00000110 & 00100110 F 01000110 f 01100110
BEL 00000111 ' 00100111 G 01000111 g 01100111
BS 00001000 ( 00101000 H 01001000 h 01101000
HT 00001001 ) 00101001 I 01001001 i 01101001
LF 00001010 * 00101010 J 01001010 j 01101010
VT 00001011 + 00101011 K 01001011 k 01101011
FF 00001100 , 00101100 L 01001100 l 01101100
CR 00001101 - 00101101 M 01001101 m 01101101
SO 00001110 . 00101110 N 01001110 n 01101110
SI 00001111 / 00101111 O 01001111 o 01101111
DLE 00010000 0 00110000 P 01010000 p 01110000
DC1 00010001 1 00110001 Q 01010001 q 01110001
DC2 00010010 2 00110010 R 01010010 r 01110010
DC3 00010011 3 00110011 S 01010011 s 01110011
DC4 00010100 4 00110100 T 01010100 t 01110100
NAK 00010101 5 00110101 U 01010101 u 01110101
SYN 00010110 6 00110110 V 01010110 v 01110110
ETB 00010111 7 00110111 W 01010111 w 01110111
CAN 00011000 8 00111000 X 01011000 x 01111000
EM 00011001 9 00111001 Y 01011001 y 01111001
SUB 00011010 : 00111010 Z 01011010 z 01111010
ESC 00011011 ; 00111011 [ 01011011 { 01111011
FS 00011100 < 00111100 \ 01011100 | 01111100
GS 00011101 = 00111101 ] 01011101 } 01111101
RS 00011110 > 00111110 ^ 01011110 ~ 01111110
US 00011111 ? 00111111 _ 01011111 DEL 01111111
註:有框框的是特殊字元,沒有實際的外型,無法顯示在螢幕上。
註:第二排第一個是空白鍵。

http://www.csie.ntnu.edu.tw/~u91029/Code.html 4/10
3/10/2018 演算法筆記 - Code

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+----------------------------------------------------------------
0 | NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI
16 | DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US
32 | ! " # $ % & ' ( ) * + , - . /
48 | 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
64 | @ A B C D E F G H I J K L M N O
80 | P Q R S T U V W X Y Z [ \ ] ^ _
96 | ` a b c d e f g h i j k l m n o
112| p q r s t u v w x y z { | } ~ DEL

ASCII 一共有 128 種字元,每個字元對應的二元碼,換成數值


剛好就是 0 到 127 的整數。可以想成編號 0 號到編號 127 號。

128 種字元當中,有些字元沒有實際的外型,通常作為特殊用
途。例如編號 0 號的字元,在 C 程式語言當中用來當作字串的結尾
符號。

大部分的字元,在鍵盤上面都有對應的按鍵,例如 qwe123`-=
等等。少部分的字元,則沒有對應的按鍵,必須預先按住 shift 或者
ctrl 之後才能輸入。

ASCII 有英文字母、標點符號、數字、四則運算符號等等,卻沒
有中文字、注音符號、微積分符號、可愛小圖示之類的。這是因為當
初設計 ASCII 的人,從未想過電腦會普及遍佈全世界,所以只設計
了一些簡易的符號。

1 byte

早期的中央處理器,一次可以接受 8 bit = 1 byte ,因此我們就


固定採用 8 bit = 1 byte 來儲存一個 ASCII 字元,每個 ASCII 字元對
應的二元碼長度都是 8 。

程式語言的 char 變數,記憶體大小正好就是 1 byte ,用來儲存


一個 ASCII 字元。

8 bit = 1 byte ,一共有 2⁸ = 256 種數字,照理來說可以對應


256 種字元。不過 ASCII 只有 128 種字元,所以剩下的 128 種,就
有人拿來自由運用。

寫程式處理 ASCII

任何一本程式語言的書籍一定都有介紹,此處就不贅述了。

 
1. void process_ASCII()
2. {
3.     // 在電腦螢幕上顯示字元
4.     cout << "Hello World!";
5.  
6.     // 用while迴圈不斷擷取鍵盤輸入的字元。
7.     // cin.get()會暫停執行程式,
8.     // 直到使用者輸入之後,才回傳字元,繼續執行程式。

http://www.csie.ntnu.edu.tw/~u91029/Code.html 5/10
3/10/2018 演算法筆記 - Code

9.     char c;
10.     while (c = cin.get())
11.     {
12.         // 在電腦螢幕上顯示字元
13.         cout << c;
14.  
15.         // 小寫字母改成大寫字母
16.         if (c >= 'a' && c <= 'z')
17.             cout << c - 'a' + 'A';
18.     }
19. }

Big5

Code Page

ASCII 只有英文字,沒有其他文字。世界各國為了讓電腦處理自
家文字,以及處理原本就有的英文(以便讓電腦正常運作),於是世
界各國皆以 ASCII 作為基礎,各自創造自己的碼以及編碼解碼方
式,稱作「內碼表」。

世界各國也共同商議了內碼表編號。例如編號 950 是繁體中文


Big5 、編號 936 是簡體中文 GB 2312 、編號 932 是日文 Shift_JIS
、編號 819 是西歐文字 ISO 8859-1 、編號 37 是美加文字 EBCDIC
US/Canada 。

同樣的碼,套用不同的內碼表,解碼出來的文字就完全不同!想
要處理其他國家的檔案文件、網站頁面、軟體程式,就必須使用該國
的內碼表!例如瀏覽日本網站就得換日文內碼表,閱讀西歐文件就得
換西歐文字內碼表。

Big5

如果第一個 byte 是正數或零,那麼就確定是 ASCII 的字元。如


果第一個 byte 是負數,就再讀一個 byte ,兩個 byte 作為一個字
元。

寫程式處理 Big5

我尚未找到好讀的資料,請讀者各顯神通。

我有找到一篇講述 Big5 發展歷史的文章:

http://libai.math.ncu.edu.tw/~shann/Chinese/big5.html

Big5 的瑕疵:他國無法執行軟體

直到現今,大家還是習慣使用自家的內碼表編寫程式。

採用 Big5 製作軟體,凡出現繁體中文字,在繁體中文作業系統
會顯示正確文字,在其他語言作業系統則會顯示亂碼。甚至當 Big5

http://www.csie.ntnu.edu.tw/~u91029/Code.html 6/10
3/10/2018 演算法筆記 - Code

與他國內碼表的解碼規則差太多時,解讀長度不一致,讀得太多太
少,將造成程式指令大亂、程式當機。

相對地,世界上所有國家都有這樣的問題。為了解決這個問題,
微軟設計了 AppLocale 軟體,幫忙套用正確的內碼表。日文電腦遊
戲玩家一定聽過 AppLocale 。

Big5 的瑕疵:無法同時顯示多國文字

採用 Big5 製作文件,只能同時看到英文字和繁體中文字,無法
看到其他文字。為了同時看到其他文字,熱心人士推動了「 Unicode
補完計劃」,把簡體中文字、日文字硬是補進 Big5 裡面。靈感來自
於當時正在發展的 Unicode 。

當時因為 Unicode 不流行,許多電腦系統尚不支援 Unicode ,


所以才出此奇招。現在 Unicode 已經普及,根本沒有必要使用
Unicode 補完計劃。

Big5 的瑕疵:許功蓋問題

許、功、蓋這三個字元(不只這三個),第二個 byte 剛好是 \


,剛好是 C 與 C++ 程式語言的「接續上一行」功能,也是眾多程式
語言的跳脫字元的開頭(例如 \n 與 \t )。許功蓋一旦出現在程式碼
當中,常常發生意想不到的結果。

 
1. // 執行成功
2. cout << "OK!";  // 這行將接續上一行,變成註解,不會執行。

Unicode

Unicode

替世界上的每一個文字暨符號,設定獨一無二的編號。簡單來
說,就是把世上所有字符一一編號。

建立 Unicode 是一件浩大的工程,人類從二十幾年前就開始動
工,迄今已經接近完成,也相當普及了。詳細的發展歷程可以參考維
基百科: http://zh.wikipedia.org/wiki/Unicode 。

編號大體上是這樣安排:

前128個編號,與ASCII完全相同。
再來到前65536 = 2byte個編號,是世界各國常見文字。
再來到前4294967296 = 4byte個編號,是世界各國罕見文字。

編號通常使用十六進位表示法,開頭加上 U+ 。

U+0041 <-> A
U+FF21 <-> A
U+FF1F <-> ?

http://www.csie.ntnu.edu.tw/~u91029/Code.html 7/10
3/10/2018 演算法筆記 - Code
U+00E6 <-> æ
U+597D <-> 好
U+3042 <-> あ
U+2260 <-> ≠
U+32A3 <-> ㊣
U+262F <-> ☯
U+1F513 <->
U+2019F <-> 𠆟
U+1F323 <->

字型未包含的字元,則顯示成一個框框,裡面寫出編號。

UTF-8

UTF-8 是將 Unicode 編號變成二元碼的其中一種方式,其他的


轉換方式還有 UTF-16 、 UTF-32 等等。轉換過程請參考:

簡易說明
UTF-8:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.htm

詳細規格
UTF-8:http://tools.ietf.org/html/rfc3629#page-4
UTF-16:http://tools.ietf.org/html/rfc2781#section-2

現實生活中,沒有人使用 Unicode 儲存資料,而是使用 UTF-8


或者 UTF-16 儲存資料。例如網頁、電子郵件習慣使用 UTF-8 ,文
字檔案習慣使用 UTF-16 。至於 UTF-32 非常罕見。

寫程式處理 UTF-8

雖然 UTF-8 已經推行很久了,但是處理 UTF-8 的方法卻發展遲


緩。現時想要處理 UTF-8 ,大致上需要考慮下述五個方面:

一、IDE必須能夠顯示UTF-8格式的程式碼。
二、Compiler必須能夠編譯UTF-8格式的程式碼。
三、C/C++規格書必須定義UTF-8的變數、函式庫,Compiler也必須支援。
四、OS必須能夠把系統資訊以UTF-8的格式呈現,像是目錄名稱、檔案名稱。
五、Console必須能夠顯示UTF-8格式的文字。

http://www.cplusplus.com/reference/codecvt/

Base64

Base64

https://zh.wikipedia.org/wiki/Base64

先前是把字符變成二元碼。此處則是角色互換,把二元碼變成可
見字符。

採用 64 種可見字符:大小寫英文字母 52 個、數字 10 個、加


號 + 、斜線 / 。

http://www.csie.ntnu.edu.tw/~u91029/Code.html 8/10
3/10/2018 演算法筆記 - Code

◀ Index 二元碼每 3 個字符( byte ),替換成 4 個可見字符。 256³ =


64⁴ ,資訊量相等。掃描到最後,如果不足 3 個字符,則額外添上一
Code 個或兩個等號 = ,以便精準記錄二元碼有多少字符。

ASCII Base64 應用廣泛。例如 data URI :一個檔案實施 Base64 編


Big5 碼,添上表頭,作為網址。瀏覽器將自動解碼,得到檔案。
Unicode  

Base64 1. void encode(unsigned char* in, int n,
2.             unsigned char* out, int& m)
3. {
4.     static char encode_table[] =
5.         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef"
6.         "ghijklmnopqrstuvwxyz0123456789+/";
7.  
8.     int i = 0, j = 0;
9.     while (i < n)
10.     {
11.         unsigned int x = 0;
12.         if (i < n) x |= (unsigned int)in[i++] << 16;
13.         if (i < n) x |= (unsigned int)in[i++] << 8;
14.         if (i < n) x |= (unsigned int)in[i++];
15.         out[j++] = encode_table[(x>>18) & 0x3f];
16.         out[j++] = encode_table[(x>>12) & 0x3f];
17.         out[j++] = encode_table[(x>>6)  & 0x3f];
18.         out[j++] = encode_table[ x      & 0x3f];
19.     }
20.     if (n % 3 == 2) out[j++] = '=';
21.     if (n % 3 == 1) out[j++] = '=';
22. //  m = 4 * ((n + 2) / 3);
23.     m = j;
24. }
25.  
26. void decode(unsigned char* in, int n,
27.             unsigned char* out, int& m)
28. {
29.     static unsigned int decode_table[256];
30.     for (int i='A'; i<='Z'; i++) decode_table[i] = i - 
'A';
31.     for (int i='a'; i<='z'; i++) decode_table[i] = i - 
'a' + 26;
32.     for (int i='0'; i<='9'; i++) decode_table[i] = i - 
'0' + 52;
33.     decode_table['+'] = 62;
34.     decode_table['/'] = 63;
35.     decode_table['='] = 0;
36.  
37.     m = n / 4 * 3;
38.     if (in[n-1] == '=') m--;
39.     if (in[n-2] == '=') m--;
40.  
41.     int i = 0, j = 0; 
42.     while (i < n)

http://www.csie.ntnu.edu.tw/~u91029/Code.html 9/10
3/10/2018 演算法筆記 - Code

43.     {
44.         unsigned int x = 0;
45.         x |= decode_table[in[i++]] << 18;
46.         x |= decode_table[in[i++]] << 12;
47.         x |= decode_table[in[i++]] << 6;
48.         x |= decode_table[in[i++]];
49.         if (j < m) out[j++] = (x>>16) & 0xff;
50.         if (j < m) out[j++] = (x>>8)  & 0xff;
51.         if (j < m) out[j++] =  x      & 0xff;
52.     }
53. }

http://www.csie.ntnu.edu.tw/~u91029/Code.html 10/10

You might also like