簡體   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