【问题标题】:Upper vs Lower Case大写与小写
【发布时间】:2010-09-19 01:47:02
【问题描述】:

在进行不区分大小写的比较时,将字符串转换为大写还是小写更有效?这还重要吗?

建议in this SO post 使用 ToUpper 的 C# 更高效,因为“Microsoft 以这种方式对其进行了优化”。但我也读过this argument,转换 ToLower 与 ToUpper 取决于您的字符串包含更多内容,并且通常字符串包含更多小写字符,这使 ToLower 更高效。

我特别想知道:

  • 有没有办法优化 ToUpper 或 ToLower 以使一个比另一个快?
  • 在大小写字符串之间进行不区分大小写的比较是否更快,为什么?
  • 是否存在任何一种情况明显优于另一种情况的编程环境(例如 C、C#、Python 等),为什么?

【问题讨论】:

    标签: string language-agnostic uppercase


    【解决方案1】:

    由于某些文化(尤其是土耳其)的“有趣”特征,为了进行不区分大小写的比较而转换为大写或小写是不正确的。相反,使用StringComparer使用适当的选项。

    MSDN 有一些关于字符串处理的great guidelines。您可能还想检查您的代码是否通过the Turkey test

    编辑:注意 Neil 关于 ordinal 不区分大小写比较的评论。这整个领域都很模糊:(

    【讨论】:

    • 是的 StringComparer 很棒,但问题没有得到解答......在您无法使用 StringComparer 的情况下,例如针对字符串的 swtich 语句;我应该在开关中使用 ToUpper 还是 ToLower?
    • 使用 StringComparer 和 "if"/"else" 而不是使用 ToUpper 或 ToLower。
    • John,我知道转换为小写是不正确的,但我没有听说转换为大写是不正确的。你能提供一个例子或参考吗?您链接到的 MSDN 文章说:“使用 OrdinalIgnoreCase 进行的比较在行为上是两个调用的组合:在两个字符串参数上调用 ToUpperInvariant,并进行 Ordinal 比较。”在标题为“序数字符串操作”的部分中,它在代码中重申了这一点。
    • @Neil:有趣,我没看到那一点。对于 ordinal 不区分大小写的比较,我想这已经足够公平了。毕竟,它必须选择something。对于文化敏感、不区分大小写的比较,我认为仍有一些奇怪行为的空间。将在答案中指出您的评论...
    • @Triynko:我认为主要关注正确性很重要,快速得到错误答案通常并不比得到正确答案更好(有时甚至更糟)慢慢答错。
    【解决方案2】:

    来自 MSDN 上的Microsoft

    Best Practices for Using Strings in the .NET Framework

    字符串使用建议

    为什么?来自Microsoft

    将字符串标准化为大写

    有一小部分字符在转换为小写时无法往返。

    这种不能往返的角色的例子是什么?

    • 开始:希腊 Rho 符号 (U+03f1) ϱ
    • 大写: 大写希腊字母 Rho (U+03a1) Ρ
    • 小写: 小写希腊字母 Rho (U+03c1) ρ

    ϱ , Ρ , ρ

    .NET Fiddle

    Original: ϱ
    ToUpper: Ρ
    ToLower: ρ
    

    这就是为什么,如果您想进行不区分大小写的比较,请将字符串转换为大写,而不是小写。

    因此,如果您必须选择一个,请选择大写

    【讨论】:

    • 回到原来的问题的答案:有些语言知道一个大写变体不止一个小写变体。除非您知道何时使用哪种表示的规则(希腊语中的另一个示例:小 sigma 字母,您在单词开头或中间使用 σ,在单词结尾使用 ς(请参阅en.wikipedia.org/wiki/Sigma),您无法安全地转换回到小写变体。
    • 实际上德语 'ß' 怎么样,如果你调用 ToUpper() 它将在许多系统上变成 'SS'。所以这实际上也不是往返的。
    • 如果微软优化了执行大写比较的代码,是因为大写字母的 ASCII 码只有 65 - 90 两个数字,而小写字母 97 -122 的 ASCII 码包含 3 个数字(需要更多处理)
    • 应该注意的是,“ϱ”和“ς”都从ToUpperInvariant()返回,所以还是很高兴看到为什么大写优于小写的真实例子
    • ϱ != ρ,但是如果你使用大写字母,那么它不会本质上都变成 Ρ,然后比较 Ρ == Ρ,即使 ϱ != ρ 也是如此?
    【解决方案3】:

    根据MSDN,传递字符串并告诉比较忽略大小写更有效:

    String.Compare(strA, strB, StringComparison.OrdinalIgnoreCase) 相当于(但比)调用

    String.Compare(ToUpperInvariant(strA), ToUpperInvariant(strB), StringComparison.Ordinal)。

    这些比较还是很快的。

    当然,如果你一遍又一遍地比较一个字符串,那么这可能不成立。

    【讨论】:

      【解决方案4】:

      基于具有更多小写条目的字符串,ToLower 理论上应该更快(很多比较,但很少分配)。

      在 C 中,或者当使用每个字符串的可单独访问的元素(例如 C 字符串或 C++ 中的 STL 字符串类型)时,它实际上是一个字节比较 - 所以比较 UPPERlower 没有什么不同。

      如果您偷偷摸摸地将字符串加载到long 数组中,那么您将获得对整个字符串的非常快速的比较,因为它一次可以比较 4 个字节。但是,加载时间可能不值得。

      为什么您需要知道哪个更快?除非你在做一个度量对接负载的比较,否则运行几个周期的速度与整体执行的速度无关,听起来像是过早的优化:)

      【讨论】:

      • 回答为什么我需要知道哪个更快的问题:我不需要知道,我只是想知道。 :) 这只是看到有人提出声明(例如“比较大写字符串更快!”)并想知道它是否真的是真的和/或他们为什么提出声明的情况。
      • 这是有道理的——我也对这样的东西永远好奇:)
      • 使用 C 字符串,将 st 转换为 long 数组,这样如果数组相等,则字符串相等,您必须向下走 s 和 t 直到找到终止 @ 987654326@ 字符(否则您可能会比较字符串末尾的垃圾,这可能是调用未定义行为的非法内存访问)。但是,为什么不一一遍历角色时进行比较呢?使用 C++ 字符串,您可能可以获得长度和 .c_str(),转换为 long * 并比较长度为 .size() - .size()%(sizeof long) 的前缀。对我来说看起来有点可疑。
      • @JonasKölker - 将字符串加载到longs 的数组中只是为了比较 将是愚蠢的。但是,如果您“经常”这样做-我可以看到一个 possible 参数来完成它。
      • 请不要试图“修正”语法——尤其是去掉“STL's”上不是复数的撇号:它是所有格
      【解决方案5】:

      微软优化了ToUpperInvariant(),而不是ToUpper()。不同之处在于不变量对文化更友好。如果您需要对文化可能不同的字符串进行不区分大小写的比较,请使用 Invariant,否则不变量转换的性能无关紧要。

      我不能说是 ToUpper() 还是 ToLower() 更快。我从来没有尝试过,因为我从来没有遇到过性能如此重要的情况。

      【讨论】:

      • 如果微软优化了执行大写比较的代码,是因为大写字母的 ASCII 码只有 65 - 90 两个数字,而小写字母 97 -122 的 ASCII 码包含 3 个数字(需要更多处理) ?
      • @Medo 我不记得优化的确切原因,但 2 位和 3 位数字几乎肯定不是原因,因为所有字母都存储为二进制数字,所以十进制数字实际上没有意义基于它们的存储方式。
      【解决方案6】:

      如果您在 C# 中进行字符串比较,使用 .Equals() 比将两个字符串都转换为大写或小写要快得多。使用 .Equals() 的另一大优点是不会为 2 个新的大写/小写字符串分配更多内存。

      【讨论】:

      • 另外,如果你选择了正确的选项,它实际上会给你正确的结果:)
      • @JonSkeet 在您的回答中,您建议使用StringComparer。就性能而言,这是否优于使用Equals(...)
      • @Abdul:我没有测量它们的性能,也不想猜测。
      【解决方案7】:

      我想要一些关于这方面的实际数据,所以我提取了两个字节的 the full list ToLowerToUpper 失败的字符。然后我在下面运行了这个测试:

      using System;
      
      class Program {
         static void Main() {
            char[][] pairs = {
      new[]{'\u00E5','\u212B'},new[]{'\u00C5','\u212B'},new[]{'\u0399','\u1FBE'},
      new[]{'\u03B9','\u1FBE'},new[]{'\u03B2','\u03D0'},new[]{'\u03B5','\u03F5'},
      new[]{'\u03B8','\u03D1'},new[]{'\u03B8','\u03F4'},new[]{'\u03D1','\u03F4'},
      new[]{'\u03B9','\u1FBE'},new[]{'\u0345','\u03B9'},new[]{'\u0345','\u1FBE'},
      new[]{'\u03BA','\u03F0'},new[]{'\u00B5','\u03BC'},new[]{'\u03C0','\u03D6'},
      new[]{'\u03C1','\u03F1'},new[]{'\u03C2','\u03C3'},new[]{'\u03C6','\u03D5'},
      new[]{'\u03C9','\u2126'},new[]{'\u0392','\u03D0'},new[]{'\u0395','\u03F5'},
      new[]{'\u03D1','\u03F4'},new[]{'\u0398','\u03D1'},new[]{'\u0398','\u03F4'},
      new[]{'\u0345','\u1FBE'},new[]{'\u0345','\u0399'},new[]{'\u0399','\u1FBE'},
      new[]{'\u039A','\u03F0'},new[]{'\u00B5','\u039C'},new[]{'\u03A0','\u03D6'},
      new[]{'\u03A1','\u03F1'},new[]{'\u03A3','\u03C2'},new[]{'\u03A6','\u03D5'},
      new[]{'\u03A9','\u2126'},new[]{'\u0398','\u03F4'},new[]{'\u03B8','\u03F4'},
      new[]{'\u03B8','\u03D1'},new[]{'\u0398','\u03D1'},new[]{'\u0432','\u1C80'},
      new[]{'\u0434','\u1C81'},new[]{'\u043E','\u1C82'},new[]{'\u0441','\u1C83'},
      new[]{'\u0442','\u1C84'},new[]{'\u0442','\u1C85'},new[]{'\u1C84','\u1C85'},
      new[]{'\u044A','\u1C86'},new[]{'\u0412','\u1C80'},new[]{'\u0414','\u1C81'},
      new[]{'\u041E','\u1C82'},new[]{'\u0421','\u1C83'},new[]{'\u1C84','\u1C85'},
      new[]{'\u0422','\u1C84'},new[]{'\u0422','\u1C85'},new[]{'\u042A','\u1C86'},
      new[]{'\u0463','\u1C87'},new[]{'\u0462','\u1C87'}
            };
            int upper = 0, lower = 0;
            foreach (char[] pair in pairs) {
               Console.Write(
                  "U+{0:X4} U+{1:X4} pass: ",
                  Convert.ToInt32(pair[0]),
                  Convert.ToInt32(pair[1])
               );
               if (Char.ToUpper(pair[0]) == Char.ToUpper(pair[1])) {
                  Console.Write("ToUpper ");
                  upper++;
               } else {
                  Console.Write("        ");
               }
               if (Char.ToLower(pair[0]) == Char.ToLower(pair[1])) {
                  Console.Write("ToLower");
                  lower++;
               }
               Console.WriteLine();
            }
            Console.WriteLine("upper pass: {0}, lower pass: {1}", upper, lower);
         }
      }
      

      结果如下。注意我还测试了Invariant 版本,结果是 完全一样。有趣的是,其中一对都失败了。但基于此 ToUpper 是最佳选择

      U+00E5 U+212B pass:         ToLower
      U+00C5 U+212B pass:         ToLower
      U+0399 U+1FBE pass: ToUpper
      U+03B9 U+1FBE pass: ToUpper
      U+03B2 U+03D0 pass: ToUpper
      U+03B5 U+03F5 pass: ToUpper
      U+03B8 U+03D1 pass: ToUpper
      U+03B8 U+03F4 pass:         ToLower
      U+03D1 U+03F4 pass:
      U+03B9 U+1FBE pass: ToUpper
      U+0345 U+03B9 pass: ToUpper
      U+0345 U+1FBE pass: ToUpper
      U+03BA U+03F0 pass: ToUpper
      U+00B5 U+03BC pass: ToUpper
      U+03C0 U+03D6 pass: ToUpper
      U+03C1 U+03F1 pass: ToUpper
      U+03C2 U+03C3 pass: ToUpper
      U+03C6 U+03D5 pass: ToUpper
      U+03C9 U+2126 pass:         ToLower
      U+0392 U+03D0 pass: ToUpper
      U+0395 U+03F5 pass: ToUpper
      U+03D1 U+03F4 pass:
      U+0398 U+03D1 pass: ToUpper
      U+0398 U+03F4 pass:         ToLower
      U+0345 U+1FBE pass: ToUpper
      U+0345 U+0399 pass: ToUpper
      U+0399 U+1FBE pass: ToUpper
      U+039A U+03F0 pass: ToUpper
      U+00B5 U+039C pass: ToUpper
      U+03A0 U+03D6 pass: ToUpper
      U+03A1 U+03F1 pass: ToUpper
      U+03A3 U+03C2 pass: ToUpper
      U+03A6 U+03D5 pass: ToUpper
      U+03A9 U+2126 pass:         ToLower
      U+0398 U+03F4 pass:         ToLower
      U+03B8 U+03F4 pass:         ToLower
      U+03B8 U+03D1 pass: ToUpper
      U+0398 U+03D1 pass: ToUpper
      U+0432 U+1C80 pass: ToUpper
      U+0434 U+1C81 pass: ToUpper
      U+043E U+1C82 pass: ToUpper
      U+0441 U+1C83 pass: ToUpper
      U+0442 U+1C84 pass: ToUpper
      U+0442 U+1C85 pass: ToUpper
      U+1C84 U+1C85 pass: ToUpper
      U+044A U+1C86 pass: ToUpper
      U+0412 U+1C80 pass: ToUpper
      U+0414 U+1C81 pass: ToUpper
      U+041E U+1C82 pass: ToUpper
      U+0421 U+1C83 pass: ToUpper
      U+1C84 U+1C85 pass: ToUpper
      U+0422 U+1C84 pass: ToUpper
      U+0422 U+1C85 pass: ToUpper
      U+042A U+1C86 pass: ToUpper
      U+0463 U+1C87 pass: ToUpper
      U+0462 U+1C87 pass: ToUpper
      upper pass: 46, lower pass: 8
      

      【讨论】:

        【解决方案8】:

        这真的不应该重要。对于 ASCII 字符,这绝对无关紧要 - 只是一些比较和任一方向的翻转。 Unicode 可能稍微复杂一些,因为有些字符会以奇怪的方式改变大小写,但实际上应该没有任何区别,除非您的文本中充满了这些特殊字符。

        【讨论】:

          【解决方案9】:

          如果你做对了,如果你转换成小写,应该会有一个很小的、微不足道的速度优势,但正如许多人暗示的那样,这是依赖于文化的,并且不是在函数中继承,而是在你转换的字符串中(很多小写字母意味着对内存的分配很少)——如果您有一个包含大量大写字母的字符串,则转换为大写会更快。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-06-11
            • 2015-01-09
            • 2017-07-18
            • 2012-11-13
            • 2017-07-26
            • 2011-10-31
            • 2016-05-14
            • 1970-01-01
            相关资源
            最近更新 更多