从NetworkStream读取时更改StreamReader编码

时间:2014-03-20 13:13:24

标签: c# .net streamreader pop3 networkstream

我正在尝试从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 + "\r\n");

            //st += tmp + "\r\n";

            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。

有关如何在从网络流中读取数据时更改编码的任何建议?

2 个答案:

答案 0 :(得分:1)

你做错了(tm)。

但是,说真的,你要试图以完全错误的方式解决这个问题。不要使用StreamReader。特别是不要一次读取1个字节(正如你所说的,你需要在之前的#34;解决方案#34中发表评论)。

为了解释为什么使用StreamReader,除了显而易见的"因为它不是为了在阅读过程中在编码之间切换而感觉到的,所以感觉我可以免费阅读另一个答案,我在这里回答了使用StreamReader的低效问题:Reading an mbox file in C#

你需要做的是缓冲你的读数(例如4k缓冲区应该没问题)。然后,正如您必须要做的那样,扫描'\n'字节以逐行提取内容,组合折叠的标题行。

每个标题可能有多个编码字标记,每个标记都可以在一个单独的字符集中,假设它们被正确编码,否则你将不得不处理未声明的8位数据,并尝试以某种方式将其按入unicode (可能有一套后备字符集)。我建议首先尝试使用UTF-8,然后选择你的库用户提供的字符集,最后再尝试iso-8859-1(确保不要尝试使用iso-8859-1直到你完成#8859-1尝试了其他一切,因为任何8位文本序列都将使用iso-8859-1字符编码正确转换为unicode。

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

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

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

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

如果您的目标是学习和编写自己的图书馆,我仍然强烈建议您阅读我的代码,因为它非常高效且能够以适当的方式完成任务。

答案 1 :(得分:0)

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

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

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);
    }
}