检测字节流是否是UTF8编码

来源:互联网 发布:淘宝集市店怎么申请 编辑:程序博客网 时间:2024/06/15 13:21

原文:点击打开链接


几天前偶尔看到有人发帖子问“如何自动识别判断url中的中文参数是GB2312还是Utf-8编码”

也拜读了wcwtitxu使用巨牛的正则表达式检测UTF8编码的算法。

 

使用无数或条件的正则表达式用起来却是性能不高。

刚好曾经在项目中有类似的需求,这里把处理思路和整理后的源代码贴出来供大家参考

 

先聊聊原理:

UTF8的编码规则如下表

UTF8 Encoding Rule

看起来很复杂,总结起来如下:

ASCII码(U+0000 - U+007F),不编码

其余编码规则为

  • 第一个Byte二进制以形式为n个1紧跟个0 (n >= 2), 0后面的位数用来存储真正的字符编码,n的个数说明了这个多Byte字节组字节数(包括第一个Byte)
  • 结下来会有n个以10开头的Byte,后6个bit存储真正的字符编码。

因此对整个编码byte流进行分析可以得出是否是UTF8编码的判断。

根据这个规则,我给出的C#代码如下:

 

[c-sharp] view plain copy
  1. /// <summary>  
  2. ///   Determines whether the given <paramref name="inputStream"/>is UTF8 encoding bytes.  
  3. /// </summary>  
  4. /// <param name="inputStream">  
  5. ///    The input stream.  
  6. ///  </param>  
  7. /// <returns>  
  8. ///   <see langword="true"/> if given bystes stream is in UTF8 encoding; otherwise, <see langword="false"/>.  
  9. /// </returns>  
  10. /// <remarks>  
  11. ///   All ASCII chars will regards not UTF8 encoding.  
  12. /// </remarks>  
  13. public static bool IsTextUTF8(ref byte[] inputStream)  
  14. {  
  15.     int encodingBytesCount = 0;  
  16.     bool allTextsAreASCIIChars = true;  
  17.   
  18.     for (int i = 0; i < inputStream.Length; i++)  
  19.     {  
  20.         byte current = inputStream[i];  
  21.   
  22.         if ((current & 0x80) == 0x80)  
  23.         {                      
  24.             allTextsAreASCIIChars = false;  
  25.         }  
  26.         // First byte  
  27.         if (encodingBytesCount == 0)  
  28.         {  
  29.             if ((current & 0x80) == 0)  
  30.             {  
  31.                 // ASCII chars, from 0x00-0x7F  
  32.                 continue;  
  33.             }  
  34.   
  35.             if ((current & 0xC0) == 0xC0)  
  36.             {  
  37.                 encodingBytesCount = 1;  
  38.                 current <<= 2;  
  39.   
  40.                 // More than two bytes used to encoding a unicode char.  
  41.                 // Calculate the real length.  
  42.                 while ((current & 0x80) == 0x80)  
  43.                 {  
  44.                     current <<= 1;  
  45.                     encodingBytesCount++;  
  46.                 }  
  47.             }                      
  48.             else  
  49.             {  
  50.                 // Invalid bits structure for UTF8 encoding rule.  
  51.                 return false;  
  52.             }  
  53.         }                  
  54.         else  
  55.         {  
  56.             // Following bytes, must start with 10.  
  57.             if ((current & 0xC0) == 0x80)  
  58.             {                          
  59.                 encodingBytesCount--;  
  60.             }  
  61.             else  
  62.             {  
  63.                 // Invalid bits structure for UTF8 encoding rule.  
  64.                 return false;  
  65.             }  
  66.         }  
  67.     }  
  68.   
  69.     if (encodingBytesCount != 0)  
  70.     {  
  71.         // Invalid bits structure for UTF8 encoding rule.  
  72.         // Wrong following bytes count.  
  73.         return false;  
  74.     }  
  75.   
  76.     // Although UTF8 supports encoding for ASCII chars, we regard as a input stream, whose contents are all ASCII as default encoding.  
  77.     return !allTextsAreASCIIChars;  
  78. }  

 

