문자열 내용을 확인 중이신가요?문자열 길이 대 빈 문자열
-
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 );
문자열은 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이라고 가정합니다.
문자열이 null인지 확인하려면 IsNullOrEmpty를 사용하세요. 예를 들어 TextBox.Text 등을 확인할 때 문자열이 null이 아니라는 것을 이미 알고 있다면 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는 그렇지 않습니다. 작고 사소한 것을 알고 있지만 필요하지 않을 때는 객체를 생성하지 않는 편이 낫습니다!(원천)
실제로 IMO에서 결정하는 가장 좋은 방법은 문자열 클래스의 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 속성으로 빈 문자열에 대한 동일성을 테스트하면 다음을 나타내는 두 개의 인수 함수 호출이 설정됩니다. 불평등 0을 반환하여.나는 몇 달 전에 수행한 다른 테스트를 바탕으로 이 결론을 내렸습니다. 이 테스트에서는 관리/비관리 구분에 걸쳐 내 코드 중 일부를 따랐습니다.모든 경우에 둘 이상의 인수가 필요한 모든 호출은 첫 번째 인수를 ECX 레지스터에 넣고 두 번째 인수를 레지스터 EDX에 넣습니다.후속 인수가 어떻게 전달되었는지 기억이 나지 않습니다.그럼에도 불구하고 호출 설정은 __stdcall보다는 __fastcall에 더 가깝습니다.마찬가지로 예상되는 반환 값은 거의 보편적인 EAX 레지스터에 항상 표시됩니다.
문자열 길이를 테스트하면 테스트 중인 문자열의 길이인 1(EAX 레지스터에서)을 반환하는 단일 인수 함수 호출이 설정됩니다.
즉시 눈에 보이는 기계어 코드가 거의 동일하다는 점을 감안할 때, 보고된 문자열 길이에 대한 문자열 동등성 성능이 더 나은 것을 설명할 수 있는 유일한 이유는 다음과 같습니다. 시니 비교를 수행하는 두 개의 인수 함수가 문자열 인스턴스에서 길이를 읽는 단일 인수 함수보다 훨씬 더 잘 최적화된다는 것입니다.
결론
원칙적으로 빈 문자열 리터럴은 소스 코드에서 모호하게 나타날 수 있으므로 빈 문자열을 리터럴로 비교하는 것을 피합니다.이를 위해 내 .NET 도우미 클래스에서는 오랫동안 빈 문자열을 상수로 정의해 왔습니다.내가 사용하지만 문자열.비어 있음 직접 인라인 비교의 경우 상수는 값이 빈 문자열인 다른 상수를 정의하기 위해 유지됩니다. 상수를 할당할 수 없기 때문입니다. 문자열.비어 있음 그 가치로.
이 연습은 둘 중 하나와 비교하는 데 드는 비용에 대해 내가 가질 수 있는 모든 우려를 완전히 해결합니다. 문자열.비어 있음 또는 도우미 클래스에서 정의한 상수입니다.
그러나 이를 대체하기에는 난해한 질문도 제기됩니다.왜 비교하는거야? 문자열.비어 있음 문자열 길이를 테스트하는 것보다 더 효율적입니까?아니면 루프가 구현되는 방식으로 인해 Shinny가 사용하는 테스트가 무효화됩니까?(믿기 힘들지만, 나도 이전에 속았던 적이 있습니다. 여러분도 그럴 거라고 확신합니다!)
나는 오랫동안 그렇게 생각해왔다. 시스템.문자열 개체는 COM에서 오랫동안 알려져 온 BSTR(기본 문자열)과 근본적으로 유사한 계산된 문자열이었습니다.