繁体   English   中英

从NetworkStream读取时更改StreamReader编码

[英]Change StreamReader Encoding while reading from 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,我会收到问号。

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

您做错了(tm)。

严重的是,您将尝试以完全错误的方式解决此问题。 不要为此使用StreamReader。 特别是不要一次读取1个字节(正如您所说,您需要在对较早的“解决方案”的评论中进行此操作)。

关于为何使用StreamReader的解释,除了显而易见的“因为它并非旨在在读取过程中在编码之间切换”之外,还可以阅读我给出的关于在这里使用StreamReader的效率低下的另一个答案: 在C#中读取mbox文件

您需要做的就是缓冲读取的内容(例如4k缓冲区应该没问题)。 然后,因为您已经必须执行此操作,所以扫描'\\n'字节以逐行提取内容,并合并折叠的标题行。

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

当您获得消息的文本内容时,您将需要检查Content-Type标头中的charset参数。 如果未定义任何charset参数, 则应为US-ASCII,但实际上可以为任何值。 即使已定义字符集,它也可能与消息文本正文中使用的实际字符编码不匹配,因此,您可能再次想要一组备用。

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

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

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

如果您的目标是学习和编写自己的库,我仍然强烈建议您阅读我的代码,因为它非常高效并且可以正确地执行操作。

有几种方法可以通过查看字节顺序标记来检测编码,这是流中少几个字节的地方。 这些将告诉您编码。 但是,该流可能没有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);
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM