熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> ASP編程 >> 正文

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

2013-11-15 12:48:57  來源: ASP編程 

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

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

  什麼是文件編碼?

  首先我們來復習一下編碼的基本概念由於歷史原因Text文件存在ASCIIUnicodeUTFUTF等等編碼方式對於中文還有GB對於Unicode還有UnicodeUnicode對於Unicode又分為Unicode Little EndianUnicode Big Endian要把所有的編碼方式列舉出來是相當的復雜想仔細的研究一下各種編碼的規則和由來可以參考一下這篇文章編碼charset亂碼unicodeutf與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 bigendian 文件的前個byte是 FE FF

  UTF littleendian文件的前個byte是FF FE

  UTF bigendian文件的前個byte是FE FF

  UTF littleendian文件的前個byte是FF FE

  UTF文件的前個byte是EF BB BF

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

  判定文件編碼方式

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

  看看DNN的代碼這個函數在DotNetNukeModulesAdminResourceInstaller命名空間下的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
         UTFBigEndian = first two bytes FE FF
         UTFLittleEndian = first two bytes FF FE
         Lets do the easy ones first
         If Buffer() = And Buffer() = Then
         Return PaTextEncodingUTFLittleEndian
         End If
         If Buffer() = And Buffer() = Then
         Return PaTextEncodingUTFBigEndian
         End If
         If Buffer() = And Buffer() = And Buffer() = Then
         Return PaTextEncodingUTF
         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 PaTextEncodingUnknown
         End If
         Next
         Return PaTextEncodingUTF

  End Function

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

  SystemText

  知道了編碼方式接下來的工作就是解碼了這裡我們要用到Net的SystemText命名空間下的一些類          Encoding
        UnicodeEncoding
        ASCIIEncoding
        UTFEncoding
        UTFEncoding
        UTFEncoding

  等等

  Encoding是基類UnicodeEncodingASCIIEncoding UTFEncodingUTFEncodingUTFEncoding等類繼承自Encoding類專門用來處理各種編碼

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

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

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

             PaTextEncoding EncodingType = GetTextEncodingType(Buffer);
         string DecodedString = ;
         switch (EncodingType)
         {
         case PaTextEncodingUTFLittleEndian:
         DecodedString = SystemTextEncodingUnicodeGetString(buffer);
         break;
         case PaTextEncodingUTFBigEndian:
         DecodedString = SystemTextEncodingBigEndianUnicodeGetString(buffer);
         break;
         case PaTextEncodingUTF:
         DecodedString = SystemTextEncodingUTFGetString(buffer);
         break;
         case PaTextEncodingUTF:
         DecodedString = SystemTextEncodingUTFGetString(buffer);
         break;
         case PaTextEncodingUnknown:
         throw new Exception(Unkonw Encoding);
         break;
         }

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

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

  這一部分代碼在DNN的DotNetNukeModulesAdminResourceInstaller命名空間下的PaDnnInstallerBase類裡

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

  根據不同的編碼方式傳入不同的參數           Dim strScript As String =
         Select Case sqlFileEncoding
         Case PaTextEncodingUTFLittleEndian
         strScript = GetAsciiString(sqlFileBuffer SystemTextEncodingUnicode) SystemTextEncodingUnicodeGetString(sqlFileBuffer)
         Case PaTextEncodingUTFBigEndian
         strScript = GetAsciiString(sqlFileBuffer SystemTextEncodingBigEndianUnicode) SystemTextEncodingBigEndianUnicodeGetString(sqlFileBuffer)
         Case PaTextEncodingUTF
         strScript = GetAsciiString(sqlFileBuffer SystemTextEncodingUTF) SystemTextEncodingUTFGetString(sqlFileBuffer)
         Case PaTextEncodingUTF
         strScript = GetAsciiString(sqlFileBuffer SystemTextEncodingUTF) SystemTextEncodingUTFGetString(sqlFileBuffer)
         Case PaTextEncodingUnknown
         Throw New Exception(StringFormat(SQL_UnknownFile sqlFileName))
         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 strScriptStartsWith(?) Then
         strScript = strScriptSubstring()
         End If

  最後的一點問題

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

  我想解決方案是把所有的文件都轉為UTF編碼針對BOM影響編碼的問題使用UTFEncodingGetString(buffer bufferlength)跳過字節數組的前三個字節


From:http://tw.wingwit.com/Article/program/ASP/201311/21682.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.