Проверять содержимое строки? Длина строки против пустой строки
Что является более эффективным для компилятора и лучший способ проверить, является ли строка пустой?
- Проверка, соответствует ли длина строки == 0
- Проверка, является ли строка пустой (strVar == "")
Кроме того, зависит ли ответ от языка?
14 ответов
Да, это зависит от языка, так как хранение строк отличается в разных языках.
- Строки типа Паскаля:
Length = 0
, - Струны в стиле C:
[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 есть третий вариант, что строка не определена. Это немного отличается от NULL-указателя в C хотя бы потому, что вы не получите ошибку сегментации для доступа к неопределенной строке.
Для струн C,
if (s[0] == 0)
будет быстрее чем либо
if (strlen(s) == 0)
или же
if (strcmp(s, "") == 0)
потому что вы избежите накладных расходов при вызове функции.
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 все равно не создает объекты, когда они мне не нужны! ( Источник)
В.Net:
string.IsNullOrEmpty( nystr );
строки могут быть нулевыми, поэтому.Length иногда создает исключение NullReferenceException
После прочтения этой темы я провел небольшой эксперимент, который дал два отличных и интересных вывода.
Учтите следующее.
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 давно определили пустую строку как константу. Хотя я использую string.Empty для прямых, встроенных сравнений, константа зарабатывает свое удержание для определения других констант, значение которых является пустой строкой, потому что константе не может быть присвоено string.Empty в качестве ее значения.
Это упражнение раз и навсегда решает любую проблему, которую я могу иметь в связи со стоимостью, если таковая есть, сравнения со строкой.Empty или константой, определенной моими вспомогательными классами.
Однако, это также поднимает загадочный вопрос, чтобы заменить его; почему сравнение со string.Empty более эффективно, чем проверка длины строки? Или тест, используемый Шинни, признан недействительным из-за того, как реализован цикл? (Мне трудно в это поверить, но опять же, я был одурачен раньше, как, я уверен, и вы тоже!)
Я давно предполагал, что объекты system.string были подсчитанными строками, в основном похожими на давно установленную Basic String (BSTR), которую мы давно знаем из COM.
Предполагая, что ваш вопрос.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
Это означает, что сравнение занимает намного больше времени, чем проверка длины строки.
@Натан
На самом деле, может быть лучше проверить, является ли первый символ в строке '\0':
Я почти упомянул это, но в итоге оставил это, так как звонил strcmp()
с пустой строкой и прямой проверкой первого символа в строке - оба O(1). Вы просто платите за дополнительный вызов функции, который довольно дешев. Если вам действительно нужна абсолютная лучшая скорость, определенно используйте прямое сравнение "первый символ с нулем".
Честно говоря, я всегда использую strlen() == 0
Потому что я никогда не писал программу, в которой это было бы измеримой проблемой производительности, и я думаю, что это самый читаемый способ выражения чека.
В этом случае непосредственная проверка длины выполняется быстрее, поскольку она позволяет избежать затрат на создание новой пустой строки.
@DerekPark: это не всегда так. "" является строковым литералом, поэтому в Java он почти наверняка уже будет интернирован.
На самом деле, IMO лучший способ определить это метод IsNullOrEmpty() класса string.
http://msdn.microsoft.com/en-us/library/system.string.isnullorempty.....
Обновление: я предположил.Net, на других языках, это может быть иначе.
Опять же, не зная языка, невозможно сказать.
Тем не менее, я рекомендую вам выбрать метод, который больше всего подходит программисту по техническому обслуживанию, который следует и должен будет поддерживать вашу работу.
Я бы порекомендовал написать функцию, которая явно делает то, что вы хотите, например,
#define IS_EMPTY(s) ((s)[0]==0)
или сопоставимы. Теперь нет никаких сомнений в том, что вы проверяете.
Если строки в языке имеют внутреннее свойство длины, проверка длины выполняется быстрее, поскольку это целочисленное сравнение значения свойства с нулем. Однако, когда строки не имеют такого свойства, длина должна быть определена в тот момент, когда вы хотите проверить ее, и это очень быстро для строки, которая на самом деле имеет нулевую длину, но может занять очень много времени, если строка огромна. чего вы не можете знать заранее, потому что, если вы знали, что у него нулевой размер, зачем вам вообще это проверять?
Если строки не хранят свою длину внутри, сравнение с пустой строкой обычно происходит быстрее, так как даже если это означает, что две строки будут сравниваться посимвольно, этот цикл точно завершается после самой первой итерации, поэтому время является линейным (O(1)) и не зависит от длины строки. Однако, даже если у строк есть внутреннее свойство длины, сравнение их с пустой строкой может быть таким же быстрым, как проверка этого свойства, поскольку в этом случае большинство реализаций будут делать именно это: сначала они сравнивают длины, а если они не совпадают, они пропускают сравнение. символы полностью, как будто даже длины не совпадают, строки не могут быть равными с самого начала. Тем не менее, если длины совпадают, они обычно проверяют специальный случай 0 и снова пропускают цикл сравнения символов.
И если язык предлагает явный способ проверки того, пуста ли строка, всегда используйте его, так как независимо от того, какой способ быстрее, этот способ проверки используется внутри. Например, чтобы проверить пустую строку в сценарии оболочки, вы можете использовать
[ "$var" = "" ]
что было бы сравнением строк. Или вы могли бы использовать
[ ${#var} -eq 0 ]
который использует сравнение длины строки. Но на самом деле самый эффективный способ
[ -z "$var" ]
как-z
операция существует только для этой цели.
C в этом отношении особенный, так как внутреннее содержимое строки открыто (строки не являются инкапсулированными объектами в C), и хотя строки C не имеют свойства длины, и их длина должна определяться каждый раз, когда это необходимо, очень легко проверить, строка C пуста, просто проверяя, является ли ее первый символ NUL, поскольку NUL всегда является последним символом в строке C, поэтому ничто не может победить:
char * string = ...;
if (!*string) { /* empty */ }
(обратите внимание, что в С*string
такой же какstring[0]
,!x
такой же какx == 0
, и0
та же'\0'
, так что вы могли бы написатьstring[0] == '\0'
но для компилятора это точно так же, как я написал выше)