1、Encoding
(1)、如何生成一個Encoding即一種編碼
Encoding位於System.Text命名空間下,是一個抽象類,它的派生類如下圖:
要實例化一個Encoding一共有以下兩種方式:
a、通過實例化它的派生類,然后通過里式轉換實例化一個Encoding,代碼如下:
Encoding e=new UTF8Encoding();
b、通過Encoding的靜態屬性ASCII,Unicode,UTF32,UTF7,UTF8,Default來生成,代碼如下:
Encoding e = Encoding.UTF8;
其實b中的靜態屬性無非是new了一個a中的派生類,有圖為證
注:上面通過靜態屬性生成的Encoding實例,符合單例模式,但是並不適用在多線程環境下,所以當你的Encoding需要全局唯一時,請使用靜態屬性的方式,而不是通過new的方式。
(2)Encoding.Default
注意(1)中b,Encoding的靜態屬性中有一個Default,它沒有對應的派生類,但是它返回的也是一個Encoding對象,至於返回那種語言的Encoding,取決於取決於你電腦里-->控制面板->區域和語言 里面的設置,也就是ANSI,比如我的電腦設置的是中文,那么對應的就是gb2312,但是如果你的代碼不止在一個國家使用,那么就不要使用Encoding.Default,這樣會造成亂碼,最好使用Encoding.UTF8.
2、如何調用常用編碼之外的編碼,通過GetEncoding()和GetEncodings()
上面介紹了ASCII,Unicode,UTF32,UTF7,UTF8常規的5中編碼方式,但是有一些編碼如gb2312就沒有對應的派生類,那么獲取這類語言對應的Encoding只能通過GetEncoding()和GetEncodings()方法來獲取
(1)、GetEncodings()
通過GetEncodings()可以獲取所有的編碼,代碼如下:
EncodingInfo[] infos = Encoding.GetEncodings();
目前為止一共有140種,通過GetEncodings()方法你可以方便的查看所有語言的編碼信息,本人開發了一個簡單窗體應用程序,來查詢不同的編碼信息。點擊下載
(2)、GetEncoding()
通過這個方法可以獲取指定語言的Encoding,當然你必須給出一個codePage或者是name,代碼如下:
Encoding ei=Encoding.GetEncoding(936); Console.WriteLine(ei.WebName);
3、通過Encoding完成字節和字符之間的轉換
(1)、GetBytes() 含多種重載方法
通過GetBytes()可以把一個字符串或者是字符串數組轉換成字節,代碼如下
string str = "阿薩德喝酒啊是貸款"; byte[] bytes = Encoding.Unicode.GetBytes(str);
(2)、GetChars()含多種重載方法
通過GetChars()可以將字節數組轉換成字符,代碼如下
string str = "阿薩德喝酒啊是貸款"; byte[] bytes = Encoding.Unicode.GetBytes(str); char[] a=Encoding.Unicode.GetChars(bytes); Console.WriteLine(a);
(3)、GetByteCount()含多種重載方法
通過GetByteCount()可以獲得將字符串或者字符串數組轉換成字節數組的字節數組的長度,代碼如下:
string str = "阿薩德喝酒啊是貸款"; int count= Encoding.Unicode.GetByteCount(str); Console.WriteLine(count);
(4)、GetCharCount()含多種重載方法
通過GetCharCount()可以獲得將字節數組轉換成字符串或者字符數組的字符串長度,代碼如下:
string str = "阿薩德喝酒啊是貸款"; byte[] bytes = Encoding.Unicode.GetBytes(str); int count = Encoding.Unicode.GetCharCount(bytes); Console.WriteLine(count);
4、BOM 判斷文件的編碼方式
這個BOM並不是Html中的BOM,而是一種字節順序標記,BOM的全稱是全稱是Byte Order Mark,是一段二進制,用於標識一個文本是用什么編碼的,比如當用Notepad打開一個文本時,如果文本里包括這一段BOM,那么它就能判斷是采用哪一種編碼方式,並用相應的解碼方式,就會正確打開文本不會有亂碼.如果沒有這一段BOM,Notepad會默認以ANSI打開,這種會有亂碼的可能性.
下面是通過BOM來判斷文件編碼的一段工具方法,代碼如下:
public static Encoding GetFileEncoding(string filePath) { Encoding Result = null; FileInfo info = new FileInfo(filePath); FileStream fs = default(FileStream); try { fs = info.OpenRead(); Encoding[] unicodeEncodings = { Encoding.BigEndianUnicode, Encoding.Unicode, Encoding.UTF8, Encoding.UTF32, Encoding.UTF7, new UTF32Encoding(true,true) }; for (int i = 0; Result == null && i < unicodeEncodings.Length; i++) { fs.Position = 0; byte[] preamble = unicodeEncodings[i].GetPreamble(); bool isEqual = true; for (int j = 0; isEqual && j < preamble.Length; j++) { isEqual = preamble[j] == fs.ReadByte(); } if (isEqual) Result = unicodeEncodings[i]; } } catch (IOException ex) { throw ex; } finally { if (fs != null) { fs.Close();//包括了Dispose,並通過GC強行釋放資源 } } if (object.ReferenceEquals(null, Result)) { Result = Encoding.Default; } return Result; }
5、Encoder和Decoder
(1)、Encoder是一個抽象類,它在Encoding中以一個虛方法的形式出現,調用該方法,會返回一個指定編碼的編碼器
代碼如下:
string str = "Encoder測試"; int charCount=str.Length; Encoding ed=Encoding.UTF8; char[] chars = str.ToCharArray(); int maxByteCount=ed.GetEncoder().GetByteCount(chars,0,charCount,false);//通過GetEncoder()獲得的UFF8編碼器獲得str轉換成byte數組的長度 byte[] result=new byte[maxByteCount]; ed.GetEncoder().GetBytes(chars, 0, charCount, result, 0, false);//通過GetEncoder()獲得的UTF8編碼器對str進行加密,並將加密后的字節數組賦給result for (int i = 0; i < result.Length; i++) { if (i != result.Length-1) Console.Write("{0:X}-", result[i]);//以16進制輸出 else Console.Write("{0:X}", result[i]); } Console.ReadLine();
(2)、Decoder也是一個抽象類,它在Encoding中也是以一個虛方法的形式出現,調用該方法,會返回一個指定編碼的解碼器
代碼如下:
string str = "Encoder測試"; int charCount=str.Length; Encoding ed=Encoding.UTF8; char[] chars = str.ToCharArray(); int maxByteCount=ed.GetEncoder().GetByteCount(chars,0,charCount,false); byte[] result=new byte[maxByteCount]; ed.GetEncoder().GetBytes(chars, 0, charCount, result, 0, false); char[] resultChars=new char[ed.GetDecoder().GetCharCount(result,0,maxByteCount)]; ed.GetDecoder().GetChars(result, 0, result.Length, resultChars,0,false); for (int i = 0; i < resultChars.Length; i++) { if (i != resultChars.Length - 1) Console.Write("{0}-", resultChars[i]); else Console.Write("{0}", resultChars[i]); } Console.ReadLine();
通過分析上面兩端發現,其實它們的作用和單純的調用Encoding的靜態屬性進行編解碼並沒有什么區別,而且使用Encoding進行編解碼更加的便捷,就不需要創建額外的Encoder和Decoder對象實例,現在就來講解Encoder和Decoder真正的作用!
(3)、通過一個特殊的需求來說明GetDecoder和GetEncoder()的作用
通過Encoding的靜態屬性對(字符串或者字符數組)的整個片段進行編解碼時,並不會出現任何問題,代碼如下:
string str = "Encoding博客系列"; byte[] bytes = Encoding.UTF8.GetBytes(str); char[] result = Encoding.UTF8.GetChars(bytes); Console.WriteLine(result);
但是當處理部分片段,並且片段中有多字節字符或者字符串(如中文),就會出現亂碼的情況,代碼如下:
假設我們需要的在后處理一個特殊文件流,要求每次只處理4個字節,代碼如下:
string path = Path.GetTempFileName();//創建臨時文件,並返回該文件的路徑 File.WriteAllText(path, "Encoding博客系列", new UTF8Encoding(false));//覆蓋上面的臨時文件,並向文件中追加一段字符串,采用UTF8編碼 using (FileStream stream = File.OpenRead(path)) { byte[] buffer = new byte[4]; int size; while ((size = stream.Read(buffer, 0, 4)) > 0) { char[] chars = Encoding.UTF8.GetChars(buffer, 0, size); if (chars.Length != 0) { Console.Write("{0,-6}", new string(chars)); Console.Write("字節:"); for (int i = 0; i < size; i++) { Console.Write("{0:X2} ", buffer[i]);//將單個字節以2位16進制輸出 } Console.WriteLine(); } Thread.Sleep(1000); } } Console.Read();
目前我不清楚為什么出現這個問題的原因,由於時間問題,我也不想深究下去,如果有興趣,你可以去解讀下Encoding的源碼。
so,為了解決這個問題,就只能使用調用UTF8的解碼器,對字符或者字符數組進行解碼,修正代碼如下:
string path = Path.GetTempFileName();//創建臨時文件,並返回該文件的路徑 File.WriteAllText(path, "Encoding博客系列", new UTF8Encoding(false));//覆蓋上面的臨時文件,並向文件中追加一段字符串,采用UTF8編碼 Decoder dr = Encoding.UTF8.GetDecoder(); using (FileStream stream = File.OpenRead(path)) { byte[] buffer = new byte[5]; int size; while ((size = stream.Read(buffer, 0, 5)) > 0) { char[] charsDecoder = new char[dr.GetCharCount(buffer, 0, size)]; dr.GetChars(buffer, 0, size, charsDecoder, 0); char[] chars = Encoding.UTF8.GetChars(buffer, 0, size); if (chars.Length != 0) { Console.Write("{0,-6}", new string(charsDecoder)); Console.Write("{0,-6}", new string(chars)); Console.Write("字節:"); for (int i = 0; i < size; i++) { Console.Write("{0:X2} ", buffer[i]);//將單個字節以2位16進制輸出 } Console.WriteLine(); } Thread.Sleep(1000); } } Console.Read();
Encoder和Decoder 維護對 GetBytes() 和GetChars()的連續調用間的狀態信息,因此它可以正確地對跨塊的字符序列進行編碼。Encoder 還保留數據塊結尾的尾部字符並將這些尾部字符用在下一次編碼操作中。例如,一個數據塊的末尾可能是一個不匹配的高代理項,而與其匹配的低代理項則可能位於下一個數據塊中。因此,Decoder 和 Encoder 對網絡傳輸和文件操作很有用,這是因為這些操作通常處理數據塊而不是完整的數據流。StreamReader和SteamWriter關於讀和書的就是用Decoder和Encoder。
綜上所述:
1. CLR中字符串都是Unicode 16 編碼
2. 盡量調用Encoding的靜態屬性UTF8,Unicode等,而不是去實例它們
3. 盡量避免用Encoding.Defalut
4. BOM是用來識別哪一種編碼的,默認是帶有的,如果不需要,那么調用它們的帶有參數的構造器,找到相應參數傳false
5. 在對文件流和網絡流操作時,應該用Encoder和Decoder
本文參考自:http://www.cnblogs.com/criedshy/archive/2012/08/07/2625358.html