UCS只是規(guī)定如何編碼,并沒有規(guī)定如何傳輸、保存這個編碼。
例如“漢”字的UCS編碼是6C49,我可以用4個ASCII數(shù)字來傳輸、保存這個編碼;也可以用UTF-8編碼:3個連續(xù)的字節(jié)E6 B1 89來表示它。關(guān)鍵在于通信雙方都要認(rèn)可。
UTF-8、UTF-7、UTF-16都是被廣泛接受的方案。UTF-8的一個特別的好處是它與ISO-8859-1完全兼容。
UTF是“UCS Transformation Format”的縮寫。UCS可以看作是"Unicode Character Set"的縮寫。
UTF-8就是以8位為單元對UCS進行編碼。
---------------
內(nèi)碼和"code page"
---------------
目前Windows的內(nèi)核已經(jīng)支持Unicode字符集,這樣在內(nèi)核上可以支持全世界所有的語言文字。
但是由于現(xiàn)有的大量程序和文檔都采用了某種特定語言的編碼,例如GBK,Windows不可能不支持現(xiàn)有的編碼,而全部改用Unicode。Windows使用代碼頁("code page")來適應(yīng)各個國家和地區(qū)。"code page"可以被理解為前面提到的內(nèi)碼。GBK對應(yīng)的"code page"是CP936。
微軟也為GB18030定義了"code page":CP54936。但是由于GB18030有一部分4字節(jié)編碼,而Windows的代碼頁只支持單字節(jié)和雙字節(jié)編碼,所以這個"code page"是無法真正使用的。
----------------
UTF的字節(jié)序和BOM
----------------
UTF-8以字節(jié)為編碼單元,沒有字節(jié)序的問題。
UTF-16以兩個字節(jié)為編碼單元,在解釋一個UTF-16文本前,首先要弄清楚每個編碼單元的字節(jié)序。例如“奎”的Unicode編碼是594E,“乙”的Unicode編碼是4E59。如果我們收到UTF-16字節(jié)流“594E”,那么這是“奎”還是“乙”?
Unicode規(guī)范中推薦的標(biāo)記字節(jié)順序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一個有點小聰明的想法:在UCS編碼中有一個叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的編碼是FEFF。而FFFE在UCS中是不存在的字符,所以不應(yīng)該出現(xiàn)在實際傳輸中。UCS規(guī)范建議我們在傳輸字節(jié)流前,先傳輸字符"ZERO WIDTH NO-BREAK SPACE"。這樣如果接收者收到FEFF,就表明這個字節(jié)流是Big-Endian的;如果收到FFFE,就表明這個字節(jié)流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被稱作BOM。
UTF-8不需要BOM來表明字節(jié)順序,但可以用BOM來表明編碼方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8編碼是EF BB BF(讀者可以用我們前面介紹的編碼方法驗證一下)。所以如果接收者收到以EF BB BF開頭的字節(jié)流,就知道這是UTF-8編碼了。Windows就是使用BOM來標(biāo)記文本文件的編碼方式的。
--------
關(guān)于內(nèi)碼
--------
內(nèi)碼是指操作系統(tǒng)內(nèi)部的字符編碼。早期操作系統(tǒng)的內(nèi)碼是與語言相關(guān)的。
現(xiàn)在的Windows在系統(tǒng)內(nèi)部支持Unicode,然后用代碼頁適應(yīng)各種語言,“內(nèi)碼”的概念就比較模糊了。
微軟一般將缺省代碼頁指定的編碼說成是內(nèi)碼。
內(nèi)碼這個詞匯,并沒有什么官方的定義,代碼頁也只是微軟這個公司的叫法。作為程序員,我們只要知道它們是什么東西,沒有必要過多地考證這些名詞。
所謂代碼頁("code page")就是針對一種語言文字的字符編碼。例如GBK的"code page"是CP936,BIG5的"code page"是CP950,GB2312的"code page"是CP20936。
Windows中有缺省代碼頁的概念,即缺省用什么編碼來解釋字符。例如Windows的記事本打開了一個文本文件,里面的內(nèi)容是字節(jié)流:BA、BA、D7、D6。Windows應(yīng)該去怎么解釋它呢?
是按照Unicode編碼解釋、還是按照GBK解釋、還是按照BIG5解釋,還是按照ISO8859-1去解釋?如果按GBK去解釋,就會得到“漢字”兩個字。按照其它編碼解釋,可能找不到對應(yīng)的字符,也可能找到錯誤的字符。所謂“錯誤”是指與文本作者的本意不符,這時就產(chǎn)生了亂碼。
答案是Windows按照當(dāng)前的缺省代碼頁去解釋文本文件里的字節(jié)流。缺省代碼頁可以通過控制面板的區(qū)域選項設(shè)置。記事本的另存為中有一項ANSI,其實就是按照缺省代碼頁的編碼方法保存。
Windows的內(nèi)碼是Unicode,它在技術(shù)上可以同時支持多個代碼頁。只要文件能說明自己使用什么編碼,用戶又安裝了對應(yīng)的代碼頁,Windows就能正確顯示,例如在HTML文件中就可以指定charset。
有的HTML文件作者,特別是英文作者,認(rèn)為世界上所有人都使用英文,在文件中不指定CharSet。如果他使用了0x80-0xff之間的字符,中文Windows又按照缺省的GBK去解釋,就會出現(xiàn)亂碼。這時只要在這個html文件中加上指定CharSet的語句,例如:
如果原作者使用的代碼頁和ISO8859-1兼容,就不會出現(xiàn)亂碼了。
再說區(qū)位碼,啊的區(qū)位碼是1601,寫成16進制是0x10,0x01。這和計算機廣泛使用的ASCII編碼沖突。為了兼容00-7f的ASCII編碼,我們在區(qū)位碼的高、低字節(jié)上分別加上A0。這樣“啊”的編碼就成為B0A1。我們將加過兩個A0的編碼也稱為GB2312編碼,雖然GB2312的原文根本沒提到這一點。
為了識別 Unicode 文件,Microsoft 建議所有的 Unicode 文件應(yīng)該以 ZERO WIDTH NOBREAK SPACE(U+FEFF)字符開頭。這作為一個“特征符”或“字節(jié)順序標(biāo)記(byte-order mark,BOM)”來識別文件中使用的編碼和字節(jié)順序。
但是,Linux/UNIX 并沒有使用 BOM,因為它會破壞現(xiàn)有的 ASCII 文件的語法約定。在 POSIX 系統(tǒng)中,選中的語言環(huán)境識別了在一個過程中的所有輸入輸出文件期望的編碼形式。
最近在做的廣告系統(tǒng)中,碰到了一個問題,廣告系統(tǒng)采用的UTF-8編碼,而一些使用這套廣告系統(tǒng)的頻道頁面使用的是GB2312編碼。當(dāng)然也有使用UTF-8編碼的頻道使用這套廣告系統(tǒng)。
頻道頁面是通過嵌入類似如下的代碼方式,來調(diào)用廣告的。具體那個時間顯示那個廣告,或者那些廣告組合是廣告系統(tǒng)自己處理的。
<script type="text/javascript">< span="">script><script type="text/javascript" src="http://ads.csdn.net/AD/Show_JavaScript_AD.js" >< span="">script>
不同編碼的頁面、腳本之間互相引用,就會產(chǎn)生亂碼的問題,解決方法就是統(tǒng)一成一種編碼。
asp.net 中,如果要修改輸出頁面的編碼,可以通過修改web.config中以下配置信息
<globalization requestEncoding="utf-8" responseEncoding="utf-8" />
以上只是修改整體的默認(rèn)編碼,如果只有某個頁的編碼需要修改,ASP.net 中則可以簡單的使用下面代碼:
Encoding gb2312 = Encoding.GetEncoding("gb2312");Response.ContentEncoding = gb2312;
在非ASP.net 應(yīng)用中,可能你讀到的數(shù)據(jù)是UTF-8編碼,但是你要轉(zhuǎn)換為GB2312編碼,則可以參考以下代碼:
string utfinfo = "document.write(\"alert(‘aa你好么??‘);\");";string gb2312info = string.Empty;Encoding utf8 = Encoding.UTF8;Encoding gb2312 = Encoding.GetEncoding("gb2312");// Convert the string into a byte[].byte[] unicodeBytes = utf8.GetBytes(utfinfo);// Perform the conversion from one encoding to the other.byte[] asciiBytes = Encoding.Convert(utf8, gb2312, unicodeBytes);// Convert the new byte[] into a char[] and then into a string.// This is a slightly different approach to converting to illustrate// the use of GetCharCount/GetChars.char[] asciiChars = new char[gb2312.GetCharCount(asciiBytes, 0, asciiBytes.Length)];gb2312.GetChars(asciiBytes, 0, asciiBytes.Length, asciiChars, 0);gb2312info = new string(asciiChars);
當(dāng)然,其他各種編碼之間的轉(zhuǎn)換,跟上述代碼也類似的,就不描述了。
有一個Web項目,Web.Config中requestEncoding和responseEncoding都是gb2312,而從數(shù)據(jù)庫中取出的簡介數(shù)據(jù)可能是中文和韓、日文混合的內(nèi)容,這時候如果直接輸出到頁面上,其頁面會出現(xiàn)亂碼,其中的韓文內(nèi)容無法正確顯示。當(dāng)然如果項目的編碼都使用Utf-8的話將沒有這個問題,但這個項目是一個老項目,為了盡量不要影響已有的程序,所以無法將編碼改為Utf-8,只能在本頁面上動腦筋。
經(jīng)過研究,發(fā)現(xiàn)這個問題可以通過Html實體的方法解決。
對于Html實體請參考:
Character entity references in HTML 4
測試代碼:
Byte[] bComments = Encoding.UTF8.GetBytes("一ンブル????中文");
char[] cComments = Encoding.UTF8.GetChars(bComments);
StringBuilder charBuilder = new StringBuilder();
foreach(char c in cComments)
{
if(c > ‘\u0800‘)
{
charBuilder.Append("&#");
charBuilder.Append((int)c);
}
else
{
charBuilder.Append(c);
}
}
Response.Write(charBuilder.ToString());
這段代碼的作用是將所有的中文、韓文、日文字符通過硬編碼輸出成為html實體。而Html實體是不受ResponseEncoding和頁面編碼集影響的。
說明:
\u0800 以上的為中、韓、日字符。
中文的范圍:\u4e00 - \u9fa5,日文在\u0800 - \u4e00,韓文為\u9fa5以上。
這個方法僅僅是為了解決小范圍問題,如果各位有更好的辦法請指教。
ms的Best Practices Analyzer Tool for Microsoft SQL Server 2000 1.0上個月發(fā)布了1.0版本,和beta版相比,看起來沒有什么太大的變化。Best Practices Analyzer Tool for Microsoft SQL Server 2000檢查的一些rules可以做為t-sql編程的checklist
sql server數(shù)據(jù)庫編程指導(dǎo)以及最佳實踐
原則
編寫高可讀的代碼:遵循命名原則和代碼風(fēng)格約定
開始就要關(guān)注t-sql代碼性能的影響:減少網(wǎng)絡(luò)流量,減少磁盤IO,利用索引,避免lock
編寫安全的代碼
------------------------------------------------------------------------------
命名數(shù)據(jù)庫對象時,采用統(tǒng)一的前綴或者后綴
采用統(tǒng)一的前綴或者后綴是為了提高代碼的可讀性,但是
存儲過程不要使用sp_作為前綴,函數(shù)不要使用fn_作為前綴。
如果sql server發(fā)現(xiàn)存儲過程以sp_作為前綴,都會先到master數(shù)據(jù)庫中查詢這個存儲過程
添加必要的注釋
存儲過程或者函數(shù),視圖前應(yīng)該注釋創(chuàng)建者, 創(chuàng)建時間,修改者,修改時間,功能注釋,使用說明,同時包含一到多條執(zhí)行該對象的語句
及時檢查執(zhí)行狀況
默認(rèn)情況下,如果一條sql語句執(zhí)行錯誤,sql server不會自動roll back前面的執(zhí)行(可以設(shè)置:SET XACT_ABORT ON),sql語句執(zhí)行完畢后,需要及時通過全局變量@@error和@@rowcount來檢查執(zhí)行狀況。
用標(biāo)準(zhǔn)的join方式
標(biāo)準(zhǔn)的join方式是指while語句中只包含過濾條件,不包含join條件
盡量避免客戶端程序直接通過select,insert,update等直接操作數(shù)據(jù)庫
用存儲過程封裝數(shù)據(jù)訪問,存儲過程是經(jīng)過編譯的,不用每次計算execute plan。而且封裝了邏輯,同時增加了安全性。
存儲過程如果需要返回數(shù)據(jù),使用output關(guān)鍵字
不要用return返回數(shù)據(jù),存儲過程的return應(yīng)該返回存儲過程的執(zhí)行狀況,如果需要返回數(shù)據(jù),使用帶有output關(guān)鍵字的參數(shù)
謹(jǐn)慎使用IDENTITY作為表的主鍵的數(shù)據(jù)類型
IDENTITY會給客戶端程序和database交互帶來很多影響,而且在數(shù)據(jù)導(dǎo)入導(dǎo)出時也會帶來麻煩,需要仔細評估這些影響。但是IDENTITY和guid相比也有優(yōu)點,就是可讀性。
盡量避免使用NULL
如果沒有特別設(shè)置,null參與的運算結(jié)果都為null,如果疏忽這一原則,會對程序邏輯的正確性帶來影響,而且,客戶端程序需要額外的步驟來處理NULL。需要設(shè)置ANSI_NULLS為ON。
在insert語句中,使用確定的列名
insert語句中,使用確定的列名以間少表結(jié)構(gòu)變化對t-sql代碼帶來的影響
盡量使用外鍵,約束檢查來保證數(shù)據(jù)的完整性
數(shù)據(jù)完整性至關(guān)重要,外鍵,約束檢查可以避免另外寫代碼來保證數(shù)據(jù)完整性
不要在查詢時用select * ,用確定的列名來代替 *
查詢結(jié)果中冗余的信息影響整體的性能
盡量避免使用服務(wù)器端游標(biāo)
服務(wù)器端游標(biāo)對性能有嚴(yán)重的影響,應(yīng)當(dāng)盡量避免,比如可以用while循環(huán)來代替游標(biāo),如果不能避免,則應(yīng)選擇最合適的游標(biāo)類型
盡量避免使用臨時表
臨時表會發(fā)生磁盤IO操作,影響性能,可以用嵌套查詢,view,或者table變量來代替臨時表。如果需要緩存大量的數(shù)據(jù),臨時表優(yōu)于table變量,同時注意為臨時表建index
如果需要執(zhí)行一系列sql命令,在前面添加SET NOCOUNT ON
執(zhí)行SET NOCOUNT ON,sql命令執(zhí)行影響的行數(shù)不會傳回客戶端,減少網(wǎng)絡(luò)流量,提高性能
在字符串匹配查詢時,避免在第一個字符位置使用通配符
如果第一個字符位置使用通配符,則index不起作用
避免使用IN 或者NOT IN
使用IN 或者NOT IN,則index不起作用
事務(wù)處理時,盡可能的占用最少的資源
事務(wù)處理時,盡可能的占用最少的資源以減少資源的鎖定,提高數(shù)據(jù)庫整體性能。同時檢查加鎖類型,盡量使用低級別的加鎖類型。
匹配的事務(wù)處理
如果存儲過程開始了事務(wù)處理,應(yīng)該負責(zé)結(jié)束這個事物處理,submit或者rollback
操作NCHAR或者NVARCHAR數(shù)據(jù)類型的列時,使用N關(guān)鍵字
使用N關(guān)鍵字,sql server會使用unicode編碼,避免出現(xiàn)亂碼
盡量避免使用TEXT,NTEXT,binary,image數(shù)據(jù)類型的字段,盡量避免將文件或者圖片直接存入數(shù)據(jù)庫
這些數(shù)據(jù)類型訪問方式不同于普通的數(shù)據(jù)類型。數(shù)據(jù)庫也不是存儲文件或者圖片內(nèi)容合適的地方
盡量使用VARCHAR代替CHAR,用NVARCHAR代替VARCHAR,
用VARCHAR代替CHAR是為了節(jié)省數(shù)據(jù)庫空間,NVARCHAR代替VARCHAR是為了避免unicode帶來的麻煩
如果某個table中的數(shù)據(jù)對不同用戶的可見性是不一樣的,使用view來隔離用戶對table的直接訪問
檢查sql注入式攻擊對代碼的影響。