代码改变世界

关于 StreamReader 逐字读取字符的问题

2008-06-03 23:47 by 晓风残月, ... 阅读, ... 评论, 收藏, 编辑
MSDN 对 StreamReader.Read() 方法的解释是:

读取输入流中的下一个字符并使该字符的位置提升一个字符。

而该方法的返回值是一个 int ,之前竟然是这样转换的:

string str =System.Text.Encoding.Unicode.GetString(BitConverter.GetBytes(sr.Read()));

还是费了九牛二虎之力,才得到正确结果,才发现无论使用何种Encoding打开Stream,返回的总是 Unicode 编码。

今天才发现:可以直接将 int 强制转换成 char,并且可以正确的解码。因为,上面提到 Read 总是返回 Unicode 编码,而 char 在 .NET 内部正是用的 Unicode 编码。

char ch = (char)sr.Read();

事实上,MSDN这句话出现了两处字符已经暗示了 char 类型,只怪自己未能领悟。那么为什么 Read() 方法不直接返回 char ?StreamReader 的基类 TextReader 就是明确定义为字符的读取器。 找到MSDN对char类型的说明中提到:

多数 Unicode 字符可由一个 Char 对象表示,但编码为基字符、代理项对和/或组合字符序列的字符由多个 Char 对象表示。因此,String 对象中的 Char 结构不一定与单个 Unicode 字符等效。

看样子,返回 int 类型是为了兼容双字节(char为双字节)仍能无法表示的字符,只是此时就不知道怎么转换了? 用第一种方法强制解码?
有空找几个特殊字符来试试~
 
以下示例代码使用了GB2312作为流编码,当然其他编码也是一样的。
    public string Foo(string path)
    {
        StringBuilder outBuffer = new StringBuilder();       

        try
        {
            if (File.Exists(path))
            {
                File.Delete(path);
            }

            Encoding fileEncoding = Encoding.GetEncoding("GB2312");            
            using (StreamWriter sw = new StreamWriter(path, false, fileEncoding))
            {
                sw.WriteLine("");
                sw.WriteLine("");
                sw.WriteLine("中国人");
            }

            using (StreamReader sr = new StreamReader(path, fileEncoding))
            {
                while (sr.Peek() >= 0)
                {
                    // 曾经是这样读取的:将 int 转成 byte[],然后再解码,然后尝试了N多编码,
                    //费了九牛二虎之力,才得到正确结果,才发现无论使用何种Encoding打开Stream,返回的总是 Unicode 编码。
                    // outBuffer.Append(System.Text.Encoding.Unicode.GetString(BitConverter.GetBytes(sr.Read())));
                    // 
                    // 今天才在MSDN中发现:可以直接将 int 强制转换成 char,并且可以正确的解码。
                    // 因为,上面提到 Reader 总是返回 Unicode 编码,而 char 在 .NET 内部正是用的 Unicode 编码。
                    // 郁闷!
                    outBuffer.Append((char)sr.Read());                    
                }
            }
        }
        catch (Exception ex)
        {
            outBuffer.AppendFormat("The process failed: {0}", ex.ToString()).AppendLine();
        }
        //
        return outBuffer.ToString();
    }