在从NetworkStream读取时更改StreamReader编码



我正试图从POP3阅读电子邮件,当我在标题中找到字符集时,更改为正确的编码。

我使用TCP客户端连接到POP3服务器。

下面是我的代码:
    public string ReadToEnd(POP3Client pop3client, out System.Text.Encoding messageEncoding)
    {
        messageEncoding = TCPStream.CurrentEncoding;
        if (EOF)
            return ("");
        System.Text.StringBuilder sb = new System.Text.StringBuilder(m_bytetotal * 2);
        string st = "";
        string tmp;
        do
        {
            tmp = TCPStream.ReadLine();
            if (tmp == ".")
                EOF = true;
            else
                sb.Append(tmp + "rn");
            //st += tmp + "rn";
            m_byteread += tmp.Length + 2; // CRLF discarded by read
            FireReceived();
            if (tmp.ToLower().Contains("content-type:") && tmp.ToLower().Contains("charset="))
            {
                try
                {
                    string charSetFound = tmp.Substring(tmp.IndexOf("charset=") + "charset=".Length).Replace(""", "").Replace(";", "");
                    var realEnc = System.Text.Encoding.GetEncoding(charSetFound);
                    if (realEnc != TCPStream.CurrentEncoding)
                    {
                        TCPStream = new StreamReader(pop3client.m_tcpClient.GetStream(), realEnc);
                    }
                }
                catch { }
            }                
        } while (!EOF);
        messageEncoding = TCPStream.CurrentEncoding;
        return (sb.ToString());
    }

如果我删除这一行:

TCPStream = new StreamReader(pop3client.m_tcpClient.GetStream(), realEnc);

一切正常,除了当电子邮件包含不同的字符集字符时,我得到问号,因为初始编码是ASCII。

关于如何改变编码,而从网络流读取数据的任何建议?

你做错了。

严肃地说,你正试图用完全错误的方法来解决这个问题。不要使用StreamReader。特别是不要一次读取1字节(就像你在之前的"解决方案"的注释中说的那样)。

对于为什么使用StreamReader的解释,除了明显的"因为它不是设计为在阅读过程中在编码之间切换"之外,请随意阅读我在这里给出的关于使用StreamReader效率低下的另一个答案:在c#中读取mbox文件

您需要做的是缓冲读取(例如4k缓冲区就可以了)。然后,正如您已经不得不做的那样,扫描'n'字节以逐行提取内容,合并折叠的标题行。

每个报头可能有多个编码字令牌,假设它们被正确编码,可能每个都在一个单独的字符集中,否则你将不得不处理未声明的8位数据,并尝试以某种方式将其揉入unicode(可能通过一组回退字符集)。我建议在最后尝试iso-8859-1之前,先尝试UTF-8,然后选择您的库用户提供的字符集(请确保在尝试其他所有方法之前不要尝试iso-8859-1,因为任何8位文本序列都可以使用iso-8859-1字符编码正确转换为unicode)。

当您获得消息的文本内容时,您将需要检查Content-Type标头中的charset参数。如果没有定义字符集参数,则应该是US-ASCII,但实际上它可以是任何东西。即使定义了字符集,它也可能与消息文本主体中使用的实际字符编码不匹配,因此您可能再次希望有一组回退。

正如您可能已经猜到的那样,这显然不是一项微不足道的任务,因为它需要解析器在运行时进行动态字符转换(并且字符转换需要内部解析器在任何给定时间关于预期字符集的状态)。

既然我已经完成了这项工作,你应该考虑使用MimeKit,它将解析电子邮件,并使用适当的字符集编码正确地对标题和内容进行字符集转换。

我还写了一个Pop3Client类,它包含在我的MailKit库中。

如果您的目标是学习和编写自己的库,我仍然强烈建议您阅读我的代码,因为它非常高效,并且以正确的方式处理事情。

有一些方法可以通过查看字节顺序标记来检测编码,字节顺序标记是流的前几个字节。这些会告诉你编码。然而,流可能没有BOM,在这些情况下,它可能是ASCII、没有BOM的UTF或其他。

你可以使用encoding类将你的流从一种编码转换为另一种编码:

Encoding textEncoding = Encoding.[your detected encoding here];
byte[] converted = Encoding.UTF8.GetBytes(textEncoding.GetString(TCPStream.GetBuffer()));

您可以在转换时选择您的首选编码。

希望它能回答你的问题。

编辑
你可以用这段代码来读取你的流。

MemoryStream st = new MemoryStream();
int numOfBytes = 1024;
int reads = 1;
while (reads > 0)
{
    byte[] bytes = new byte[numOfBytes];
    reads = yourStream.Read(bytes, 0, numOfBytes);
    if (reads > 0)
    {
        int writes = ( reads < numOfBytes ? reads : numOfBytes);
        st.Write(bytes, 0, writes);
    }
}

最新更新