在C#中确定未修剪字符串是否为空的最有效方法是什么?

我有一个字符串,周围可能有空白字符,我想检查它是否基本上是空的。

有很多方法可以做到这一点:

1 if (myString.Trim().Length == 0) 2 if (myString.Trim() == "") 3 if (myString.Trim().Equals("")) 4 if (myString.Trim() == String.Empty) 5 if (myString.Trim().Equals(String.Empty)) 

我知道这通常是一个过早优化的明显案例,但我很好奇,并且有可能做到这一点足以产生性能影响。

那么这些中最有效的方法是哪一种?

有没有更好的方法我没想过?


编辑:此问题的访问者注意事项:

  1. 对这个问题进行了一些非常详细的调查 – 特别是来自Andy和Jon Skeet。

  2. 如果你在搜索某些内容时偶然发现了这个问题,那么至少阅读Andy和Jon的post是非常值得的。

似乎有一些非常有效的方法, 有效的方法取决于我需要处理的字符串的内容。

如果我无法预测字符串(在我的情况下我不能预测),Jon的IsEmptyOrWhiteSpace方法通常会更快。

谢谢大家的意见。 我将选择安迪的答案作为“正确”的答案仅仅是因为他应该为他所付出的努力赢得声誉,而Jon已经拥有了十亿美元的声誉。

编辑:新测试:

 Test orders: x. Test name Ticks: xxxxx //Empty String Ticks: xxxxx //two space Ticks: xxxxx //single letter Ticks: xxxxx //single letter with space Ticks: xxxxx //long string Ticks: xxxxx //long string with space 1. if (myString.Trim().Length == 0) ticks: 4121800 ticks: 7523992 ticks: 17655496 ticks: 29312608 ticks: 17302880 ticks: 38160224 2. if (myString.Trim() == "") ticks: 4862312 ticks: 8436560 ticks: 21833776 ticks: 32822200 ticks: 21655224 ticks: 42358016 3. if (myString.Trim().Equals("")) ticks: 5358744 ticks: 9336728 ticks: 18807512 ticks: 30340392 ticks: 18598608 ticks: 39978008 4. if (myString.Trim() == String.Empty) ticks: 4848368 ticks: 8306312 ticks: 21552736 ticks: 32081168 ticks: 21486048 ticks: 41667608 5. if (myString.Trim().Equals(String.Empty)) ticks: 5372720 ticks: 9263696 ticks: 18677728 ticks: 29634320 ticks: 18551904 ticks: 40183768 6. if (IsEmptyOrWhitespace(myString)) //See John Skeet's Post for algorithm ticks: 6597776 ticks: 9988304 ticks: 7855664 ticks: 7826296 ticks: 7885200 ticks: 7872776 7. is (string.IsNullOrEmpty(myString.Trim()) //Cloud's suggestion ticks: 4302232 ticks: 10200344 ticks: 18425416 ticks: 29490544 ticks: 17800136 ticks: 38161368 

并使用的代码:

 public void Main() { string res = string.Empty; for (int j = 0; j <= 5; j++) { string myString = ""; switch (j) { case 0: myString = ""; break; case 1: myString = " "; break; case 2: myString = "x"; break; case 3: myString = "x "; break; case 4: myString = "this is a long string for testing triming empty things."; break; case 5: myString = "this is a long string for testing triming empty things. "; break; } bool result = false; Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i <= 100000; i++) { result = myString.Trim().Length == 0; } sw.Stop(); res += "ticks: " + sw.ElapsedTicks + Environment.NewLine; } Console.ReadKey(); //break point here to get the results } 

(编辑:有关该方法的不同微优化的基准,请参见post的底部)

