當前位置:成語大全網 - 新華字典 - ASP.NET程序是如何處理文件編碼

ASP.NET程序是如何處理文件編碼

 DotNetNuke作為開源項目 很多地方為我們提供了優良的示範 得以壹窺前人的智慧 前幾日 因為研究壹個DNN的BUG 對文件編碼和文件編碼相關方面的處理有壹些認識

 我們經常需要把壹個Text文件(如XML SQL Script)上傳到服務器 然後進行處理(如顯示或者執行) 這裏就涉及到文本文件編碼的問題了

 什麽是文件編碼?

 首先我們來復習壹下編碼的基本概念 由於歷史原因 Text文件存在ASCII Unicode UTF UTF 等等編碼方式 對於中文 還有GB 對於Unicode還有Unicode Unicode 對於Unicode 又分為Unicode Little Endian Unicode Big Endian 要把所有的編碼方式列舉出來是相當的復雜 想仔細的研究壹下各種編碼的規則和由來可以參考壹下這篇文章 編碼 charset 亂碼 unicode utf 與net簡單釋義 我們讀取壹個文本文件時 總是使用某壹種編碼方式去解碼這個文本文件 如果我們使用的解碼方式和文本文件本身的編碼方式不壹致 最後的結果就是得到壹個亂碼的文件

 我可以不用關心這個麻煩的文件編碼嗎?

 大致了解了什麽是文件編碼 我們來看看在DNN裏為什麽要和文件編碼打交道 這麽麻煩 我們不能繞開它嗎?

 在DNN裏 人們可以制作和上傳皮膚 模塊 語言包的 就拿模塊包說吧 模塊包裏包含各種文本文件 比如定義模塊的 dnn文件 數據庫的SQL 腳本文件等等 因為DNN是壹個開源軟件 世界上任何壹個地方的人群都可能使用它 所以這些文本文件可能以各種編碼格式存儲 妳無法強制別人只用某壹種格式來儲存 我們只能偵測每壹個遇到文本文件的編碼方式 並做對應的解碼

 這裏要強調的壹點是 對於DNN 對文本文件的編碼方式做了壹些限制 那就是壹定要使用帶有BOM的Unicode格式 其它格式都壹律按不支持處理 所以DNN的代碼並不是壹個徹底的解決方案 但事情總是取壹個平衡 為 %的應用在多做 %的工作 有時候是沒必要的

 如何解決文件編碼轉換的問題?

 回到我們的問題 對於壹個上傳到服務器的Text文件 我們要解決的問題就是 如何得知這個文件的編碼方式 並用正確的方式解碼 得到 文本文件中的內容

 如何得知這個文件的編碼方式?

 首先我們來看看如何得知文本文件的編碼方式 為了簡化問題 我們只討論Unicode編碼這種形式(實際上DNN裏也只針對Unicode做了處理) 對於其它各種編碼的判別方式我們不做討論

 BOM

 這裏涉及到壹個BOM(Byte Order Mark) 的概念 簡單的講 在Unicode標準中 為了標示文本文件的編碼類型 可以在文本文件的開始插入幾個特殊的byte 通過這幾個特殊的byte 應用程序就可以鑒別文本文件使用的是那種編碼了 那幾個特殊的byte也被稱之為BOM(參考 )

 對於Unicode 幾種編碼的BOM如下

 UTF big endian 文件的前 個byte是 FE FF

 UTF little endian文件的前 個byte是 FF FE

 UTF big endian文件的前 個byte是 FE FF

 UTF little endian文件的前 個byte是 FF FE

 UTF 文件的前 個byte是 EF BB BF

 UTF 的規律特殊壹點 不是前幾個byte 而是所有的byte轉換為十進制都小於

 判定文件編碼方式

 知道了這壹點 妳也應該能想到如何判定壹個文本文件的編碼方式了吧 讀取文件的前面幾個字節 跟上面的表對比 就可以知道這個文件使用的哪壹種編碼了

 看看DNN的代碼 這個函數在DotNetNuke Modules Admin ResourceInstaller命名空間下的PaFile類裏

 GetTextEncodingType Private Function GetTextEncodingType()Function GetTextEncodingType(ByVal Buffer As Byte()) As PaTextEncoding UTF = No byte higher than UTF = first three bytes EF BB BF UTF BigEndian = first o bytes FE FF UTF LittleEndian = first o bytes FF FE Lets do the easy ones first If Buffer( ) = And Buffer( ) = Then Return PaTextEncoding UTF LittleEndian End If If Buffer( ) = And Buffer( ) = Then Return PaTextEncoding UTF BigEndian End If If Buffer( ) = And Buffer( ) = And Buffer( ) = Then Return PaTextEncoding UTF End If This does a simple test to verify that there are no bytes with a value larger than which would be invalid in UTF encoding Dim i As Integer For i = To If Buffer(i) > Then Return PaTextEncoding Unknown End If Next Return PaTextEncoding UTF

 End Function

 代碼很好懂 PaTextEncoding是壹個枚舉類型 枚舉各種編碼格式 唯壹要註意的就是對於UTF 編碼 采用了壹種比較簡單的判定方式——只檢查了前 個byte是否小於

 System Text

 知道了編碼方式 接下來的工作就是解碼了 這裏我們要用到 Net的System Text命名空間下的壹些類 Encoding UnicodeEncoding ASCIIEncoding UTF Encoding UTF Encoding UTF Encoding

 等等

 Encoding是基類 UnicodeEncoding ASCIIEncoding UTF Encoding UTF Encoding UTF Encoding等類繼承自Encoding類 專門用來處理各種編碼

 使用Encoding Convert (Encoding Encoding Byte[])方法 可以把字節數組從壹種編碼的轉換為另壹種編碼

 使用GetString(Byte[])方法 比如UTF Encoding GetString(Byte[])就可以把UTF 編碼得到字節數組還原成壹個String

 復習了Sytem Text下關於編碼轉換的壹些類 回到我們的問題 妳也許已經在想 判斷完文件編碼的類型後 只需要調用相應的GetString()函數就可以解碼了 如下

  ? PaTextEncoding EncodingType = GetTextEncodingType(Buffer); string DecodedString = ; switch (EncodingType) { case PaTextEncoding UTF LittleEndian: DecodedString = System Text Encoding Unicode GetString(buffer); break; case PaTextEncoding UTF BigEndian: DecodedString = System Text Encoding BigEndianUnicode GetString(buffer); break; case PaTextEncoding UTF : DecodedString = System Text Encoding UTF GetString(buffer); break; case PaTextEncoding UTF : DecodedString = System Text Encoding UTF GetString(buffer); break; case PaTextEncoding Unknown: throw new Exception( Unkonw Encoding ); break; }

 想法是沒錯的 但有壹個小小的問題 之前我們提到過BOM 不同的編碼文件前面幾個字節會有不同的BOM標示 這幾個字節唯壹的作用就是指明編碼類型 在解碼時應該去掉這幾個字節 但問題是 GetString()函數不會自動去掉這幾個字節 如果直接把所有的字節數組傳給GetString()函數 因為BOM的影響 解碼得到的字符串前面幾個字是亂碼

 DNN裏用了壹個比較巧妙的辦法 首先偵測字節數組的編碼方式 之後把所有的字節數組都轉換為ASCII編碼方式的字節數組 最後通過ASCIIEncoding的GetString()函數得到字符串 因為BOM的影響 轉換得到的ASCII字符串前面會有壹些 ? 字符 查找這些字符並去掉即可 代碼如下

 這壹部分代碼在DNN的DotNetNuke Modules Admin ResourceInstaller命名空間下的PaDnnInstallerBase類裏

 GetAsciiString()函數實現轉換為ASCII編碼 並解碼為String ? Protected Function GetAsciiString()Function GetAsciiString(ByVal Buffer As Byte() ByVal SourceEncoding As Encoding) As String Create o different encodings Dim TargetEncoding As Encoding = Encoding ASCII Perform the conversion from one encoding to the other Dim asciiBytes As Byte() = Encoding Convert(SourceEncoding TargetEncoding Buffer) Convert the new byte[] into an ascii string Dim asciiString As String = System Text Encoding ASCII GetString(asciiBytes) Return asciiString End Function

 根據不同的編碼方式 傳入不同的參數 ? Dim strScript As String = Select Case sqlFile Encoding Case PaTextEncoding UTF LittleEndian strScript = GetAsciiString(sqlFile Buffer System Text Encoding Unicode) System Text Encoding Unicode GetString(sqlFile Buffer) Case PaTextEncoding UTF BigEndian strScript = GetAsciiString(sqlFile Buffer System Text Encoding BigEndianUnicode) System Text Encoding BigEndianUnicode GetString(sqlFile Buffer) Case PaTextEncoding UTF strScript = GetAsciiString(sqlFile Buffer System Text Encoding UTF ) System Text Encoding UTF GetString(sqlFile Buffer) Case PaTextEncoding UTF strScript = GetAsciiString(sqlFile Buffer System Text Encoding UTF ) System Text Encoding UTF GetString(sqlFile Buffer) Case PaTextEncoding Unknown Throw New Exception(String Format(SQL_UnknownFile sqlFile Name)) End Select This check needs to be included because the unicode Byte Order mark results in an extra character at the start of the file The extra character ? causes an error with the database If strScript StartsWith( ? ) Then strScript = strScript Substring( ) End If

 最後的壹點問題

 DNN裏這種避免BOM影響解碼的方法有壹個問題 那就是它把所有的文件都轉為ASCII編碼 而ASCII編碼是不支持雙字節的 也就是說如果文件中包含中文 中文在解碼後就成為亂碼了 具體現象可以參考這個文章 SQL SERVER EXPRESS與出現中文變成問號的奇怪問題 很可能不是通常的utf 編碼問題

lishixinzhi/Article/program/ASP/201311/21682