何為“流文本”? 他就像“流媒體”,眾所周知“流媒體”可以邊傳送資料邊將已接收的不完整的資料預覽給接收方。“流文本”亦如此。
資料轉送中的資料並不會按期望以一個整體直接被接收的,尤其是資料量比較大的情況(或者接收方接收速度有限的情況),都會使資料被分割成一塊一塊的形式傳給接收方的。這個資料區塊的邊界是沒有任何規律的,因此所謂“流xx”模式實現的一大痛點就是怎樣把接收到的資料片段正確分析並給使用者提供即時預覽。
.NET中有System.Text.Decoder類前來幫忙!
在.NET下,一般情況下將位元組數群組轉換成字串的方法是使用Encoding類的成員函數,但是只有在資料是全部完整的字元編碼才可以,遇到不完整的資料片段,Encoding類解析結果中會有亂碼。
這裡就得用到System.Text.Decoder類,同Encoding類一樣,它可以將位元組數群組轉換成文字,不過他有一個獨一無二的特點,他可以把後面無法識別的編碼緩衝起來,等下一批資料進入後,先把緩衝的資料加在前面再解析。這恰恰是“流文本”所需要的!
一般情況下使用Decoder類的GetCharCount和GetChars方法就可以滿足我們所說的功能,用法和Encoding類的相應方法類似,這裡就不再多介紹了。
另外為了不介入其他複雜的概念且方便理解,代碼上沒有用System.Net命名空間的類來做一些真的網路流傳輸,我們就簡簡單單用FileStream,隨機從Stream中讀一段連續位元組來類比資料傳輸中的不確定資料區塊,接著每接收到一部分資料區塊,程式試圖給出預覽。
static Random random = new Random(Environment.TickCount);
public static void Main()
{
//臨時檔案
string path = Path.GetTempFileName();
//使用不加BOM的UTF32寫入字串ABCDEFG 我你他 123456789 Mgen
File.WriteAllText(path, "ABCDEFG 我你他 123456789 Mgen", new UTF32Encoding(false, false));
//建立Decoder對象
Decoder dec = Encoding.UTF32.GetDecoder();
using (FileStream fs = File.OpenRead(path))
{
byte[] buffer;
int size;
//隨機讀取一部分,並用Decoder嘗試輸出可以得到的文字
while ((size = GetRandomPart(fs, out buffer)) > 0)
{
char[] chars = new char[dec.GetCharCount(buffer, 0, buffer.Length)];
dec.GetChars(buffer, 0, buffer.Length, chars, 0);
if (chars.Length != 0)
{
Console.Write("{0,-20}", new string(chars).Replace(" ", "<空格>"));
Console.Write("資料區塊:");
PrintBytes(buffer, size);
}
}
}
}
//輸出位元組數組
static void PrintBytes(byte[] bytes, int len)
{
for (int i = 0; i < len; i++)
Console.Write("{0:X2} ", bytes[i]);
Console.WriteLine();
}
//隨機讀取一塊資料
static int GetRandomPart(Stream s, out byte[] buffer)
{
buffer = new byte[random.Next(1, 12)];
return s.Read(buffer, 0, buffer.Length);
}
一種輸出(因為每次輸出結果隨機)
AB 資料區塊:41 00 00 00 42 00 00 00 43 00
CD 資料區塊:00 00 44 00 00 00 45 00 00
E 資料區塊:00 46 00 00
F 資料區塊:00 47
G<空格> 資料區塊:00 00 00 20 00 00 00
我你 資料區塊:00 00 60 4F 00 00 D6 4E 00
他<空格> 資料區塊:00 20 00 00 00 31 00 00
1 資料區塊:00 32 00 00
2 資料區塊:00 33 00
345 資料區塊:00 00 34 00 00 00 35 00 00 00 36
678 資料區塊:00 00 00 37 00 00 00 38 00 00 00
9<空格> 資料區塊:39 00 00 00 20 00 00 00 4D 00 00
M 資料區塊:00 67 00
ge 資料區塊:00 00 65 00 00 00 6E 00 00
n 資料區塊:00
不錯,Decoder可以很好得預覽出每一段接受的資料區塊,即使這些資料區塊是不完整的(並不是某些字元的對應編碼值是在一個資料區塊裡的)
如果你把上面的Decoder的GetCharCount和GetChars方法改成Encoding類的對應方法,結果會是一片亂碼(只有少數字元由於隨機資料區塊較規則會被正確解析,不過只有很少)。