.NET String对象和无效的Unicode代码点

And*_*tan 4 .net string unicode

.NET String对象是否可能包含无效的Unicode代码点?

如果是,可能会发生这种情况(如何确定字符串是否具有此类无效字符)?

DPe*_*er1 6

对的,这是可能的.根据微软的文档,.NET String很简单

String对象是表示字符串的System.Char对象的顺序集合.

而.NET Char

将字符表示为UTF-16代码单元.

总之,这意味着.NET String只是一系列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
    }
}
Run Code Online (Sandbox Code Playgroud)


And*_*tan 5

尽管@DPenner给出的响应非常好(并且我以它为起点),但我想提供一些其他细节。除了我认为是无效字符串的明显标志的孤立代理之外,字符串始终包含未分配的代码点的可能性,并且.NET Framework不能将这种情况视为错误,因为新字符总是添加到Unicode标准中,例如参见Unicode版本http://en.wikipedia.org/wiki/Unicode#Versions。并且,为了使情况更清楚,使用.NET 2.0时此调用Char.GetUnicodeCategory(Char.ConvertFromUtf32(0x1F01C), 0);返回 UnicodeCategory.OtherNotAssigned,但是UnicodeCategory.OtherSymbol使用.NET 4.0时将返回。

除此之外,还有一个有趣的观点:.NET类库方法甚至都没有就如何处理Unicode非字符和未配对的代理字符达成共识。例如:

  • 未配对的代理字符
    • System.Text.Encoding.Unicode.GetBytes("\uDDDD");-返回替换字符{ 0xfd, 0xff}的编码,即数据被视为无效。
    • "\uDDDD".Normalize(); -引发异常,并显示消息“在索引0找到无效的Unicode代码点”,即,该数据被视为无效。
  • 非字符代码点
    • System.Text.Encoding.Unicode.GetBytes("\uFFFF");-return {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;
}
Run Code Online (Sandbox Code Playgroud)

  • 您对代理人对不公平,是的。您使用`System.Text.Encoding.Unicode`,但这只是获得`System.Text.UnicodeEncoding(bigEndian:false,byteOrderMark:true,throwOnInvalidBytes:false)'的一种便捷方法,该文档记录了替换字符。 。如果改用new System.Text.UnicodeEncoding(bigEndian:false,byteOrderMark:true,throwOnInvalidBytes:true),则会在未配对代理情况下出现异常。 (3认同)