2014-11-20 129 views
3

.NET字符串對象是否可能包含無效的Unicode代碼點?.NET字符串對象和無效的Unicode代碼點

如果是的話,這將如何發生(以及如何確定字符串是否有這樣的無效字符)?

+0

壞PInvoke的,通常。避免詢問XY問題。 – 2014-11-20 21:10:44

+0

@HansPassant我只能發佈我的第一個問題。對於太短的問題有一些驗證,所以我還添加了第二部分,我認爲這是相關的。 – 2014-11-20 21:14:09

+0

@HansPassant:那麼,沒有無與倫比的代理人不會錯誤的方式?未分配的碼點呢? – Deduplicator 2014-11-20 21:24:49

回答

4

雖然@DPenner給出的響應是優異的(和我用它作爲起點),我想給出一些其他細節。 除了孤兒代理人,我認爲這是一個無效字符串的明確標誌,總是有一種可能性,即一個字符串包含未分配的代碼點,這種情況不能被視爲錯誤的。因爲新字符總是添加到Unicode標準中,請參閱Unicode http://en.wikipedia.org/wiki/Unicode#Versions的版本。而且,爲了使事情更清楚,當使用.NET 2.0時,此呼叫Char.GetUnicodeCategory(Char.ConvertFromUtf32(0x1F01C), 0);返回UnicodeCategory.OtherNotAssigned,但在使用.NET 4.0時它將返回UnicodeCategory.OtherSymbol

除此之外,還有一個有趣的地方:即使.NET類庫方法不同意如何處理Unicode非字符和不成對的替代字符。例如:

  • 不成對代理char
    • System.Text.Encoding.Unicode.GetBytes("\uDDDD"); - 返回{ 0xfd, 0xff}編碼爲Replacement character,即,數據被認爲是無效的。
    • "\uDDDD".Normalize(); - 拋出異常並顯示消息「在索引0處找到無效的Unicode代碼點」,即數據被視爲無效。
  • 非字符碼點
    • System.Text.Encoding.Unicode.GetBytes("\uFFFF"); - 返回{0xff, 0xff},也就是說,該數據被視爲有效。
    • "\uFFFF".Normalize(); - 拋出異常並顯示消息「在索引0處找到無效的Unicode代碼點」,即數據被視爲無效。

下面是將一個字符串爲無效字符搜索的方法:

/// <summary> 
/// Searches invalid charachters (non-chars defined in Unicode standard and invalid surrogate pairs) in a string 
/// </summary> 
/// <param name="aString"> the string to search for invalid chars </param> 
/// <returns>the index of the first bad char or -1 if no bad char is found</returns> 
static int FindInvalidCharIndex(string aString) 
{ 
    int ch; 
    int chlow; 

    for (int i = 0; i < aString.Length; i++) 
    { 
     ch = aString[i]; 
     if (ch < 0xD800) // char is up to first high surrogate 
     { 
      continue; 
     } 
     if (ch >= 0xD800 && ch <= 0xDBFF) 
     { 
      // found high surrogate -> check surrogate pair 
      i++; 
      if (i == aString.Length) 
      { 
       // last char is high surrogate, so it is missing its pair 
       return i - 1; 
      } 

      chlow = aString[i]; 
      if (!(chlow >= 0xDC00 && chlow <= 0xDFFF)) 
      { 
       // did not found a low surrogate after the high surrogate 
       return i - 1; 
      } 

      // convert to UTF32 - like in Char.ConvertToUtf32(highSurrogate, lowSurrogate) 
      ch = (ch - 0xD800) * 0x400 + (chlow - 0xDC00) + 0x10000; 
      if (ch > 0x10FFFF) 
      { 
       // invalid Unicode code point - maximum excedeed 
       return i; 
      } 
      if ((ch & 0xFFFE) == 0xFFFE) 
      { 
       // other non-char found 
       return i; 
      } 
      // found a good surrogate pair 
      continue; 
     } 

     if (ch >= 0xDC00 && ch <= 0xDFFF) 
     { 
      // unexpected low surrogate 
      return i; 
     } 

     if (ch >= 0xFDD0 && ch <= 0xFDEF) 
     { 
      // non-chars are considered invalid by System.Text.Encoding.GetBytes() and String.Normalize() 
      return i; 
     } 

     if ((ch & 0xFFFE) == 0xFFFE) 
     { 
      // other non-char found 
      return i; 
     } 
    } 

    return -1; 
} 
+0