不要修剪它 – 这可能会创建一个你实际上并不需要的新字符串。 相反,请查看字符串中是否有任何不是空格的字符(无论您想要什么样的定义)。 例如:

 public static bool IsEmptyOrWhitespace(string text) { // Avoid creating iterator for trivial case if (text.Length == 0) { return true; } foreach (char c in text) { // Could use Char.IsWhiteSpace(c) instead if (c==' ' || c=='\t' || c=='\r' || c=='\n') { continue; } return false; } return true; } 

如果textnull您可能还会考虑您希望该方法执行的操作。

可能进一步微观优化以进行试验:

  • foreach比使用下面的for循环更快还是更慢? 请注意,使用for循环,您可以在开始时删除“ if (text.Length==0) ”测试。

     for (int i = 0; i < text.Length; i++) { char c = text[i]; // ... 
  • 与上面相同,但是提升Length呼叫。 请注意,这对于普通数组来说并不好,但可能对字符串很有用。 我没有测试过。

     int length = text.Length; for (int i = 0; i < length; i++) { char c = text[i]; 
  • 在循环体中,我们得到的和之间有什么不同(速度):

     if (c != ' ' && c != '\t' && c != '\r' && c != '\n') { return false; } 
  • 开关/箱子会更快吗?

     switch (c) { case ' ': case '\r': case '\n': case '\t': return false; } 

更新修剪行为

我一直在研究Trim如何像这样高效。 似乎Trim只会在需要时创建一个新字符串。 如果它可以返回this""它将:

 using System; class Test { static void Main() { CheckTrim(string.Copy("")); CheckTrim(" "); CheckTrim(" x "); CheckTrim("xx"); } static void CheckTrim(string text) { string trimmed = text.Trim(); Console.WriteLine ("Text: '{0}'", text); Console.WriteLine ("Trimmed ref == text? {0}", object.ReferenceEquals(text, trimmed)); Console.WriteLine ("Trimmed ref == \"\"? {0}", object.ReferenceEquals("", trimmed)); Console.WriteLine(); } } 

这意味着这个问题中的任何基准都应该使用混合数据,这一点非常重要:

  • 空字符串
  • 空白
  • 围绕文字的空白
  • 没有空格的文本

当然,这四者之间的“现实世界”平衡是无法预测的......

基准我已经对我的原始建议进行了一些基准测试,并且我的投入似乎在我投入的所有内容中获胜,这让我感到惊讶,因为其他答案的结果。 但是,我还使用text.Length ,使用text.Length ,然后反转迭代顺序,以及使用提升的长度, for foreach之间的差异进行基准测试。

基本上for循环非常快,但提升长度检查使它比foreach慢。 反转for循环方向也比foreach慢一点。 我强烈怀疑JIT在这里做了一些有趣的事情,包括删除重复的边界检查等。

代码:(请参阅我的基准博客条目 ,该框架针对此编写)

 using System; using BenchmarkHelper; public class TrimStrings { static void Main() { Test(""); Test(" "); Test(" x "); Test("x"); Test(new string('x', 1000)); Test(" " + new string('x', 1000) + " "); Test(new string(' ', 1000)); } static void Test(string text) { bool expectedResult = text.Trim().Length == 0; string title = string.Format("Length={0}, result={1}", text.Length, expectedResult); var results = TestSuite.Create(title, text, expectedResult) /* .Add(x => x.Trim().Length == 0, "Trim().Length == 0") .Add(x => x.Trim() == "", "Trim() == \"\"") .Add(x => x.Trim().Equals(""), "Trim().Equals(\"\")") .Add(x => x.Trim() == string.Empty, "Trim() == string.Empty") .Add(x => x.Trim().Equals(string.Empty), "Trim().Equals(string.Empty)") */ .Add(OriginalIsEmptyOrWhitespace) .Add(IsEmptyOrWhitespaceForLoop) .Add(IsEmptyOrWhitespaceForLoopReversed) .Add(IsEmptyOrWhitespaceForLoopHoistedLength) .RunTests() .ScaleByBest(ScalingMode.VaryDuration); results.Display(ResultColumns.NameAndDuration | ResultColumns.Score, results.FindBest()); } public static bool OriginalIsEmptyOrWhitespace(string text) { if (text.Length == 0) { return true; } foreach (char c in text) { if (c==' ' || c=='\t' || c=='\r' || c=='\n') { continue; } return false; } return true; } public static bool IsEmptyOrWhitespaceForLoop(string text) { for (int i=0; i < text.Length; i++) { char c = text[i]; if (c==' ' || c=='\t' || c=='\r' || c=='\n') { continue; } return false; } return true; } public static bool IsEmptyOrWhitespaceForLoopReversed(string text) { for (int i=text.Length-1; i >= 0; i--) { char c = text[i]; if (c==' ' || c=='\t' || c=='\r' || c=='\n') { continue; } return false; } return true; } public static bool IsEmptyOrWhitespaceForLoopHoistedLength(string text) { int length = text.Length; for (int i=0; i < length; i++) { char c = text[i]; if (c==' ' || c=='\t' || c=='\r' || c=='\n') { continue; } return false; } return true; } } 

结果:

 ============ Length=0, result=True ============ OriginalIsEmptyOrWhitespace 30.012 1.00 IsEmptyOrWhitespaceForLoop 30.802 1.03 IsEmptyOrWhitespaceForLoopReversed 32.944 1.10 IsEmptyOrWhitespaceForLoopHoistedLength 35.113 1.17 ============ Length=1, result=True ============ OriginalIsEmptyOrWhitespace 31.150 1.04 IsEmptyOrWhitespaceForLoop 30.051 1.00 IsEmptyOrWhitespaceForLoopReversed 31.602 1.05 IsEmptyOrWhitespaceForLoopHoistedLength 33.383 1.11 ============ Length=3, result=False ============ OriginalIsEmptyOrWhitespace 30.221 1.00 IsEmptyOrWhitespaceForLoop 30.131 1.00 IsEmptyOrWhitespaceForLoopReversed 34.502 1.15 IsEmptyOrWhitespaceForLoopHoistedLength 35.690 1.18 ============ Length=1, result=False ============ OriginalIsEmptyOrWhitespace 31.626 1.05 IsEmptyOrWhitespaceForLoop 30.005 1.00 IsEmptyOrWhitespaceForLoopReversed 32.383 1.08 IsEmptyOrWhitespaceForLoopHoistedLength 33.666 1.12 ============ Length=1000, result=False ============ OriginalIsEmptyOrWhitespace 30.177 1.00 IsEmptyOrWhitespaceForLoop 33.207 1.10 IsEmptyOrWhitespaceForLoopReversed 30.867 1.02 IsEmptyOrWhitespaceForLoopHoistedLength 31.837 1.06 ============ Length=1002, result=False ============ OriginalIsEmptyOrWhitespace 30.217 1.01 IsEmptyOrWhitespaceForLoop 30.026 1.00 IsEmptyOrWhitespaceForLoopReversed 34.162 1.14 IsEmptyOrWhitespaceForLoopHoistedLength 34.860 1.16 ============ Length=1000, result=True ============ OriginalIsEmptyOrWhitespace 30.303 1.01 IsEmptyOrWhitespaceForLoop 30.018 1.00 IsEmptyOrWhitespaceForLoopReversed 35.475 1.18 IsEmptyOrWhitespaceForLoopHoistedLength 40.927 1.36 

我真的不知道哪个更快; 虽然我的直觉是第一名。 但这是另一种方法:

 if (String.IsNullOrEmpty(myString.Trim())) 

myString.Trim()。Length == 0 Took:421 ms

myString.Trim()==”花了: 468毫秒

if(myString.Trim()。Equals(“”)): 515 ms

if(myString.Trim()== String.Empty) Took:484 ms

if(myString.Trim()。Equals(String.Empty)) Took:500 ms

if(string.IsNullOrEmpty(myString.Trim())) Took:437 ms

在我的测试中,它看起来像myString.Trim()。Length == 0并且令人惊讶的是,string.IsNullOrEmpty(myString.Trim())始终是最快的。 上述结果是进行10,000,000次比较的典型结果。

检查字符串的长度为零是测试空字符串的最有效方法,所以我想说数字1:

 if (myString.Trim().Length == 0) 

进一步优化这一点的唯一方法可能是避免使用编译的正则表达式进行修剪(编辑:这实际上比使用Trim()慢得多。长度)。

编辑:使用Length的建议来自FxCop指南。 我也刚刚对它进行了测试:它比空字符串快2-3倍。 然而,这两种方法仍然非常快(我们说的是纳秒) – 所以你使用哪种方法几乎不重要。 修剪是一个瓶颈,它比最后的实际比较慢几百倍。

.NET 4 Beta 2中的String.IsNullOrWhitespace也在这个空间中播放,不需要自定义编写

自从我刚开始以来,我不能发表评论,所以在这里。

 if (String.IsNullOrEmpty(myString.Trim())) 

如果myString为null,则Trim()调用将失败,因为您无法调用null( NullReferenceException )对象中的方法。

所以正确的语法是这样的:

 if (!String.IsNullOrEmpty(myString)) { string trimmedString = myString.Trim(); //do the rest of you code } else { //string is null or empty, don't bother processing it } 
 public static bool IsNullOrEmpty(this String str, bool checkTrimmed) { var b = String.IsNullOrEmpty(str); return checkTrimmed ? b && str.Trim().Length == 0 : b; }