94

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

在这篇 SO 帖子中建议C# 使用 ToUpper 更有效,因为“Microsoft 以这种方式对其进行了优化”。但我也读过这个论点,即转换 ToLower 与 ToUpper 取决于您的字符串包含更多内容,并且通常字符串包含更多小写字符,这使得 ToLower 更有效。

特别是,我想知道:

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

9 回答 9

93

由于某些文化(尤其是土耳其)的“有趣”特征,为了进行不区分大小写的比较而转换为大写或小写是不正确的。Instead, use a StringComparer with the appropriate options.

MSDN对字符串处理有一些很好的指导。您可能还想检查您的代码是否通过了土耳其测试

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

于 2008-10-24T18:28:52.890 回答
31

来自微软在 MSDN 上:

在 .NET Framework 中使用字符串的最佳实践

字符串使用建议

为什么?来自微软

将字符串规范化为大写

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

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

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

ε , Ρ , ρ

.NET 小提琴

Original: ϱ
ToUpper: Ρ
ToLower: ρ

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

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

于 2013-01-02T20:42:23.137 回答
18

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

String.Compare(strA, strB, StringComparison.OrdinalIgnoreCase) 等价于(但比)调用

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

这些比较仍然非常快。

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

于 2008-10-24T17:54:39.073 回答
12

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

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

如果你偷偷摸摸地把你的字符串加载到long数组中,你会对整个字符串进行非常快速的比较,因为它一次可以比较 4 个字节。但是,加载时间可能使其不值得。

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

于 2008-10-24T17:51:57.490 回答
5

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

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

于 2008-10-24T17:56:35.013 回答
4

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

于 2008-10-24T17:56:26.433 回答
3

我想要一些关于这方面的实际数据,所以我提取了以or失败的两个字节字符的完整列表。然后我在下面运行了这个测试: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
于 2020-12-25T19:31:39.353 回答
0

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

于 2008-10-24T17:52:22.887 回答
0

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

于 2010-06-04T15:48:48.083 回答