Проверяете содержимое строки?Длина строки против пустой строки
-
08-06-2019 - |
Вопрос
Что более эффективно для компилятора и как лучше всего проверять, является ли строка пустой?
- Проверка того, равна ли длина строки == 0
- Проверка того, пуста ли строка (strVar == "")
Кроме того, зависит ли ответ от языка?
Решение
Да, это зависит от языка, поскольку хранение строк в разных языках различается.
- Строки типа Паскаль:
Length = 0
. - Строки в стиле C:
[0] == 0
. - .СЕТЬ:
.IsNullOrEmpty
.
И т. д.
Другие советы
В языках, использующих строки в стиле C (с нулевым завершением), по сравнению с ""
будет быстрее.Это операция O(1), а длина строки в стиле C равна O(n).
В языках, которые хранят длину как часть строкового объекта (C#, Java,...), проверка длины также осуществляется за O(1).В этом случае непосредственная проверка длины выполняется быстрее, поскольку позволяет избежать накладных расходов на создание новой пустой строки.
В .Net:
string.IsNullOrEmpty( nystr );
строки могут быть нулевыми, поэтому .Length иногда выдает исключение NullReferenceException
В языках, использующих строки в стиле C (с нулевым завершением), сравнение с "" будет быстрее.
На самом деле, возможно, лучше проверить, является ли первый символ в строке '\0':
char *mystring;
/* do something with the string */
if ((mystring != NULL) && (mystring[0] == '\0')) {
/* the string is empty */
}
В Perl есть третий вариант: строка не определена.Это немного отличается от NULL-указателя в C, хотя бы потому, что вы не получаете ошибку сегментации при доступе к неопределенной строке.
Предполагая, что ваш вопрос .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 не будет - я знаю, что это что-то маленькое и тривиальное, но я все равно предпочитаю не создавать объекты, когда они мне не нужны!(Источник)
На самом деле, по моему мнению, лучший способ определить это — метод 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
Из приведенных выше списков машинного кода, сгенерированных модулем NGEN .NET Framework версии 4.5, я делаю следующие выводы.
Проверка на равенство пустого строкового литерала и статического свойства string.Empty класса System.string для всех практических целей идентична.Единственная разница между двумя фрагментами кода — это источник первой инструкции перемещения, и оба являются смещениями относительно ds, подразумевая, что оба относятся к встроенным константам.
Проверка равенства пустой строки, как литерала, так и свойства string.Empty, устанавливает вызов функции с двумя аргументами, который указывает неравенство вернув ноль.Я основываю этот вывод на других тестах, которые я провел пару месяцев назад, в которых я проследил часть своего собственного кода через границу между управляемым и неуправляемым и обратно.Во всех случаях любой вызов, требующий двух или более аргументов, помещает первый аргумент в регистр ECX, а второй — в регистр EDX.Я не помню, как принимались последующие аргументы.Тем не менее, настройка звонка больше напоминала __fastcall, чем __stdcall.Аналогично, ожидаемые возвращаемые значения всегда отображались в регистре EAX, который практически универсален.
Проверка длины строки вызывает вызов функции с одним аргументом, которая возвращает 1 (в регистре EAX), что соответствует длине проверяемой строки.
Учитывая, что сразу видимый машинный код почти идентичен, единственная причина, которую я могу себе представить, объясняет лучшую производительность равенства строк по длине строки, о которой сообщает Шинни заключается в том, что функция с двумя аргументами, выполняющая сравнение, значительно лучше оптимизирована, чем функция с одним аргументом, которая считывает длину экземпляра строки.
Заключение
В принципе, я избегаю сравнения с пустой строкой как с литералом, потому что литерал пустой строки может показаться неоднозначным в исходном коде.С этой целью мои вспомогательные классы .NET уже давно определили пустую строку как константу.Хотя я использую строка.Пусто для прямых, встроенных сравнений константа используется для определения других констант, значением которых является пустая строка, поскольку константа не может быть присвоена строка.Пусто как его ценность.
Это упражнение раз и навсегда разрешает любые опасения, которые могут возникнуть у меня по поводу стоимости сравнения с любым из них, если таковая имеется. строка.Пусто или константа, определенная моими вспомогательными классами.
Однако возникает также загадочный вопрос о его замене;почему сравнивается с строка.Пусто более эффективно, чем проверка длины строки?Или тест, используемый Шинни, признан недействительным из-за способа реализации цикла?(Мне в это трудно поверить, но, опять же, меня и раньше обманывали, как, я уверен, и вас тоже!)
Я давно это предполагал system.string объекты представляли собой подсчитываемые строки, что в основном похоже на давно устоявшуюся базовую строку (BSTR), которую мы давно знаем из COM.