【问题标题】:Is uppercase string always of the same length as the original one?大写字符串是否总是与原始字符串的长度相同?
【发布时间】:2013-12-16 13:41:18
【问题描述】:
  • 无论使用什么文化,unicode 大写字符串的长度是否总是与原始字符串的长度相同?

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

换句话说,在 C# 中以下是正确的吗?

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

请注意,我对字节数不感兴趣:the question about that 已回答。

【问题讨论】:

  • 澄清一下,当你说“长度”时,你真正的意思是“.NET 的System.StringLength 属性”。因为字符串类的行为通常与 Unicode 指定的行为完全不同(并且有许多不同的方法可以测量字符串的“长度”)(具体而言,System.String.Length 测量一个字符串中 UTF-16 代码单元的数量字符串,而 不是 Unicode 代码点的数量(这将是“字符串长度”在语义上更正确的度量)
  • 德语单词“gemäß”的常见大写版本是“GEMASS”。
  • 请注意,.NET 字符中的长度是 UCS-2 字符中的长度,而不是代码点中的长度。代码点是实际的 unicode 字符。所以 string.Length 对 i18n 应用来说意义不大。
  • 另外,作为一个好的经验法则,对于任何问题“我可以假设 X 对于任何 Unicode 字符串都为真”,答案是“否”:)
  • .NET 大写有点问题。 “”效果“。ToUpper() 产生“效果”。依赖那些永远得到修复的错误是不明智的。

标签: c# unicode


【解决方案1】:

就 Unicode 标准而言,问题的答案是“否”和“是”。

例如,在转换为大写时,“ß” U+00DF LATIN SMALL LETTER SHARP S 通过 Unicode 映射规则映射为两个字符“SS”。可以将其映射到单个字符“ẞ” U+1E9E 拉丁大写字母 SHARP S,但这不是默认设置(而且根本不常见)。另一个例子是“fi” U+FB01 LATIN SMALL LIGATURE FI 映射到“FI”。

在相反的方向,没有会改变字符数的默认映射。请参阅Character Properties, Case Mappings & Names FAQ,它链接到文件SpecialCasing.txt,其中包含与简单的一对一映射的所有偏差。使小写字符串与大写原始字符串不同的唯一规则是一些与立陶宛惯例相关的可选规则。

【讨论】:

  • 好答案。 .NET 目前似乎没有实现任何这些规则,至少在我测试的文化中没有。
【解决方案2】:

我可以给出部分答案。对于所有长度为 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 分钟。

我认为这是断言始终成立的有力证据,因为通过测试两个字符的所有可能组合,我们会自动测试所有存在的代码点以及所有没人会想到的奇怪组合。

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

【讨论】:

  • 很好的答案,但值得澄清的是(1)您在谈论 .NET 的特性,而不是 Unicode(从字面上看,问题是关于 Unicode,但在 . NET 上下文,因此答案仍然相关)),并且(2)这是基于 .NETtoday 的行为方式,这可能是由于实现不完整,或者只是简单的错误。尽管如此,实际上只是运行测试并查看它实际上在此时此地的表现是绝对值得的。记住它没有告诉我们的内容也很重要。
  • @jalf 你是 100% 正确的。这是一个非常部分的答案,我只会依赖它的结果,以防它的正确性不是 100% 重要。我永远不会根据这个结果来决定安全或生死。
【解决方案3】:

目前,这是真的。 .NET 开发人员决定使用适用于 Unicode 的 Windows API,这些 API 不支持更改字符串长度的大小写转换。

不过,.NET 团队并不能保证它会一直如此。如果 Windows 获得支持这些转换的新 API,.NET 可能会更新以使用它们。

在 Microsoft Connect 上查看 System.String.ToUpper() doesn't follow UNICODE SpecialCasing

【讨论】:

  • 所以它是真正的 ATM 但应该是,并且可能有一天变成,假的。
猜你喜欢
  • 2011-10-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-23
  • 1970-01-01
  • 2021-06-25
  • 2021-05-26
  • 2012-12-23
相关资源
最近更新 更多