关于优化:检查字符串内容? 字符串长度vs空字符串

关于优化:检查字符串内容? 字符串长度vs空字符串

Checking for string contents? string Length Vs Empty String

哪种方法对编译器更有效,并且是检查字符串是否为空的最佳实践?

  • 检查字符串的长度是否== 0
  • 检查字符串是否为空(strVar ==")
  • 另外,答案是否取决于语言?


    是的,这取决于语言,因为字符串存储在语言之间有所不同。

    • Pascal类型的字符串:Length = 0
    • C风格的字符串:[0] == 0
    • .NET:.IsNullOrEmpty

    等等。


    在使用C样式(以空字符结尾)字符串的语言中,与""相比将更快。这是一个O(1)操作,而C风格字符串的长度为O(n)。

    在将长度存储为字符串对象的一部分的语言(C#,Java等)中,检查长度也是O(1)。在这种情况下,直接检查长度会更快,因为它避免了构造新的空字符串的开销。


    String.IsNullOrEmpty()仅适用于.net 2.0及更高版本,对于.net 1 / 1.1,我倾向于使用:

    1
    2
    3
    4
    if (inputString == null || inputString == String.Empty)
    {
        // String is null or empty, do something clever here. Or just expload.
    }

    我使用String.Empty而不是"因为"会创建一个对象,而String.Empty不会-我知道它很小而琐碎,但是当我不需要它们时,id仍然不会创建对象! (资源)


    In languages that use C-style (null-terminated) strings, comparing to"" will be faster

    实际上,最好检查字符串中的第一个字符是否为' 0':

    1
    2
    3
    4
    5
    char *mystring;
    /* do something with the string */
    if ((mystring != NULL) && (mystring[0] == '\0')) {
        /* the string is empty */
    }

    在Perl中,还有第三个选项,即字符串未定义。这仅与C中的NULL指针有所不同,仅是因为您不会因访问未定义的字符串而出现分段错误。


    在.Net中:

    1
    string.IsNullOrEmpty( nystr );

    字符串可以为null,因此.Length有时会引发NullReferenceException


    阅读此主题后,我进行了一个小实验,得出了两个截然不同且有趣的发现。

    考虑以下。

    1
    strInstallString   "1" string

    以上内容是从Visual Studio调试器的本地窗口复制的。以下三个示例中都使用相同的值。

    if(strInstallString ==")=== if(strInstallString == string.Empty)

    以下是这两个基本相同的情况在Visual Studio 2013调试器的反汇编窗口中显示的代码。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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)没有明显的不同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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模块生成的机器代码清单中,我得出以下结论。

  • 出于所有实际目的,针对空字符串文字和静态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得知的已建立的Basic String(BSTR)。


    假设您的问题是.NET:

    如果还要验证字符串是否为空,请使用IsNullOrEmpty,如果已经知道字符串不为空,例如在检查TextBox.Text等时,请勿使用IsNullOrEmpty,然后出现问题。
    因此,我认为String.Length的性能不如字符串比较。

    我对其进行了事件测试(我也对C#进行了测试,结果相同):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    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

    结果:

    1
    2
    3
    4
    5
    Way 1...
    Way 2...
    way 1 took 624001 ticks
    way 2 took 468001 ticks
    way 1 took 156000 ticks more than way 2

    这意味着比较比字符串长度检查更重要。


    在Java 1.6中,String类具有一个新方法isEmpty

    还有一个具有isBlank方法的Jakarta Commons库。空白定义为仅包含空格的字符串。


    同样,如果不知道语言,就无法分辨。

    但是,我建议您选择对随后的维护程序员最有意义并且必须维护您的工作的技术。

    我建议编写一个明确执行所需功能的函数,例如

    1
    #define IS_EMPTY(s) ((s)[0]==0)

    或可比。现在毫无疑问,您正在检查。


    @内森

    Actually, it may be better to check if the first char in the string is '\0':

    我几乎提到了这一点,但最终还是把它遗漏了,因为用空字符串调用strcmp()并直接检查字符串中的第一个字符都是O(1)。基本上,您只需要为额外的函数调用付费,这非常便宜。但是,如果您确实需要绝对的最佳速度,则可以直接进行从第一个字符到0的比较。

    老实说,我一直使用strlen() == 0,因为我从未编写过一个实际上是可衡量的性能问题的程序,而且我认为这是表达支票最易读的方式。


    对于C字符串,

    1
    if (s[0] == 0)

    会比任何一个都快

    1
    if (strlen(s) == 0)

    要么

    1
    if (strcmp(s,"") == 0)

    因为您将避免函数调用的开销。


    In this case, directly checking the length is faster, because it avoids the overhead of constructing the new empty string.

    @DerekPark:并非总是如此。"是一个字符串文字,因此,在Java中,几乎可以肯定它已经被嵌入。


    实际上,IMO确定的最佳方法是字符串类的IsNullOrEmpty()方法。

    http://msdn.microsoft.com/zh-cn/library/system.string.isnullorempty。

    更新:我假设.Net在其他语言中可能有所不同。


    推荐阅读

      3500元超额值学生娱乐结构的优化配置

      3500元超额值学生娱乐结构的优化配置,,作为一个DIY的主流用户领域的学生,每个用户51学生攒机的高峰。因为学生用户没有稳定的收入来源,攒机

      优化PostgreSQL中的批量更新性能

      优化PostgreSQL中的批量更新性能,数据,表格,在Ubuntu 12.04上使用PG 9.1. 我们目前需要24小时才能运行大量UPDATE数据库上的语句,其形式

      字符库快捷键|字符串快捷键

      字符库快捷键|字符串快捷键,,1. 字符串快捷键1、单行注释单行注释是 #Mac的快捷键是 command+/windows的快捷键是 Ctrl + /2、多行注

      512内存的电脑优化|笔记本内存512

      512内存的电脑优化|笔记本内存512,,1. 笔记本内存512够用,因为运行非常流畅,苹果笔记本 16g512的运行内存是16g内存,机身内存是512g内存,运行

      Windows7下固态硬盘的优化技术

      Windows7下固态硬盘的优化技术,,当微软开发Windows Vista时,固态硬盘没有那么热,所以没有进行优化。Windows 7是不同的。微软从一开始就把SS

      C上的引导检查文件系统:文件的类型

      C上的引导检查文件系统:文件的类型,,故障现象:系统检查启动检查文件 每一次你启动一台计算机,都会是这样的。 在d上检查文件系统: 该文件

      记一次服务端系统性能优化

      记一次服务端系统性能优化,在线,设备, 首先简单介绍一下业务场景,物联网设备,关注公众号,免费领取环保袋。12月8号,也就是昨天上午,突然接

      幻灯片放映慢优化可以加快速度

      幻灯片放映慢优化可以加快速度,,用于制作幻灯片的一些技术更复杂,这些幻灯片在一些旧机器上运行缓慢或缓慢。在这种情况下,我们应该如何优化