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