從字面意思上理解就是編碼和解碼,CLR有類似的,像UrlDecode()和UrlEncode()是對URL中的參數(shù)解碼編碼一樣。Encoder,Decoder這兩個是用來字符和字節(jié)之間的編碼和解碼的,是兩個類型,而且還是抽象的,所以我們不能直接實例化它,但是目前CLR中給我們使用的類型中沒有它們的派生類,不過CLR內(nèi)部實現(xiàn)里肯定有它們的派生類。比如說下面的DecoderNLS就被定義成了internal,做為調(diào)用者的我們是看不到的。Encoder和Decoder是在Encoding里以兩個虛方法出現(xiàn)的,GetEncoder()和GetDecoder(),派生類里有不同的實現(xiàn)。比較UTF-8里就返回UTF8Decoder。
[Serializable]
internal class UTF8Decoder : DecoderNLS, ISerializable
[Serializable]
internal class DecoderNLS : Decoder, ISerializable
public override Decoder GetDecoder()
{
return new UTF8Decoder(this);
}
用法也比較簡單,下面代碼不詳細(xì)解釋了。
//Encoderstring test = "ABCDE1234測試";Console.WriteLine("The test of string is {0}", test);Encoding encoding = Encoding.UTF8;char[] source = test.ToCharArray();int strLength = test.Length;int len = encoding.GetEncoder().GetByteCount(source, 0, strLength, false);byte[] result = new byte[len];encoding.GetEncoder().GetBytes(source, 0, strLength, result, 0, false);Console.WriteLine("After Encoder,the byte of test is output below。");foreach (byte b in result){ Console.Write("{0:X}-", b);}Console.WriteLine();//DecoderConsole.Write("After Decoder,the string is ");int deslen = encoding.GetDecoder().GetCharCount(result, 0, result.Length);char[] des = new char[deslen];encoding.GetDecoder().GetChars(result, 0, result.Length, des, 0);foreach (char c in des){ Console.Write("{0}", c);}
也許有人看出來了,這和Encoding的編碼和解碼沒什么區(qū)別啊,Encoding還會更簡單,選擇更多些,為何我還要多創(chuàng)建兩個對象?是的,沒錯,如果對一塊完整的數(shù)據(jù)流,完全沒必要去創(chuàng)建這兩個對象,Encoding的功能已經(jīng)可以實現(xiàn)了,但是如果我們要操作的是文件流或網(wǎng)絡(luò)流,需要跨塊處理,比如每次我都從一個流中讀取5個字節(jié)進(jìn)行處理?看一下代碼就知道了
public static void Main(){ //臨時文件 string path = Path.GetTempFileName(); File.WriteAllText(path, "ABCDE1234測試∑我", new UTF8Encoding(false)); //創(chuàng)建Decoder對象 //Decoder dec = Encoding.UTF8.GetDecoder(); using (FileStream fs = File.OpenRead(path)) { byte[] buffer; int size; //每次都讀取5個字節(jié) buffer = new byte[5]; while ((size = fs.Read(buffer, 0, 5)) > 0) { //char[] chars = new char[dec.GetCharCount(buffer, 0, size)]; //dec.GetChars(buffer, 0, size, chars, 0); char[] chars1 = Encoding.UTF8.GetChars(buffer, 0, size); if (chars1.Length != 0) { //Console.Write("{0,-10}", new string(chars)); Console.Write("{0,-10}", new string(chars1)); Console.Write("字節(jié):"); PrintBytes(buffer, size); } Thread.Sleep(500); } } Console.Read();}static void PrintBytes(byte[] bytes, int len){ for (int i = 0; i < len; i++) Console.Write("{0:X2} ", bytes[i]); Console.WriteLine();}
我們先將字符串"ABCDE1234測試∑我”用UTF-8編碼寫到一個臨時文件里,然后放到一個stream里,再對這個stream每次讀取5個字節(jié)的操作。我們可以看出來這個字符串轉(zhuǎn)化成字節(jié)的長度為1+1+1+1+1+1+1+1+1+3+3+3+3,讀取前5個是沒任何問題的,都是單字節(jié)字符。再讀接下來五個時就有問題了,第10個字符是一個多字節(jié)字符,其中的兩個字節(jié)要放下一次的讀取了,Encoding.GetChars()就不能正確識別了,第10個字符將被識別為亂碼,將會以為?顯示。
下面是打印的結(jié)果:
我們把注釋的代碼取消注釋后,再重新運行看一下結(jié)果,
Decoder dec = Encoding.UTF8.GetDecoder();
char[] chars = new char[dec.GetCharCount(buffer, 0, size)];
dec.GetChars(buffer, 0, size, chars, 0);
Console.Write("{0,-10}", new string(chars));
最左邊的是用Decoder解碼的,中間的是用Encoding解碼的
亂碼消失了,Decoder可以正確的得到我們想要的結(jié)果,而且Encoding卻有亂碼。為什么會這樣?
Encoder和Decoder 維護(hù)對 GetBytes() 和GetChars()的連續(xù)調(diào)用間的狀態(tài)信息,因此它可以正確地對跨塊的字符序列進(jìn)行編碼。Encoder 還保留數(shù)據(jù)塊結(jié)尾的尾部字符并將這些尾部字符用在下一次編碼操作中。例如,一個數(shù)據(jù)塊的末尾可能是一個不匹配的高代理項,而與其匹配的低代理項則可能位于下一個數(shù)據(jù)塊中。因此,Decoder 和 Encoder 對網(wǎng)絡(luò)傳輸和文件操作很有用,這是因為這些操作通常處理數(shù)據(jù)塊而不是完整的數(shù)據(jù)流。StreamReader和SteamWriter關(guān)于讀和書的就是用Decoder和Encoder。
//StreamWriter
int count = this.encoder.GetBytes(this.charBuffer, 0, this.charPos, this.byteBuffer, 0, flushEncoder)
//StreamReader
charIndex = this.decoder.GetChars(this.byteBuffer, 0, this.byteLen, this.charBuffer, charIndex);
這個問題有人問過我,我查了一些資料。因為所有的字符在CLR中都是以Unicode-16編碼的,這個問題就比較好處理了,全角和半角的值它們相差65248,除了空格相差12256。所以全角的字符若是想轉(zhuǎn)換成半角除空格減12256外,其他相減65248便是相應(yīng)的半角。具體可以參考園子里的這篇博客:C#全角和半角轉(zhuǎn)換
聯(lián)系客服