检查字符串内容?字符串长度与空字符串
-
08-06-2019 - |
题
对于编译器和检查字符串是否为空的最佳实践,哪一个更有效?
- 检查字符串长度是否==0
- 检查字符串是否为空(strVar == "")
另外,答案取决于语言吗?
解决方案
是的,这取决于语言,因为字符串存储因语言而异。
- 帕斯卡类型字符串:
Length = 0
. - C 风格字符串:
[0] == 0
. - 。网:
.IsNullOrEmpty
.
ETC。
其他提示
在使用 C 风格(空终止)字符串的语言中,与 ""
会更快。这是一个 O(1) 操作,而 C 风格字符串的长度是 O(n)。
在将长度存储为字符串对象一部分的语言(C#、Java 等)中,检查长度的时间复杂度也是 O(1)。在这种情况下,直接检查长度会更快,因为它避免了构造新的空字符串的开销。
在.Net中:
string.IsNullOrEmpty( nystr );
字符串可以为 null,因此 .Length 有时会抛出 NullReferenceException
在使用 C 风格(空终止)字符串的语言中,与“”相比会更快
实际上,最好检查字符串中的第一个字符是否为“\0”:
char *mystring;
/* do something with the string */
if ((mystring != NULL) && (mystring[0] == '\0')) {
/* the string is empty */
}
在 Perl 中还有第三种选择,即字符串未定义。这与 C 中的 NULL 指针有点不同,只是因为访问未定义的字符串时不会出现分段错误。
假设您的问题是 .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
这意味着比较需要的不仅仅是字符串长度检查。
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 不会 - 我知道它是一些小而琐碎的东西,但 id 仍然宁愿在我不需要它们时不创建对象!(来源)
实际上,IMO 确定的最佳方法是 string 类的 IsNullOrEmpty() 方法。
http://msdn.microsoft.com/en-us/library/system.string.isnullorempty。
更新:我假设.Net,在其他语言中,这可能会有所不同。
在这种情况下,直接检查长度会更快,因为它避免了构造新的空字符串的开销。
@德里克帕克:这并不总是正确的。“”是一个字符串文字,因此在 Java 中,它几乎肯定已经被保留。
对于 C 字符串,
if (s[0] == 0)
会比任何一个都快
if (strlen(s) == 0)
或者
if (strcmp(s, "") == 0)
因为您将避免函数调用的开销。
@内森
实际上,最好检查字符串中的第一个字符是否为“\0”:
我差点就提到了这一点,但最终还是把它省略了,因为打电话 strcmp()
使用空字符串和直接检查字符串中的第一个字符都是 O(1)。您基本上只需支付额外的函数调用费用,这非常便宜。如果你 真的 不过,如果需要绝对最佳的速度,肯定要直接进行第一个字符与 0 的比较。
老实说,我一直用 strlen() == 0
, , 因为我有 绝不 编写了一个程序,其中这实际上是一个可测量的性能问题,我认为这是表达检查的最易读的方式。
再说一遍,如果不懂得语言,就无法分辨。
但是,我建议您选择对后续维护程序员最有意义的技术,并且必须维护您的工作。
我建议编写一个明确执行您想要的操作的函数,例如
#define IS_EMPTY(s) ((s)[0]==0)
或可比较。现在毫无疑问你正在检查。
读完这篇文章后,我进行了一个小实验,得出了两个截然不同且有趣的发现。
考虑以下。
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 中),这恰好是正在测试的字符串的长度。
鉴于立即可见的机器代码几乎相同,我可以想象这将解释字符串相等性比报告的字符串长度更好的性能的唯一原因 欣尼 一个问题是,执行比较的双参数函数比读取字符串实例长度的单参数函数得到了更好的优化。
结论
原则上,我避免将空字符串作为文字进行比较,因为空字符串文字在源代码中可能会显得不明确。为此,我的 .NET 帮助器类早已将空字符串定义为常量。虽然我用 字符串.空 对于直接内联比较,该常量通过定义其值为空字符串的其他常量而获得保留,因为常量不能被赋值 字符串.空 作为它的价值。
这项练习一劳永逸地解决了我可能对与两者进行比较的成本(如果有的话)的任何担忧 字符串.空 或我的辅助类定义的常量。
然而,取代它也引发了一个令人费解的问题;为什么要比较 字符串.空 比测试字符串的长度更有效?或者Shinny使用的测试是无效的,因为循环的实现方式?(我发现这很难相信,但是,话又说回来,我以前也被愚弄过,我相信你也被愚弄过!)
我长期以来一直认为 系统字符串 对象是可计数的字符串,基本上类似于我们从 COM 中早就知道的长期建立的基本字符串 (BSTR)。