哪个对编译器更有效,以及检查字符串是否为空白的最佳实践?
- 检查字符串的长度是否== 0
- 检查字符串是否为空(strVar == "")
另外,答案是否取决于语言?
哪个对编译器更有效,以及检查字符串是否为空白的最佳实践?
另外,答案是否取决于语言?
是的,这取决于语言,因为字符串存储因语言而异。
Length = 0
.[0] == 0
..IsNullOrEmpty
:。等等。
在使用 C 风格(空终止)字符串的语言中,比较起来""
会更快。这是一个 O(1) 操作,而 C 风格字符串的长度是 O(n)。
在将长度存储为字符串对象的一部分的语言(C#、Java、...)中,检查长度也是 O(1)。在这种情况下,直接检查长度会更快,因为它避免了构造新空字符串的开销。
在使用 C 风格(空终止)字符串的语言中,与 "" 相比会更快
实际上,最好检查字符串中的第一个字符是否为 '\0':
char *mystring;
/* do something with the string */
if ((mystring != NULL) && (mystring[0] == '\0')) {
/* the string is empty */
}
在 Perl 中还有第三个选项,即字符串未定义。这与 C 中的 NULL 指针有点不同,只是因为访问未定义字符串时不会出现分段错误。
在 .Net 中:
string.IsNullOrEmpty( nystr );
字符串可以为空,因此 .Length 有时会抛出 NullReferenceException
String.IsNullOrEmpty()
仅适用于 .net 2.0 及更高版本,对于 .net 1/1.1,我倾向于使用:
if (inputString == null || inputString == String.Empty)
{
// String is null or empty, do something clever here. Or just expload.
}
我使用 String.Empty 而不是 "" 因为 "" 将创建一个对象,而 String.Empty 不会 - 我知道它是一些小而微不足道的东西,但是当我不需要它们时,我仍然宁愿不创建对象!(来源)
对于 C 字符串,
if (s[0] == 0)
会比任何一个都快
if (strlen(s) == 0)
或者
if (strcmp(s, "") == 0)
因为您将避免函数调用的开销。
假设您的问题是 .NET:
如果您想验证您的字符串是否为空,请使用 IsNullOrEmpty,如果您已经知道您的字符串不为空,例如在检查 TextBox.Text 等时,请不要使用 IsNullOrEmpty,然后提出您的问题。
因此,在我看来,String.Length 的性能不如字符串比较。
我对它进行了测试(我也用 C# 进行了测试,结果相同):
Module Module1
Sub Main()
Dim myString = ""
Dim a, b, c, d As Long
Console.WriteLine("Way 1...")
a = Now.Ticks
For index = 0 To 10000000
Dim isEmpty = myString = ""
Next
b = Now.Ticks
Console.WriteLine("Way 2...")
c = Now.Ticks
For index = 0 To 10000000
Dim isEmpty = myString.Length = 0
Next
d = Now.Ticks
Dim way1 = b - a, way2 = d - c
Console.WriteLine("way 1 took {0} ticks", way1)
Console.WriteLine("way 2 took {0} ticks", way2)
Console.WriteLine("way 1 took {0} ticks more than way 2", way1 - way2)
Console.Read()
End Sub
End Module
结果:
Way 1...
Way 2...
way 1 took 624001 ticks
way 2 took 468001 ticks
way 1 took 156000 ticks more than way 2
这意味着比较不仅仅是字符串长度检查。
读完这篇文章后,我做了一个小实验,得出了两个截然不同且有趣的发现。
考虑以下。
strInstallString "1" string
以上是从 Visual Studio 调试器的本地窗口复制而来的。以下三个示例都使用相同的值。
if (strInstallString == "") === if (strInstallString == string.Empty)
以下是 Visual Studio 2013 调试器的反汇编窗口中显示的这两种基本相同情况的代码。
if ( strInstallString == "" )
003126FB mov edx,dword ptr ds:[31B2184h]
00312701 mov ecx,dword ptr [ebp-50h]
00312704 call 59DEC0B0 ; On return, EAX = 0x00000000.
00312709 mov dword ptr [ebp-9Ch],eax
0031270F cmp dword ptr [ebp-9Ch],0
00312716 sete al
00312719 movzx eax,al
0031271C mov dword ptr [ebp-64h],eax
0031271F cmp dword ptr [ebp-64h],0
00312723 jne 00312750
if ( strInstallString == string.Empty )
00452443 mov edx,dword ptr ds:[3282184h]
00452449 mov ecx,dword ptr [ebp-50h]
0045244C call 59DEC0B0 ; On return, EAX = 0x00000000.
00452451 mov dword ptr [ebp-9Ch],eax
00452457 cmp dword ptr [ebp-9Ch],0
0045245E sete al
00452461 movzx eax,al
00452464 mov dword ptr [ebp-64h],eax
00452467 cmp dword ptr [ebp-64h],0
0045246B jne 00452498
if (strInstallString == string.Empty) 没有显着差异
if ( strInstallString.Length == 0 )
003E284B mov ecx,dword ptr [ebp-50h]
003E284E cmp dword ptr [ecx],ecx
003E2850 call 5ACBC87E ; On return, EAX = 0x00000001.
003E2855 mov dword ptr [ebp-9Ch],eax
003E285B cmp dword ptr [ebp-9Ch],0
003E2862 setne al
003E2865 movzx eax,al
003E2868 mov dword ptr [ebp-64h],eax
003E286B cmp dword ptr [ebp-64h],0
003E286F jne 003E289C
从以上由.NET Framework 4.5 版的NGEN 模块生成的机器代码清单,我得出以下结论。
对空字符串文字和 System.string 类上的静态 string.Empty 属性进行相等性测试,就所有实际目的而言,都是相同的。两个代码片段之间的唯一区别是第一个移动指令的来源,两者都是相对于 ds 的偏移量,这意味着它们都引用了烘焙常量。
对空字符串(作为文字或 string.Empty 属性)进行相等性测试,会设置一个双参数函数调用,它通过返回零来表示不相等。我基于几个月前执行的其他测试得出这个结论,在这些测试中,我遵循了一些我自己的代码,跨越了托管/非托管的鸿沟并返回。在所有情况下,任何需要两个或更多参数的调用都将第一个参数放在寄存器 ECX 中,将第二个参数放在寄存器 EDX 中。我不记得后来的论点是如何通过的。尽管如此,呼叫设置看起来更像 __fastcall 而不是 __stdcall。同样,预期的返回值总是出现在寄存器 EAX 中,这几乎是通用的。
测试字符串的长度会建立一个单参数函数调用,它返回 1(在寄存器 EAX 中),这恰好是被测试字符串的长度。
鉴于立即可见的机器代码几乎相同,我能想象到的唯一原因是字符串相等性优于Shinny报告的字符串长度的唯一原因是执行比较的双参数函数明显更好比从字符串实例中读取长度的单参数函数进行了优化。
结论
作为一个原则问题,我避免将空字符串作为文字进行比较,因为空字符串文字在源代码中可能显得模棱两可。为此,我的 .NET 帮助程序类早就将空字符串定义为常量。尽管我使用string.Empty进行直接的内联比较,但该常量仍可用于定义其他值为空字符串的常量,因为不能将常量指定为string.Empty作为其值。
这个练习一劳永逸地解决了我可能对与string.Empty或我的助手类定义的常量进行比较的成本(如果有的话)的任何担忧。
然而,它也提出了一个令人费解的问题来取代它;为什么与string.Empty进行比较比测试字符串的长度更有效?或者由于循环的实现方式,Shinny 使用的测试是否无效?(我觉得这很难相信,但话说回来,我以前也被愚弄过,我相信你也一样!)
我一直认为system.string对象是计数字符串,基本上类似于我们从 COM 早就知道的长期建立的基本字符串 (BSTR)。
实际上,IMO 最好的确定方法是字符串类的 IsNullOrEmpty() 方法。
http://msdn.microsoft.com/en-us/library/system.string.isnullorempty。
更新:我假设.Net,在其他语言中,这可能会有所不同。
在这种情况下,直接检查长度会更快,因为它避免了构造新空字符串的开销。
@DerekPark:这并不总是正确的。"" 是一个字符串文字,所以在 Java 中,它几乎肯定已经被实习了。
@内森
实际上,最好检查字符串中的第一个字符是否为 '\0':
我几乎提到了这一点,但最终将其忽略了,因为strcmp()
使用空字符串调用并直接检查字符串中的第一个字符都是 O(1)。您基本上只需为额外的函数调用付费,这非常便宜。但是,如果您真的需要绝对的最佳速度,请务必进行直接的 first-char-to-0 比较。
老实说,我总是使用strlen() == 0
,因为我从来没有写过一个程序,这实际上是一个可测量的性能问题,我认为这是表达检查的最易读的方式。
同样,如果不了解语言,就无法分辨。
但是,我建议您选择对后续维护程序员最有意义的技术,并且必须维护您的工作。
我建议编写一个明确执行您想要的功能的函数,例如
#define IS_EMPTY(s) ((s)[0]==0)
或可比的。现在毫无疑问,您正在检查。