轉(zhuǎn)自:http://blog.csdn.net/v_JULY_v/archive/2011/01/10/6127953.aspx
譯者:July 二零一一年一月十日
------------------------------------
參考論文:
The Best of the 20th Century: Editors Name Top 10 Algorithms。
By Barry A. Cipra。
博主說明:
1、此20世紀的十大算法,除了快速排序算法,或者快速傅立葉變換,其它算法只要稍作了解即可。
2、此文非最新文章,只是本人對算法比較感興趣,所以也做翻譯,學(xué)習(xí)研究下。
3、本人喜好研究算法,寫了一系列經(jīng)典算法研究的文章。詳情,參考本文文末鏈接。
===============================
一、1946 蒙特卡洛方法
[1946: John von Neumann, Stan Ulam, and Nick Metropolis, all at the Los Alamos Scientific Laboratory, cook up the Metropolis algorithm, also known as the Monte Carlo method.]
1946年,美國拉斯阿莫斯國家實驗室的三位科學(xué)家John von Neumann,Stan Ulam 和 Nick Metropolis
共同發(fā)明,被稱為蒙特卡洛方法。
它的具體定義是:
在廣場上畫一個邊長一米的正方形,在正方形內(nèi)部隨意用粉筆畫一個不規(guī)則的形狀,
現(xiàn)在要計算這個不規(guī)則圖形的面積,怎么計算列?
蒙特卡洛(Monte Carlo)方法告訴我們,均勻的向該正方形內(nèi)撒N(N 是一個很大的自然數(shù))個黃豆,
隨后數(shù)數(shù)有多少個黃豆在這個不規(guī)則幾何形狀內(nèi)部,比如說有M個,
那么,這個奇怪形狀的面積便近似于M/N,N越大,算出來的值便越精確。
在這里我們要假定豆子都在一個平面上,相互之間沒有重疊。(撒黃豆只是一個比喻。)
蒙特卡洛方法可用于近似計算圓周率:
讓計算機每次隨機生成兩個0到1之間的數(shù),看這兩個實數(shù)是否在單位圓內(nèi)。
生成一系列隨機點,統(tǒng)計單位圓內(nèi)的點數(shù)與總點數(shù),內(nèi)接圓面積和正方形面積之比為PI:4,PI為圓周率。
(多謝網(wǎng)友七里河蠢才指出,S內(nèi)接圓:S正=PI:4。具體,請看文下第99條評論。十六日修正),
當隨機點取得越多(但即使取10的9次方個隨機點時,其結(jié)果也僅在前4位與圓周率吻合)時,
其結(jié)果越接近于圓周率。
二、1947 單純形法
[1947: George Dantzig, at the RAND Corporation, creates the simplex method for linear programming.]
1947年,蘭德公司的,Grorge Dantzig,發(fā)明了單純形方法。
單純形法,此后成為了線性規(guī)劃學(xué)科的重要基石。
所謂線性規(guī)劃,簡單的說,就是給定一組線性(所有變量都是一次冪)約束條件
(例如a1*x1+b1*x2+c1*x3>0),求一個給定的目標函數(shù)的極值。
這么說似乎也太太太抽象了,但在現(xiàn)實中能派上用場的例子可不罕見——比如對于一個公司而言,其能夠投
入生產(chǎn)的人力物力有限(“線性約束條件”),而公司的目標是利潤最大化(“目標函數(shù)取最大值”),看
,線性規(guī)劃并不抽象吧!
線性規(guī)劃作為運籌學(xué)(operation research)的一部分,成為管理科學(xué)領(lǐng)域的一種重要工具。
而Dantzig提出的單純形法便是求解類似線性規(guī)劃問題的一個極其有效的方法。
三、1950 Krylov子空間迭代法
[1950: Magnus Hestenes, Eduard Stiefel, and Cornelius Lanczos, all from the Institute for Numerical Analysis at the National Bureau of Standards, initiate the development of Krylov subspace iteration methods.]
1950年:美國國家標準局數(shù)值分析研究所的,馬格努斯Hestenes,愛德華施蒂費爾和
科尼利厄斯的Lanczos,發(fā)明了Krylov子空間迭代法。
Krylov子空間迭代法是用來求解形如Ax=b 的方程,A是一個n*n 的矩陣,當n充分大時,直接計算變得非常
困難,而Krylov方法則巧妙地將其變?yōu)镵xi+1=Kxi+b-Axi的迭代形式來求解。
這里的K(來源于作者俄國人Nikolai Krylov姓氏的首字母)是一個構(gòu)造出來的接近于A的矩陣,
而迭代形式的算法的妙處在于,它將復(fù)雜問題化簡為階段性的易于計算的子步驟。
四、1951 矩陣計算的分解方法
[1951: Alston Householder of Oak Ridge National Laboratory formalizes the decompositional approach to matrix computations.]
1951年,阿爾斯通橡樹嶺國家實驗室的Alston Householder提出,矩陣計算的分解方法。
這個算法證明了任何矩陣都可以分解為三角、對角、正交和其他特殊形式的矩陣,
該算法的意義使得開發(fā)靈活的矩陣計算軟件包成為可能。
五、1957 優(yōu)化的Fortran編譯器
[1957: John Backus leads a team at IBM in developing the Fortran optimizing compiler.]
1957年:約翰巴庫斯領(lǐng)導(dǎo)開發(fā)的IBM的團隊,創(chuàng)造了Fortran優(yōu)化編譯器。
Fortran,亦譯為福傳,是由Formula Translation兩個字所組合而成,意思是“公式翻譯”。
它是世界上第一個被正式采用并流傳至今的高級編程語言。
這個語言現(xiàn)在,已經(jīng)發(fā)展到了,F(xiàn)ortran 2008,并為人們所熟知。
六、1959-61 計算矩陣特征值的QR算法
[1959–61: J.G.F. Francis of Ferranti Ltd, London, finds a stable method for computing
eigenvalues, known as the QR algorithm.]
1959-61:倫敦費倫蒂有限公司的J.G.F. Francis,找到了一種穩(wěn)定的特征值的計算方法,
這就是著名的QR算法。
這也是一個和線性代數(shù)有關(guān)的算法,學(xué)過線性代數(shù)的應(yīng)該記得“矩陣的特征值”,計算特征值是矩陣計算的
最核心內(nèi)容之一,傳統(tǒng)的求解方案涉及到高次方程求根,當問題規(guī)模大的時候十分困難。
QR算法把矩陣分解成一個正交矩陣(希望讀此文的你,知道什么是正交矩陣。:D。)與一個上三角矩陣的積,
和前面提到的Krylov 方法類似,這又是一個迭代算法,它把復(fù)雜的高次方程求根問題化簡為階段性的易于
計算的子步驟,使得用計算機求解大規(guī)模矩陣特征值成為可能。
這個算法的作者是來自英國倫敦的J.G.F. Francis。
七、1962 快速排序算法
[1962: Tony Hoare of Elliott Brothers, Ltd., London, presents Quicksort.]
1962年:倫敦的,托尼埃利奧特兄弟有限公司,霍爾提出了快速排序。
哈哈,恭喜你,終于看到了可能是你第一個比較熟悉的算法~。
快速排序算法作為排序算法中的經(jīng)典算法,它被應(yīng)用的影子隨處可見。
快速排序算法最早由Tony Hoare爵士設(shè)計,它的基本思想是將待排序列分為兩半,
左邊的一半總是“小的”,右邊的一半總是“大的”,這一過程不斷遞歸持續(xù)下去,直到整個序列有序。
說起這位Tony Hoare爵士,快速排序算法其實只是他不經(jīng)意間的小小發(fā)現(xiàn)而已,他對于計算機貢獻主要包括
形式化方法理論,以及ALGOL60 編程語言的發(fā)明等,他也因這些成就獲得1980 年圖靈獎。
========
關(guān)于快速排序算法的具體認識與應(yīng)用,可參考我寫的一篇文章,
精通八大排序算法系列、一、快速排序算法:
http://blog.csdn.net/v_JULY_v/archive/2011/01/04/6116297.aspx
------------------------------------------------------------
快速排序的平均時間復(fù)雜度僅僅為O(Nlog(N)),相比于普通選擇排序和冒泡排序等而言,
實在是歷史性的創(chuàng)舉。
八、1965 快速傅立葉變換
[1965: James Cooley of the IBM T.J. Watson Research Center and John Tukey of Princeton
University and AT&T Bell Laboratories unveil the fast Fourier transform.]
1965年:IBM 華生研究院的James Cooley,和普林斯頓大學(xué)的John Tukey,
AT&T貝爾實驗室共同推出了快速傅立葉變換。
快速傅立葉算法是離散傅立葉算法(這可是數(shù)字信號處理的基石)的一種快速算法,其時間復(fù)雜度僅為O
(Nlog(N));比時間效率更為重要的是,快速傅立葉算法非常容易用硬件實現(xiàn),因此它在電子技術(shù)領(lǐng)域得到
極其廣泛的應(yīng)用。
日后,我會在我的經(jīng)典算法研究系列,著重闡述此算法。
九、1977 整數(shù)關(guān)系探測算法
[1977: Helaman Ferguson and Rodney Forcade of Brigham Young University advance an integer
relation detection algorithm.]
1977年:Helaman Ferguson和 伯明翰大學(xué)的Rodney Forcade,提出了Forcade檢測算法的整數(shù)關(guān)系。
整數(shù)關(guān)系探測是個古老的問題,其歷史甚至可以追溯到歐幾里德的時代。具體的說:
給定—組實數(shù)X1,X2,...,Xn,是否存在不全為零的整數(shù)a1,a2,...an,使得:a1 x 1 +a2 x2 + . . . + an x
n =0?
這一年BrighamYoung大學(xué)的Helaman Ferguson 和Rodney Forcade解決了這一問題。
該算法應(yīng)用于“簡化量子場論中的Feynman圖的計算”。ok,它并不要你懂,了解即可。:D。
十、1987 快速多極算法
[1987: Leslie Greengard and Vladimir Rokhlin of Yale University invent the fast multipole algorithm.]
1987年:萊斯利的Greengard,和耶魯大學(xué)的Rokhlin發(fā)明了快速多極算法。
此快速多極算法用來計算“經(jīng)由引力或靜電力相互作用的N 個粒子運動的精確計算——例如銀河系中的星體,
或者蛋白質(zhì)中的原子間的相互作用”。ok,了解即可。
完。
1.1. 字符編碼基本概念
現(xiàn)代編碼模型的編碼思想包括:有什么字符、他們的編號、這些編號如何編碼成一系列的碼元,以及最后這些單元如何編碼為8位字節(jié)流。對應(yīng)于如下術(shù)語:
1)字符表 一個系統(tǒng)所支持的所有抽象字符的總合。
2)編碼字符集 定義了如何使用稱為碼點的非負整數(shù)集表示一個字符集,一個整數(shù)對應(yīng)一個抽象的字符。
3)字符編碼形式 定義將編碼字符集的整數(shù)代碼轉(zhuǎn)換成有限大小整數(shù)代碼值以利于使用固定位的二進制表示數(shù)字的形式的系統(tǒng)存儲。例如使用8位或16位單元存儲數(shù)字信息。字符編碼形式定義了如何用單個或多個碼值表示碼點的方法。例如utf8是一種編碼形式,utf-16則是另一種編碼形式。
4)字符編碼機制 定義固定大小的整數(shù)代碼如何映射到基于8位字節(jié)數(shù)據(jù)的文件系統(tǒng)存儲或者基于8位字節(jié)網(wǎng)絡(luò)傳輸。在多數(shù)使用unicode的場合,一個簡單的字符編碼機制用來指定每個整數(shù)的字節(jié)順序是大字節(jié)在先順序還是小字節(jié)在先順序。還有其他復(fù)雜的字符編碼機制。
1.2. 字符編碼發(fā)展
字符編碼的歷史大致可以分為三個階段:
1)ascii階段
剛開始只支持英語,其他語言不能夠在計算機上存儲和顯示。使用一個字節(jié)來存一個字符。
2)ansi編碼(本地化)
為使計算機支持更過語言,通過使用0x80~0xFF范圍的2個字節(jié)來表示1個字符。不同的國家和地區(qū)制定了不同的標準,由此產(chǎn)生了各種各樣的編碼標準,如gb2312、big5、jis等。這些使用兩個字節(jié)來表示一個字符的各種漢字延伸編碼方式,稱為ansi編碼。
3)Unicode階段(國際化)
為了使國際間信息交流更加方便,國際組織制定了unicode字符集,為各種語言中的每一個字符設(shè)定了統(tǒng)一并且唯一的數(shù)字編號,以滿足跨語言、跨平臺進行文本轉(zhuǎn)換、處理的要求。Unicode僅僅制定了字符集,用來給unicode編碼的標準有utf-7、utf-8、utf-16、unicodeLittle、unicodebig等。
1.3. 主要編碼
1.3.1. Ascii
ascii全稱美國信息互換標準代碼(american standard code for information interchage)。主要用于顯示現(xiàn)代英語和其他西歐語言,是現(xiàn)今最通用的單字節(jié)編碼,等于國標標準iso 646。包含控制字符32個和可打印字符94個。編碼單元為8位,取值單位從0x00-0x7F,最高為0。
1.3.2. 漢字編碼
漢字編碼均采用雙字節(jié)編碼,編碼單元為8位。
1.3.2.1. Gb2312-80
Gb2312是對ascii的中文擴展,是中華人民共和國國家標準漢字信息交換用編碼。收錄簡化漢字及一般符號、序號、數(shù)字、拉丁字母、日文假名、希臘字母、俄文字母等共7445個圖形字符。其中漢字以外的圖形字符682個,漢字6763個。為了與系統(tǒng)中基本的ascii字符集區(qū)分開,所有漢字編碼的每個字節(jié)的第一位都是1。
Gb2312的漢字編碼規(guī)則是:第一個字節(jié)的值在0xB0到0xF7之間,第二個字節(jié)的值在0xA0到0xFE之間。但是gb2312收錄的漢字太少,以致很多常用字都沒有收錄,如朱鎔基的“鎔”字。為了解決這些問題,以及配合unicode的實施,全國信息技術(shù)化技術(shù)委員會制定了gb13000,即gbk。Gbk向下與gb2312完全兼容,向上支持iso-10646國際標準。
1.3.2.2. Gbk
Gbk包含了20902個漢字,其編碼范圍是0x8140-0xfefe,剔除高位0x80的字位。收錄漢字包括:
1)gb2312中全部漢字、非漢字字符
2)big5中的全部漢字
3)與iso-10646相應(yīng)的國家標準gb13000中的其他cjk漢字
4)其他漢字、部首、符號等。
其編碼區(qū)分成三個部分:
1)漢字區(qū) 包括
a) Gbk/2:0xb0a1-f7fe,收錄gb2312漢字6763個,按原序排列,0xd7fa-0xd7fe為空洞。
b) Gbk/3:0x8140-a0fe,收錄cjk漢字6080個,0x817f-0xa07f為空洞
c) Gbk/4:0xaa40-fea0,收錄cjk漢字和增補漢字8160個,0xaa7f-0xfe7f為空洞
2)圖形符號區(qū) 包括
a) Gbk/1:0xa1a1-0xa9fe,除gb2312的符號外,還增補了其他符號
b) Gbk/5:0xa840-0xa9a0,擴充非漢字區(qū)
3)用戶自定義區(qū)
1.3.2.3. Gb18030-2000
GB18030-2000是2000年推出的國家標準。它可以視為GBK的升級,因為它主要增加了Unicode 3.0中新增的一些字符。除了GBK的字符,它能表示UNICODE中所有的字符。中國出售的所有軟件產(chǎn)品都要求支持GB18030。
GB18030與GBK完全兼容,除了歐元符號 :在GB18030中是A2E3,在GBK中是0x80。
采用單字節(jié)、雙字節(jié)和四字節(jié)三種方式對字符編碼,編碼范圍如下:
1)單字節(jié): 0x00-0x7f
2)雙字節(jié): 0x81-0xfe + 0x40-0x7e, 0x80-0xfe
3)四個字節(jié): 0x81-0xfe + 0x30-0x39 + 0x81-0xfe + 0x30-0x39
1.3.2.4. Big5
Big5又稱五大碼,是使用繁體中文字社群眾最常用的計算機漢字字符集標準,由臺灣5家大公司的方案拼湊而成。
Big5共收錄13053個中文字,其中有兩個字重碼,為兀(0xa461及0xc94a)和嗀(0xdcd1-0xddfc)。
Big5使用雙八碼存儲方式,以兩個字節(jié)來安放一個字。高位字節(jié)使用了0xa1-0xf9,低位字節(jié)使用了0x40-0x7e及0xa1-0xfe。
原始的BIG-5 只包括一些常用的字,甚至不包括日文的假名等,在實際的應(yīng)用中很多系統(tǒng)給BIG-5加上了自己的擴展。例如,MS code page 950,歐元符號A3E1。
1.3.3. Unicode
Unicode是一個大一統(tǒng)的方案,它是http://www.unicode.org制定的編碼機制,要將全世界常用文字都函括進去。它為每種語言中的每個字符設(shè)定了統(tǒng)一并且唯一的二進制編碼,以滿足跨語言、跨平臺進行文本轉(zhuǎn)換、處理的要求。1990年開始研發(fā),1994年正式公布。隨著計算機工作能力的增強,Unicode也在面世以來的十多年里得到普及。
但自從unicode2.0開始,unicode采用了與ISO 10646-1相同的字庫和字碼,ISO也承諾ISO10646將不會給超出0x10FFFF的UCS-4編碼賦值,使得兩者保持一致。
Unicode的編碼方式與ISO 10646的通用字符集(Universal Character Set,UCS)概念相對應(yīng),目前的用于實用的Unicode版本對應(yīng)于UCS-2,使用16位的編碼空間。也就是每個字符占用2個字節(jié),基本滿足各種語言的使用。實際上目前版本的Unicode尚未填充滿這16位編碼,保留了大量空間作為特殊使用或?qū)頂U展。
Unicode和ucs只是分配整數(shù)給字符的編碼表,即只是一個編碼字符集合。現(xiàn)在存在好幾種將一個字符表示為若干個字節(jié)的方法。最顯而易見的方法是將unicode文本存儲為2個或4個字節(jié)序列的串。這兩種方法的正式名稱為ucs-2和ucs-4。
但是在unix下使用ucs-2或ucs-4會導(dǎo)致非常嚴重的問題。用這些編碼的字符串會包含一些特殊的字符,比如’\0’或’/’,他們在文件名和其他c庫函數(shù)里都有特別的含義。另外,大多數(shù)使用ascii文件的unix下的工具,如果不進行重大修改是無法讀取16位的字符的。基于這些原因,在文件名,文本文件、環(huán)境變量等地方,ucs-2不適合作為unicode的外部編碼。
因此需要一種新的編碼方案稱為utf(unicode transfer format)運用在unix/linux環(huán)境中。Utf-7,utf-8,utf-16都是廣泛接受的方案。Rfc2781和rfc3629定義了utf-8和utf-16的編碼方式。
1.3.3.1. Utf-8
Utf-8就是以8位為單元對ucs-2進行編碼。從ucs-2到utf-8的編碼方式如下:
U-00000000 - U-0000007F: 0xxxxxxx
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF:
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF:
111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF:
1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
Utf-8有如下特性:
1)UCS 字符 U+0000 到 U+007F (ASCII) 被編碼為字節(jié) 0x00 到 0x7F (ASCII 兼容)。這意味著只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 兩種編碼方式下是一樣的。
2)所有 >U+007F 的 UCS 字符被編碼為一個多個字節(jié)的串, 每個字節(jié)都有標記位集。因此, ASCII 字節(jié) (0x00-0x7F) 不可能作為任何其他字符的一部分.
3)表示非 ASCII 字符的多字節(jié)串的第一個字節(jié)總是在 0xC0 到 0xFD 的范圍里, 并指出這個字符包含多少個字節(jié)。多字節(jié)串的其余字節(jié)都在 0x80 到 0xBF 范圍里。這使得重新同步非常容易,并使編碼無國界, 且很少受丟失字節(jié)的影響.
4)可以編入所有可能UCS 代碼
5)UTF-8 編碼字符理論上可以最多到 6 個字節(jié)長, 然而 16 位 BMP 字符最多只用到 3 字節(jié)長。
6)Bigendian UCS-4 字節(jié)串的排列順序是預(yù)定的.
7)字節(jié) 0xFE 和 0xFF 在 UTF-8 編碼中從未用到.
1.3.3.2. Uft-16
Utf-16是以16位為編碼單元的,在范圍u+0000到u+ffff間的碼點使用一個單一的16位編碼單元表示;而在范圍u+10000到u+10FFFF間的碼點則使用一對16位編碼單元表示,稱作代理對。Utf-16優(yōu)化了基本多語言平面(bmp)中字符的表示,即位于u+0000到u+FFFF范圍內(nèi)的字符。該范圍包含了目前世界上所使用的書寫系統(tǒng)中的絕大數(shù)字符,每個字符只需要一個16位的編碼單元。對于基本多語言平面,utf-16可作為固定寬度的編碼格式來有效使用。但是對于增補字符,utf-16需要兩個16位的編碼單元,意味著正式的utf-16是一個變寬的編碼格式。Utf-16是早期unicode遺留下的歷史產(chǎn)物,原本被設(shè)計成具有固定寬度的16位編碼格式,為支持超過u+ffff的增補字符,設(shè)立了代理機制。
1.3.3.3. Utf-32
Utf-32是一種最簡單的unicode編碼格式。每個unicode碼點直接被表示為一個32位的編碼單元。Utf-32是一種固定寬度的字符編碼格式。每個utf-32編碼單元的值與unicode碼點的值完全相同。
1.4. Tips
1.4.1. 編碼字節(jié)序
Big endian和Little endian是CPU處理多字節(jié)數(shù)的不同方式。例如“漢”字的Unicode編碼是6C49。那么寫到文件里時,究竟是將6C寫在前面,還是將49寫在前面?如果將6C寫在前面,就是big endian。如果將49寫在前面,就是little endian。
“endian”這個詞出自《格列佛游記》。小人國的內(nèi)戰(zhàn)就源于吃雞蛋時是究竟從大頭(Big-Endian)敲開還是從小頭(Little-Endian)敲開,由此曾發(fā)生過六次叛亂,一個皇帝送了命,另一個丟了王位。
我們一般將endian翻譯成“字節(jié)序”,將big endian和little endian稱作“大尾”和“小尾”。
對于任何字符編碼,編碼單元的順序是由編碼方案指定的,與endian無關(guān)。例如gbk的編碼單元是字節(jié),用兩個字節(jié)表示一個漢字,這兩個字節(jié)的順序是固定的,不受cpu字節(jié)序的影響。Utf-16的編碼單元是word,word之間的順序是編碼方案指定的,word內(nèi)部的字節(jié)排列才會收到endian的影響。Utf-8也是以字節(jié)為編碼單元,沒有字節(jié)序的問題。
一個使用utf-16編碼的文件如何進行解釋呢?以一個例子來說明,打開記事本,寫上一段文字,然后另存,在保存的對話框中可以看到有四種編碼方式可以選擇,分別是:ansi,unicode,unicode big endian和utf-8。
Ansi是默認編碼方式,也是系統(tǒng)的默認編碼方式,由缺省代碼頁決定。
Utf-8不用解釋了。
Unicode和unicode big endian都是utf-16編碼的兩種,區(qū)別在于前者采用little endian,后者采用big endian。還有一種方式采用bom標記字節(jié)序列,bom即“byte order mark”,是一個有點小聰明的想法。在ucs編碼中有一個叫做“zero width no-break space”的字符,他的編碼是feff。而feff在ucs中是不存在的字符,所以不應(yīng)該出現(xiàn)在實際傳輸中。Ucs規(guī)范建議我們在傳輸字節(jié)流前,西安傳輸字符“zero width no-break space”。這樣如果接受者收到fef,就表明這個字節(jié)流是big-endian;如果fffe,就表明這個字節(jié)流是little-endian的。
“ABC”這三個字符用各種方式編碼后的結(jié)果如下:
utf-16be 00 41 00 42 00 43
utf-16le 41 00 42 00 43 00
utf-16(bom be) fe ff 00 41 00 42 00 43
utf-16(bom le) ff fe 41 00 42 00 43 00
utf-16(不帶bom) 00 41 00 42 00 43
1.4.2. Windows代碼頁
Unicode推出后,microsoft將windows的內(nèi)核都改成支持unicode字符集。但是由于現(xiàn)有的大量程序和文檔都采用了某種特定語言的編碼,例如gbk,windows不可能不支持現(xiàn)有的編碼而全部改用unicode。Windows使用代碼頁來適應(yīng)各個國家和地區(qū)。Gbk對應(yīng)的code page是cp936,gb18030的code page為cp54936。但是由于gb18030有一部分四字節(jié)編碼,而windows的代碼頁只支持單字節(jié)和雙字節(jié)編碼,故cp54936是無法真正使用的。
Windows可以同時支持多個代碼頁,只要文件能夠說明自己使用什么編碼,用戶又安裝了對應(yīng)的代碼頁,windows就能正確顯示,例如在html文件中指定charset。
Windows中有缺省代碼頁的概念,可以通過控制面板的區(qū)域選項設(shè)置,其作用是缺省用什么編碼來解釋字符。Windows的記事本的存儲格式有一項是ansi,其實就是按照缺省代碼頁的編碼方式保存。
2. 網(wǎng)頁編碼方式
2.1. url編碼基礎(chǔ)知識
一個http請求需要經(jīng)過如下幾個環(huán)節(jié):
1)瀏覽器把url以及提交的內(nèi)容經(jīng)過編碼后發(fā)送給服務(wù)器
2)服務(wù)器處理完畢后將結(jié)果編碼返回給瀏覽器
3)瀏覽器按照指定的編碼顯示網(wǎng)頁
一個完整的url由如下方式組成:
域名:端口/contextPath/servletPath/pathInfo?queryString
其中pathInfo和queryString是需要編碼的部分。
Rfc1738中定義了url的語法語義,限制了url中可以出現(xiàn)的字符。對于不可在url中出現(xiàn)的字符需要按照一定的方式進行編碼,叫做url encode。需要進行encode的符號包括如下:
1)ascii中的控制字符,原因很簡單,因為他們是不可見的,范圍為00-1F和7F;
2)非ascii字符,比如中文字符等,這是因為url中沒有安全的辦法指定字符集(rfc2396);
3)保留字符,url語法中用到的字符,$&+,/:;=?@
4)不安全字符:空格#%<>{}I\~^[]`,出于各種原因;
url encode采用%XX方式,XX為字符的十六進制編碼。
但是在實際應(yīng)用中,瀏覽器是否進行url encode,采用何種字符集進行url encode,與瀏覽器和服務(wù)器的設(shè)置都有關(guān)系,分析如下,以下分析均在windows中文環(huán)境中:
1)對用戶在地址欄中直接輸入的url,編碼方式與瀏覽器的設(shè)置有關(guān)
瀏覽器(模式) PathInfo QueryString
Ie(utf-8模式,默認) Utf8編碼,無url encode Gbk編碼,無urlencode
Ie(ansi模式) Gbk編碼,無urlencode Gbk編碼,無urlencode
Firefox(utf8模式) Utf8編碼,urlencode Utf8編碼,urlencode
Firefox(ansi模式,默認) Gbk編碼,urlencode Gbk編碼,urlencode
Opera(utf8模式,默認) Utf8編碼,urlencode Utf8編碼,urlencode
2)對網(wǎng)頁中的鏈接,與該網(wǎng)頁本身的編碼方式有關(guān)。
在不改變?yōu)g覽器默認選項的情況下,各個瀏覽器的編碼方式如下
瀏覽器(網(wǎng)頁編碼方式) PathInfo QueryString
Ie(utf8網(wǎng)頁) UTF-8編碼、urlencode UTF-8編碼、無urlencode
Ie(gbk網(wǎng)頁) UTF-8編碼、urlencode GBK編碼、無urlencode
Firefox(utf8網(wǎng)頁) UTF-8編碼、urlencode UTF-8編碼、urlencode
Firefox(gbk網(wǎng)頁) GBK編碼、urlencode GBK編碼、urlencode
3)對用戶提交的數(shù)據(jù),不論是get方式還是post方式,其編碼方式由網(wǎng)頁中的編碼方式和相關(guān)調(diào)用有關(guān)。
頁面的編碼方式由http頭指定或網(wǎng)頁的meta標記指定。http頭中含有content-type參數(shù),其中指定了charset:
Content-type:text/html;charset=gb2312
Meta標記的方式如下:
<meta http-eq后臺v=”content-type” content=”text/html;charset=gb2312”/>
不同的瀏覽器處理方式也會不同,ie可能通過文件內(nèi)容識別,firefox偏向meta標簽識別。
對于傳統(tǒng)的表單提交,其編碼方式是由頁面的編碼方式?jīng)Q定的;而ajax提交的數(shù)據(jù)則與其調(diào)用方式有關(guān),如果采用了escape類似的編碼函數(shù),則編碼成utf-8進行發(fā)送。
2.2. 服務(wù)器對編碼的處理
假設(shè)后端均采用gbk存儲數(shù)據(jù),那么對提交的數(shù)據(jù)需進行編碼識別并進行相應(yīng)的編碼轉(zhuǎn)換,主要針對兩種情況:
1)url路徑部分:
由于需要支持中文,這部分是不可控的,取決于操作系統(tǒng)和瀏覽器,因此需要進行判別到底是什么編碼,然后再進行編碼轉(zhuǎn)換。
2)提交數(shù)據(jù)部分:
提交的數(shù)據(jù)也既有可能是utf-8編碼也有可能是gbk編碼。一種辦法是判斷是否是utf-8編碼,但這種判斷存在一定的失敗率;還有一種辦法從頁面上控制,提交時強制增加一個字段表示這是否是utf-8編碼,apache無需考慮其他的,只根據(jù)該字段判斷是否需要做utf-8到gbk的編碼轉(zhuǎn)化,這個辦法是不會出現(xiàn)誤傷的。
判斷是否是utf-8編碼有一定的失敗率,有如下幾種情況:
1)判斷錯誤。
主要發(fā)生在utf8與gbk編碼重疊的部分,需根據(jù)實際應(yīng)用進行處理。
2)判斷成功,但是轉(zhuǎn)換失敗。
沒有對應(yīng)的gbk編碼會導(dǎo)致轉(zhuǎn)換失敗,例如韓文字符,在這種情況下可轉(zhuǎn)成實體。
3)判斷是否是utf-8時判斷錯誤,并且轉(zhuǎn)換失敗,這種情況下不會出現(xiàn)問題。
編碼的識別和轉(zhuǎn)換完成后,后端的模塊也需要根據(jù)實際應(yīng)用進行各種各樣的處理,如:控制字符過濾、繁簡轉(zhuǎn)換、全半角轉(zhuǎn)換、半個漢字處理、字符集過濾等等。
當獲取到數(shù)據(jù)組裝將要返回的頁面時,對出現(xiàn)在頁面上的信息需進行一定的轉(zhuǎn)義,比如對于一些由html標簽組成的文本,如果不進行特殊處理,那么瀏覽器會當做標簽來進行解析,從而引起頁面展現(xiàn)錯誤。
3. 編碼問題分類
3.1. gbk字符集中的特殊字符
1) 0x80歐元符號
【分析】
數(shù)據(jù)庫支持有問題,會自動截斷;gbk對其編碼與其他編碼方式不兼容。非必要條件下建議過濾掉。
【Bad case】
我們在進行編碼設(shè)計時將包含歐元符號的信息插入數(shù)據(jù)庫,因為數(shù)據(jù)庫將歐元符號以后的部分截斷,記錄的信息的長度與實際長度不一致從而引起下游模塊的邏輯錯誤。
2) 0x00-0x31控制字符部分
【分析】
控制字符無法打印。
【bad case】
當用戶構(gòu)造了由控制字符組成的信息,這部分信息顯示在頁面上,會使頁面上本應(yīng)出現(xiàn)文字的地方出現(xiàn)空白,并且如果這部分文字存在鏈接會使得鏈接失效從而導(dǎo)致不可點擊。
3) 0x7F 空白區(qū)位
【分析】如果輸入會是不可見字符,建議過濾掉。
4) 0x5C反斜杠
【分析】
它的特殊性在于兩個原因:1、它作為轉(zhuǎn)義符標示的特殊用法;2、它編碼區(qū)間
落在GBK字符集的后半個漢字允許的編碼區(qū)間內(nèi)。由于這兩個原因,再結(jié)合GBK字符集本身存在雪崩問題的隱患,當末尾存在半個漢字,和0x5C字符結(jié)合就可能導(dǎo)致轉(zhuǎn)義符號無效,裸露出后續(xù)的’等,導(dǎo)致轉(zhuǎn)義符號實效,帶來一些安全問題和js失效等問題。或者和半個漢字結(jié)合導(dǎo)致mysql轉(zhuǎn)義處理失效。
5) 空洞區(qū):0xd7fa-0xd7fe,0x817f-0xa07f,0xaa7f-0xfe7f
【分析】
GBK前半個漢字的范圍在0x81-0xfe之間,不含0xff,如果用戶構(gòu)造了0xff這樣的半個漢字上來,由于部分瀏覽器支持的問題,例如ie,就會把0xFF字符當作GBK的前半個漢字和后續(xù)字符結(jié)合。
除此之外,碼區(qū)的中間也有一些空洞
【bad case】
如果黑客構(gòu)造0xff 0x5c 0x27這樣的字符串(0x5C是\,作為轉(zhuǎn)義標示符號,0x27是單引號’),那么0xff和0x5c結(jié)合成為一個不可見字符,導(dǎo)致原本\’的轉(zhuǎn)義失效,’暴露出來造成安全漏洞。
6) Sql語句中的特殊字符:’、\
【分析】
mysql對于特殊字符如"\"等需要處理后才能存儲,采用的函數(shù)是mysql_real_escape(),將其中的特殊字符轉(zhuǎn)義成/*,對這個函數(shù)的要求是頁面必須存在一個可用的mysql連接。
7) 字符外形和全角英文字符完全一致的字符:0xA6A2與0xA7A2,0xA976與0xA3A8
8) 全角空格:0xA1A1
9) 擴展?jié)h字區(qū)域:主要是非漢字區(qū)域和特殊字符
10)Cp936和其它碼表定義不一致的地方
11)GBK編碼中和其他編碼方式有沖突或者有處理方式不一樣的個別字符
12)可以構(gòu)成強制轉(zhuǎn)義字符的字符:
13)字符串結(jié)束符:\0
14)可能被作為數(shù)據(jù)結(jié)構(gòu)分隔符的字符
15)邊界字符:丂 (0x8140),亐 (0x8180),儈 (0x837E),凗 (0x83FE),狛 (0xA0FE),癄 (0XAFA0), (0xFE7E),鰲 (0xF7A1),(0XFEA0),齄 (0XF7FE)。
16)Trailing byte 在 low-ansi 范圍中 (4 個例子)
腀 (0xC440 / 0x8140) 儬 (0x83A0 / 0x512c)
爘 (0xA07C / 0x7218) 爢 (0xA086 / 0x7222)
17)Leading 和 trailing byte 大小寫是相同的表示 (3 個例子)
'C' / 'c' 丆 (0x8143) / 乧 (0x8163)
'M' / 'm' 鱉 (0xF74D) / 鱩 (0xF76D)
'S' / 's' S (0XA053) / s (0XA073)
18)Trailing byte 和 Leading byte 范圍相同 (4 個例子)
亖 (0x8181) 漢 (0xBABA)
牋 (0xA0A0) 鼢 (0xF7F7)
19)leading 或 trailing byte 是 0xAA, 0xAE or 0xBF, 容易變成亂碼(3 個例子) 煪 (0x9FAA) 伄 (0x81AE) 骺 (0XF7BF)
20)以下特殊字符的unicode 和漢字的 byte是一樣的,在解碼的時候容易出錯,應(yīng)該有20個左右,只列舉12個 á (U+00EA) (0xA8A2) à (U+00E0) (0xA8A4) é (U+00E9) (0xA8A6) è (U+00E8) (0xA8A8) ì (U+00EC) (0xA8AC) í (U+00ED) (0xA8AA) ó (U+00F3) (0xA8AE) ò (U+00F2) (0xA8B0) ú (U+00FA) (0xA8B2) ù (U+00F9) (0xA8B4) ü (U+00FC) (0xA8B9) ê (U+00EA) (0xA8BA)
3.2. utf8與gbk編碼沖突部分
utf8與gbk編碼沖突部分,會導(dǎo)致編碼識別失敗,從而在展現(xiàn)頁面時出現(xiàn)問題。utf8與gbk有127個重疊的編碼。
【bad case 1】
“瓔玥“這個用戶名,此用戶的正確的GBK是E8ACAB68,最后一個字節(jié)就是ascii的‘h‘。例如當用戶在ff訪問http://www.test.com/瓔玥 時,E8ACAB 是utf8編碼的”謫”,68是‘h’,所以看到的就是訪問用戶”謫h”。在ie下是正常的utf8編碼字符,所以無問題。
【bad case 2】
編碼0xc2b7,在gbk中表示“路”這個字,在uft8中表示為“•”這個字符。如果用戶的url中含有maria•sharapova,當以utf8編碼發(fā)送時,被優(yōu)先判斷為gbk編碼,則服務(wù)器識別為maria路sharapova。
重疊部分的編碼既需要在不同的瀏覽器中測試,也需要在url路徑和提交的數(shù)據(jù)中測試。
3.3. Gbk中不存在的編碼
1)韓語字符
2)其他少數(shù)語種
3)GB18030相對GBK增加的字符
3.4. 相鄰字節(jié)組合部分
1) 內(nèi)容過濾中相鄰字節(jié)組合:哈林
【bad case】
漢字“哈林”相連的2個字節(jié)被識別為“妓”,正好是個過濾詞,導(dǎo)致正常信息被過濾。
2) 頁面展現(xiàn)中相鄰字節(jié)組合:牛肩豬肉
頁面上如果存在“牛肩豬肉”的文字則出亂碼的現(xiàn)象,原因在于牛的第二個字節(jié)和肩的第一個字節(jié)正好構(gòu)成了全角的‘>’字符,后臺對這類字符進行了轉(zhuǎn)義。
3) Sql語句中相鄰字節(jié)組合成\:
當用戶輸入字符ó' 等一些字符時,出現(xiàn)了組裝SQL語句時錯誤。這類字符的特點是: 一個大于ascii128編碼的字符加上一個',在GBK編碼過程中會被解析成為\漢',然后造成SQL語句的錯誤。ó字符為ASCII碼的243,可以用小鍵盤敲入。此時,經(jīng)過mysql的轉(zhuǎn)碼,被轉(zhuǎn)義成為ó\', 而漢字編碼會將ó\形成一個漢字笈',這時就會出現(xiàn)mysql錯誤的問題。
作者:qabloger