程式中文本的編碼,是一個很煩人的問題。多掌握一些編碼方面的知識,在寫程式的時候,可以少走一些彎路。
在使用.NET應用程式讀文字檔的時候,我們很少注意檔案編碼的問題。因為在大部分情況下,它自己會識別出檔案的編碼。最簡單也是最常用的讀文字檔的語句: System.IO.File.ReadAllText(filePath)(預設採用utf-8編碼),只要傳入檔案的路徑,基本上可以正確的讀出文本的內容。偶爾出現亂碼的時候,只要傳入預設編碼System.Text.Encoding.Default,就可以解決問題。通常.NET程式員(包括我)很少會去關心編碼的問題。這裡System.Text.Encoding是.NET基底類別庫中表示編碼的基類,所有的編碼類別型都繼承它,如:UTF8Encoding, UnicodeEncoding等等。而Default是Encoding類的一個屬性,表示作業系統的當前 ANSI 字碼頁的編碼。針對不同語言,不同文化,不同地區的作業系統,該屬性值是也是不同的。在我的電腦上,Default表示的是:System.Text.DBCSCodePageEncoding。只要一條很簡單的語句就可以獲知Default的類型,在IronPython中:print System.Text.Encoding.Default;而在C#中,可以使用Console.WriteLine(System.Text.Encoding.Default)。關於System.Text.DBCSCodePageEncoding,我並沒有在MSDN上找到它的定義,但通過google我瞭解到它是一種表示亞洲地區語言的編碼。既然如此,它應該可以識別如:gb2312或gbk編碼的文本,而對於unicode或者其他編碼的文本,它應該無能為力。但情況“似乎”並不是這樣的。下面來實驗一下:
開啟記事本,輸入“中國”二字,然後分別另存新檔不同編碼類別型的檔案。如:utf-8編碼的儲存為utf-8.txt,ansi編碼的儲存為ansi.txt,unicode編碼的儲存為nicode.txt。現在通過System.IO.File來分別讀取這三個檔案的內容。下面是IronPython的代碼:
import System<br />baseDir = r'D://'<br />print System.IO.File.ReadAllText(baseDir + 'utf-8.txt')<br />print '-' * 50<br />print System.IO.File.ReadAllText(baseDir + 'unicode.txt')<br />print '-' * 50<br />print System.IO.File.ReadAllText(baseDir + 'ansi.txt')<br />
運行結果為:
System.IO.File.ReadAllText預設採用utf-8來解碼檔案,它能夠正確的解碼utf-8格式儲存的檔案,對於ansi檔案,出現亂碼,這些都在意料之中,但對於unicode格式儲存的文本,它竟然也正確的讀出了內容,這一點很讓人疑憾。
現在繼續我的實驗,在讀檔案的時候,顯式的提供檔案編碼類別型:System.Text.Encoding.Default,代碼為:
import System<br />baseDir = r'D://'<br />print System.IO.File.ReadAllText(baseDir + 'utf-8.txt', System.Text.Encoding.Default)<br />print '-' * 50<br />print System.IO.File.ReadAllText(baseDir + 'unicode.txt', System.Text.Encoding.Default)<br />print '-' * 50<br />print System.IO.File.ReadAllText(baseDir + 'ansi.txt', System.Text.Encoding.Default)<br />
運行結果為:
程式正確的識別出了所有的文本編碼。System.Text.Encoding.Default在上面這段程式中是System.Text.DBCSCodePageEncoding類型,它可以識別ansi,那是理所當然,但為什麼可以識別utf-8與unicode呢?
原因其實很簡單:Notepad在儲存檔案的時候,做了一些手腳,添加了一些附加資訊。如:儲存為utf-8格式的檔案時,notepad會向文本中添加: 0xef, 0xbb, 0xbf,即BOM,而儲存為Unicode格式的檔案時,會添加0xFE, 0xFF。
.NET程式在讀文本的時候,它首先 會檢查一下文本的頭,看是否有類似0xef0xbb0xbf的標識,如果有,則按照這些標識所表示的編碼格式來解碼文本;如果沒有這些頭資訊,則根據傳入的編碼類別型來解碼文本。 下面來驗證一下:
去掉檔案頭的文本:
用系統預設編碼方法來讀文本:
baseDir = r'D://'<br />print System.IO.File.ReadAllText(baseDir + 'utf8.txt', System.Text.Encoding.Default)<br />print System.IO.File.ReadAllText(baseDir + 'unicode.txt', System.Text.Encoding.Default)
運行結果:
在沒有檔案頭的情況下,用DBCSCodePageEncoding來解碼utf-8與unicode的文本,結果當然是亂碼。修改一下程式:
baseDir = r'D://'<br />print System.IO.File.ReadAllText(baseDir + 'utf8.txt') #沒有指定編碼,預設採用utf-8編碼<br />print System.IO.File.ReadAllText(baseDir + 'unicode.txt', System.Text.Encoding.Unicode)
結果:
這下結果正確了。