何为“流文本”? 他就像“流媒体”,众所周知“流媒体”可以边传送数据边将已接收的不完整的数据预览给接收方。“流文本”亦如此。
数据传输中的数据并不会按期望以一个整体直接被接收的,尤其是数据量比较大的情况(或者接收方接收速度有限的情况),都会使数据被分割成一块一块的形式传给接收方的。这个数据块的边界是没有任何规律的,因此所谓“流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
类的对应方法,结果会是一片乱码(只有少数字符由于随机数据块较规则会被正确解析,不过只有很少)。