這很有趣,雖然據我所知,行爲實際上符合Unicode標準。由於'GetBytes()'是一種轉換方法,當存在非法字節序列時,它是[required](http://www.unicode.org/faq/utf_bom.html#gen8)以某種方式發出錯誤信號。對於''\ uDDDD'.Normalize()',該字符串首先是無效的,因此Unicode對此沒有任何可說的。用'「\ uFFFF」.Normalize()',[TR 15的第12節](http://www.unicode.org/reports/tr15/#Stabilized_Strings)明確允許進程在具有未分配字符的字符串上中止。 – DPenner1 2014-11-28 04:15:14

+2

你對代孕對不公平,是的。你使用'System.Text.Encoding.Unicode',但這只是一個方便的方法來獲得一個新的System.Text.UnicodeEncoding(bigEndian:false,byteOrderMark:true,throwOnInvalidBytes:false) 。如果使用'new System.Text.UnicodeEncoding(bigEndian:false,byteOrderMark:true,throwOnInvalidBytes:true)',那麼您會在不成對的代理案例中遇到異常。 – 2015-01-26 19:21:18

0

嗯,我認爲.NET字符串內的無效代碼點只有在某人將某個元素設置爲hi或lo-surrogate時纔會發生。也可能發生某人從有效代理對中刪除hi或lo-surrogate的情況,後者不僅可以通過刪除元素而發生,還可以通過更改元素的值來實現。在我看來,答案是「是」,它可能發生,唯一的原因可能是在字符串中存在孤兒hi-or lo-surrogate。你有一個真正的例子字符串?張貼在這裏,我可以檢查什麼是錯的。

B.t.w.對於UTF-16文件也是如此。這有可能發生。對於帶有0xFFEE BOM的utf-16LE文件,請確保您的第一個字符不是0,因爲那麼您的前4個字節是0xFFFE0000,肯定會被解釋爲utf-32LE BOM而不是utf-16LE BOM!

+0

不,我沒有樣品,但如果可能的話,我希望看到一個樣品。 – 2014-11-21 17:14:48

+0

我有這樣的文件,但我怎麼上傳它?我留下了一個鏈接到我的http服務器,顯示了一部分文件的截圖:[鏈接到腐敗的utf-16文件](http://peter.brightman.de/pics/OrphanedSurrogate.jpg) – brighty 2014-11-23 13:45:25

1

在.NET和C#中的所有字符串都使用UTF-16編碼,但有一個例外(從Jon Skeet's blog拍攝):

...有兩種不同的表示:大部分的時間,UTF- 16 被使用,但屬性構造參數使用UTF-8 ...

5

是的,這是可能的。根據Microsoft的文檔,.NET String簡單地說就是

String對象是表示字符串的System.Char對象的順序集合。

而.NET Char

表示一個字符作爲UTF-16代碼單元。

總之,這意味着.NET字符串只是一系列UTF-16代碼單元,無論它們是否是根據Unicode標準的有效字符串。有很多方法可以發生,我可以想到的一些更常見的方法是:

  • 非UTF-16字節流被錯誤地放入String對象而沒有正確轉換。
  • 一個String對象被分割在一個代理對之間。
  • 有人故意包含這樣一個字符串來測試系統的健壯性。

其結果,下面的C#代碼是完全合法的,並且將編譯:

class Test 
    static void Main(){ 
     string s = 
      "\uEEEE" + // A private use character 
      "\uDDDD" + // An unpaired surrogate character 
      "\uFFFF" + // A Unicode noncharacter 
      "\u0888"; // A currently unassigned character  
     System.Console.WriteLine(s); // Output is highly console dependent 
    } 
} 
相關問題