再附上单元测试代码:

[c-sharp] view plain copy
  1. /// <summary>  
  2. ///This is a test class for EncodingHelperTest and is intended  
  3. ///to contain all EncodingHelperTest Unit Tests  
  4. ///</summary>  
  5. [TestClass()]  
  6. public class EncodingHelperTest  
  7. {  
  8.     /// <summary>  
  9.     ///  Normal test for this method.  
  10.     ///</summary>  
  11.     [TestMethod()]  
  12.     public void IsTextUTF8Test()  
  13.     {  
  14.         for (int i = 0; i < 1000; i++)  
  15.         {  
  16.             List<Char> chars = new List<char>();  
  17.             chars.Add('中');  
  18.   
  19.             List<UnicodeCategory> temp = new List<UnicodeCategory>();  
  20.             Random rd = new Random((int)(DateTime.Now.Ticks & 0x7FFFFFFF));  
  21.   
  22.             for (int j = 0; j < 255; j++)  
  23.             {  
  24.                 char ch = (char)rd.Next(0xFFFF);  
  25.                 UnicodeCategory uc = System.Globalization.CharUnicodeInfo.GetUnicodeCategory(ch);  
  26.                 if (uc == UnicodeCategory.Surrogate || // Single surrogate could not be encoding correctly.  
  27.                     uc == UnicodeCategory.PrivateUse || // Private use blocks should be excluded.  
  28.                     uc == UnicodeCategory.OtherNotAssigned  
  29.                     )  
  30.                 {  
  31.                     j--;  
  32.                 }  
  33.                 else  
  34.                 {  
  35.                     chars.Add(ch);  
  36.                     temp.Add(uc);  
  37.                 }  
  38.             }  
  39.   
  40.             string str = new string(chars.ToArray());  
  41.   
  42.             byte[] inputStream = Encoding.UTF8.GetBytes(str);  
  43.             bool expected = true;   
  44.             bool actual;  
  45.             actual = EncodingHelper.IsTextUTF8(ref inputStream);  
  46.             Assert.AreEqual(expected, actual, string.Format("UTF8_Assert Fails at:{0}", str));  
  47.   
  48.             inputStream = Encoding.GetEncoding(932).GetBytes(str);  
  49.             expected = false;  
  50.   
  51.             actual = EncodingHelper.IsTextUTF8(ref inputStream);  
  52.             Assert.AreEqual(expected, actual, string.Format("ShiftJIS_Assert Fails at:{0}", str));  
  53.         }  
  54.     }  
  55.   
  56.     /// <summary>  
  57.     ///   Check with All ASCII chars  
  58.     /// </summary>  
  59.     [TestMethod]  
  60.     public void IsTextUTF8Test_AllASCII()  
  61.     {  
  62.         string str = "ABCDEFGHKLHSJKLDFHJKLHAJKLSHJKLHAJKLSHDJKLAHSDJKLHAJKLSDHJKLASHDJKLHASJKLDHJKLASD";  
  63.   
  64.         byte[] inputStream = Encoding.UTF8.GetBytes(str);  
  65.         bool expected = false;  
  66.         bool actual;  
  67.         actual = EncodingHelper.IsTextUTF8(ref inputStream);  
  68.         Assert.AreEqual(expected, actual, string.Format("UTF8_Assert Fails at:{0}", str));  
  69.   
  70.   
  71.     }  
  72. }  
  

 

 另:

如果是判断一个文件是否使用了UTF8编码,不一定非用这种方法,因为通常以UTF8格式保存的文件最初两个字符是BOM头,标示该文件使用了UTF8编码。

 

参考:

维基百科:http://en.wikipedia.org/wiki/UTF-8

原创粉丝点击