Проверяете содержимое строки? длина строки против пустой строки

Что более эффективно для компилятора и лучше всего проверять, является ли строка пустой?

  1. Проверка того, что длина строки == 0
  2. Проверка, пуста ли строка (strVar == "")

Кроме того, зависит ли ответ от языка?

Ответов (13)

Решение

Да, это зависит от языка, поскольку хранение строк в разных языках различается.

  • Строки Паскаля типа: Length = 0.
  • Строки C-стиль: [0] == 0.
  • .NET: .IsNullOrEmpty.

И т.п.

Прочитав эту ветку, я провел небольшой эксперимент, который дал два разных и интересных вывода.

Обратите внимание на следующее.

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

Из приведенных выше списков машинного кода, созданного модулем NGEN .NET Framework версии 4.5, я делаю следующие выводы.

  1. Проверка на равенство пустого строкового литерала и статического свойства string.Empty в классе System.string для всех практических целей идентичны. Единственное различие между двумя фрагментами кода - это источник первой инструкции перемещения, и оба являются смещениями относительно ds, что означает, что оба относятся к встроенным константам.

  2. Проверка на равенство с пустой строкой в ​​виде литерала или свойства string.Empty устанавливает вызов функции с двумя аргументами, который указывает на неравенство , возвращая ноль. Я основываю этот вывод на других тестах, которые я выполнил пару месяцев назад, в которых я следил за некоторым из моего собственного кода через управляемое / неуправляемое разделение и обратно. Во всех случаях любой вызов, требующий двух или более аргументов, помещает первый аргумент в регистр ECX, а второй - в регистр EDX. Я не помню, как передавались последующие аргументы. Тем не менее настройка вызова больше походила на __fastcall, чем на __stdcall. Точно так же ожидаемые возвращаемые значения всегда отображаются в регистре EAX, что почти универсально.

  3. Проверка длины строки устанавливает вызов функции с одним аргументом, который возвращает 1 (в регистре EAX), что является длиной проверяемой строки.

  4. Учитывая, что сразу видимый машинный код почти идентичен, единственная причина, по которой я могу себе представить, что это объясняет лучшую производительность строкового равенства по длине жала, о которой сообщает Shinny, заключается в том, что функция с двумя аргументами, которая выполняет сравнение, значительно лучше оптимизирована, чем функция с одним аргументом, которая считывает длину экземпляра строки.

Заключение

В принципе, я избегаю сравнения с пустой строкой как с литералом, потому что пустой строковый литерал может выглядеть неоднозначно в исходном коде. С этой целью мои вспомогательные классы .NET уже давно определяют пустую строку как константу. Хотя я использую string.Empty для прямых, встроенных сравнений, константа сохраняет свое значение для определения других констант, значение которых является пустой строкой, поскольку константе нельзя присвоить значение string.Empty .

Это упражнение раз и навсегда решает любые мои опасения по поводу стоимости, если таковая имеется, сравнения либо с string.Empty, либо с константой, определенной моими вспомогательными классами.

Однако возникает загадочный вопрос о его замене; почему сравнение с string.Empty более эффективно, чем проверка длины строки? Или тест, используемый Шинни, недействителен из-за того, как реализован цикл? (Мне трудно в это поверить, но, опять же, меня раньше обманывали, как, я уверен, и вас тоже!)

Я долгое время предполагал, что объекты system.string - это счетные строки, фундаментально похожие на давно установившуюся базовую строку (BSTR), которую мы давно знаем по COM.

В .Net:

string.IsNullOrEmpty( nystr );

строки могут быть нулевыми, поэтому .Length иногда выдает исключение NullReferenceException

В языках, использующих строки в стиле C (с завершающим нулем), сравнение "" будет быстрее. Это операция O (1), тогда как длина строки в стиле C равна O (n).

В языках, которые хранят длину как часть строкового объекта (C#, Java, ...), проверка длины также O (1). В этом случае прямая проверка длины выполняется быстрее, потому что это позволяет избежать накладных расходов на создание новой пустой строки.

Собственно, IMO лучший способ определить - это метод IsNullOrEmpty () строкового класса.

http://msdn.microsoft.com/en-us/library/system.string.isnullorempty.

Обновление: я предположил .Net, на других языках это может быть иначе.

В этом случае прямая проверка длины выполняется быстрее, потому что это позволяет избежать накладных расходов на создание новой пустой строки.

@DerekPark: Это не всегда правда. "" - это строковый литерал, поэтому в Java он почти наверняка уже будет интернирован.

Для струн C

if (s[0] == 0)

будет быстрее, чем любой

if (strlen(s) == 0)

или

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

потому что вы избежите накладных расходов на вызов функции.

@Натан

На самом деле, может быть лучше проверить, является ли первый символ в строке '\ 0':

Я почти упомянул об этом, но в итоге оставил это, поскольку вызов strcmp() с пустой строкой и прямая проверка первого символа в строке - это O (1). Вы просто платите за дополнительный вызов функции, что довольно дешево. Однако, если вам действительно нужна абсолютная лучшая скорость, определенно используйте прямое сравнение первого символа с нулевым.

Честно говоря, я всегда использую strlen() == 0, потому что я никогда не писал программу, в которой это действительно было бы измеримой проблемой производительности, и я думаю, что это наиболее читаемый способ выразить проверку.

В Java 1.6 класс String имеет новый метод [isEmpty] 1

Существует также библиотека общего пользования Джакарты, в которой есть метод [isBlank] 2 . Пробел определяется как строка, содержащая только пробелы.

В языках, которые используют строки в стиле C (с завершающим нулем), сравнение с "" будет быстрее

На самом деле, может быть лучше проверить, является ли первый символ в строке '\ 0':

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

В Perl есть третий вариант: строка не определена. Это немного отличается от указателя NULL в C, хотя бы потому, что вы не получаете ошибку сегментации для доступа к неопределенной строке.

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 по-прежнему не создает объекты, когда они мне не нужны! ( Источник )

Опять же, не зная языка, сказать невозможно.

Тем не менее, я рекомендую вам выбрать метод, который имеет наибольший смысл для программиста сопровождения, который следует последующему и который должен будет поддерживать вашу работу.

Я бы рекомендовал написать функцию, которая явно делает то, что вы хотите, например

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

или сопоставимые. Теперь нет никаких сомнений в том, что вы проверяете.

Предполагая, что ваш вопрос - .NET:

Если вы хотите проверить свою строку на соответствие null, также используйте 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

Это означает, что сравнение требует большего, чем проверка длины строки.