大写字符串是否总是与原始字符串长度相同?

10
  • 无论使用哪种文化,Unicode 大写字符串的长度是否始终与原始字符串的长度相同?

  • 无论使用哪种文化,Unicode 小写字符串的长度是否始终与原始字符串的长度相同?

换句话说,在 C# 中,以下内容是否正确?

text.ToUpper(CultureInfo.CurrentCulture).Length == text.Length
text.ToLower(CultureInfo.CurrentCulture).Length == text.Length

请注意,我不关心字节数量: 关于此问题的答案 已经被回答了。


2
德语单词“gemäß”的常见大写形式为“GEMÄSS”。 - Jongware
1
请注意,在.NET中,长度以UCS-2字符的长度表示,而不是代码点的长度。代码点是实际的Unicode字符。因此,对于i18n应用程序来说,字符串长度并不是非常有意义的。 - usr
5
作为一条经验法则,对于任何问题“我是否可以假设Unicode字符串中的X始终成立”,答案通常是“不行” :) - jalf
1
我相信可以创建一种文化,改变长度(或其他任何东西)是可能的。 - hyde
3
.NET 的大写转换有点问题。""effect".ToUpper() 会产生 "EffECT"。依赖这些 bug 永远不会被修复是不明智的。 - Hans Passant
显示剩余6条评论
3个回答

5
问题的答案就是Unicode标准所关注的“否”和“是”。
例如,在转换为大写时,“ß” U+00DF LATIN SMALL LETTER SHARP S 通过Unicode映射规则被映射为两个字符“SS”。虽然可以将其映射到单个字符“ẞ” U+1E9E LATIN CAPITAL LETTER SHARP S,但这不是默认情况(也不常见)。另一个例子是“fi” U+FB01 LATIN SMALL LIGATURE FI 被映射为“FI”。
在相反的方向上,没有默认映射会更改字符数。请参见Character Properties, Case Mappings & Names FAQ,该页面链接到包含所有偏离简单一对一映射的文件SpecialCasing.txt。那里唯一可能使小写字符串与大写原始字符串不同的规则是与立陶宛语实践相关的一些可选规则。

好的回答。目前,.NET似乎没有实现这些规则,至少在我测试的文化中没有。 - usr

3

我可以给出一个部分答案。对于长度为2的所有字符串(大约有40亿个),并且对于德国文化(de-DE),你的断言是成立的:

    static unsafe void TestUnicodeLength2()
    {
        Parallel.For(char.MinValue, char.MaxValue + 1, charVal =>
        {
            var firstChar = checked((char)charVal);
            var buffer = new string(firstChar, 2);

            fixed (char* bufferPtr = buffer)
            {
                var currentCulture = CultureInfo.CurrentCulture;

                for (int i = char.MinValue; i <= char.MaxValue; i++)
                {
                    bufferPtr[1] = checked((char)i);

                    var toLower = buffer.ToLower(currentCulture);
                    if (toLower.Length != buffer.Length)
                    {
                        Console.WriteLine(buffer + " => " + toLower);
                        Debugger.Break();
                    }

                    var toUpper = buffer.ToUpper(currentCulture);
                    if (toUpper.Length != buffer.Length)
                    {
                        Console.WriteLine(buffer + " => " + toUpper);
                        Debugger.Break();
                    }
                }
            }
        });
    }

这个运行大约需要2分钟。

我认为这是相当有力的证据,因为通过测试所有可能的两个字符的组合,我们自动测试了所有现有的代码点和所有奇怪的组合,这些组合是任何人都不会想到的。

更新:后来我对随机字符串(每个256个字符长度)进行了类似的测试,总长度为2560亿个字符。这些断言仍然成立。


好的回答,但值得澄清的是(1)你谈论的是.NET的特性,而不是Unicode(当字面上阅读时,问题是关于Unicode的,但显然是在.NET上下文中,所以答案仍然相关),以及(2)这是基于.NET如何今天运行的,这可能是由于不完整的实现或简单的错误。尽管如此,实际上只需运行测试并查看它在此处和现在的实际行为肯定是值得的。重要的是要记住它没有告诉我们什么。 - jalf
@jalf 你说得完全正确。这只是一个非常片面的答案,如果其正确性不是100%重要的话,我才会依赖它的结果。我永远不会基于这个结果做出关于安全或生死的决定。 - usr

3

目前情况确实如此。.NET开发人员已决定使用Windows APIs进行Unicode编码,但这些API不支持更改字符串长度的大小写转换。

然而,这并不是.NET团队保证它会一直保持不变。如果Windows推出了支持这些转换的新API,.NET可能会更新以使用它们。

请参见Microsoft Connect上的System.String.ToUpper() doesn't follow UNICODE SpecialCasing


1
所以它现在是真的,但应该,并且有一天可能会变成假的。 - Jongware

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接