問題描述:使用BinaryReader從檔案中讀取資料,開始在建立BinaryReader執行個體的時候,未指定編碼格式,結果能通過編譯,但是能在執行過程中會報錯如下:“未處理的異常: System.ArgumentException: 輸出字元緩衝區太小,無法包含解碼後的字元,編碼“Unicode (UTF-8)”的操作回退“System.Text.DecoderReplacementFallback”。”
解決過程:
首先附上建立檔案的代碼:
BinaryWriter
1 using System;
2 using System.IO;
3
4 class binaryReader
5 {
6 static void Main ()
7 {
8 FileInfo f = new FileInfo("BinFile2.dat");
9 BinaryWriter bw = new BinaryWriter(f.OpenWrite());
10
11 Console.WriteLine("Base Stream is : {0}",bw.BaseStream);
12
13 double aDouble = 1234.67;
14 int anInt = 32141;
15 char[] aCharArray = {'A','B','C'};
16 string aString = @"teststring";
17
18 bw.Write(aDouble);
19 bw.Write(anInt);
20 bw.Write(aCharArray);
21 bw.Write(aString);
22 bw.Close();
23
24 }
25 }
然後附上BinaryReader測試代碼:
BinaryReader
1 using System;
2 using System.IO;
3 using System.Text;
4
5 class binaryReader
6 {
7 static void Main()
8 {
9 FileInfo f2 = new FileInfo("BinFile2.dat");
10
11 BinaryReader br = new BinaryReader(f2.OpenRead());
12 //BinaryReader br = new BinaryReader(f2.OpenRead(),Encoding.Default);
13
14 int temp = 0;
15
16 while (br.PeekChar() != -1)
17
18 {
19 Console.Write("{0,7:x}",br.ReadByte());
20
21 if (++temp == 4)
22 {
23 Console.WriteLine();
24 temp = 0;
25 }
26
27 }
28 Console.WriteLine();
29 }
30 }
還有錯誤提示:
由上,之輸出第一字元的16進位編碼,剩下的就開始報錯。但是覺得“字元緩衝區太小”是個很詭異的錯誤,然後就在網上搜了下,看看別人是怎麼做的。
第一次,在CSDN上看見有人給出瞭解決的方案,如題目所言,在建立BinaryReader執行個體的時候,指定其編碼方式,就像上面代碼中注釋掉的那一行那樣,就能夠解決問題,將所有字元的16進位編碼正常輸出。
這樣,問題首先集中到編碼上。預設的編碼方式有問題,必須指定,才能避免錯誤。那什麼樣的編碼是可行的,什麼樣的編碼有問題?在Encoding裡面,枚舉了六種編碼方式:UTF7、UTF8、Unicode、BigEndianUnicode、UTF32和Default。要說的是這裡的Default是指:System.Text.DBCSCodePageEncoding。接下來,我做了一個測試,枚舉每一種編碼方式,在上面的代碼中挨個試一遍。結果發現,在我寫的那個BinFile2.dat測試檔案上,除了UTF-8運行失敗外,其他的每種方式都是成功的(此處省略)。那麼,就可以推斷,不帶編碼指定的BinaryReader的建構函式預設使用的是UTF-8的編碼,而這樣在讀取過程中試有問題的。
現在,至少知道用該用那種編碼了。
問題再進一步,在函數塊內部,在讀取檔案的過程中,是哪個函數調用對編碼有“苛刻”的要求?在上面的函數塊中,只包含兩個方法的調用,一個是while語句中的PeekChar(),一個是Console.WriteLine()。我覺得後者的可能性不大,於是做了如下的測試:
BinaryReader2
1 using System;
2 using System.IO;
3 using System.Text;
4
5 class binaryReader
6 {
7 static void Main()
8 {
9 FileInfo f2 = new FileInfo("BinFile2.dat");
10
11 BinaryReader br = new BinaryReader(f2.OpenRead(),Encoding.Default);
12 int temp = 0;
13 int count=20;
14 while (count>0)
15 {
16 Console.Write("{0,7:x}",br.ReadByte());
17
18 if (++temp == 4)
19 {
20 Console.WriteLine();
21 temp = 0;
22 }
23
24 count--;
25 }
26 Console.WriteLine();
27 }
28 }
結果,除了沒能完全輸出字元之外,運行正常,於是,問題集中在了PeekChar()上面。上面用它來判斷檔案的邊界,MSDN中描述“下一個可用的字元,或者,如果沒有可用字元或者流不支援尋找時為 -1。”也就是說,PeekChar()在判斷是否到邊界的過程中,有一個預讀的過程,結合上面的編碼的問題,可以猜測,在它預讀的時候由於編碼的不合適,導致在該方法內部的緩衝區的溢出。
又在網上找到一文《不要使用PeekChar()判斷EOF》,文中只是說不要用PeekChar來判斷EOF,而是使用判斷條件 ( br.BaseStream.Position < br.BaseStream.Length),但是並沒有給出詳細的理由。
而後,又發現了老外也在討論這個問題:http://bytes.com/topic/visual-basic-net/answers/349779-binaryreader-peekchar-argumentexception-conversion-buffer-overflow
……
繼續深入下去,有兩個點要解決:1、UTF-8編碼的問題;2、PeekChar的工作詳細細節。
總結:通過以上的一系列做法,對BinaryReader的使用有了一些粗淺的瞭解,在使用過程中,能夠合理利用,避開容易出錯的地方,但是,根本的問題還未能真正解決。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
更深入的明日再續。