SimpleIsBest.NET

유경상의 닷넷 블로그

문자열 이야기 (4) - 문자열 비교

by 블로그쥔장 | 작성일자: 2005-06-08 오전 11:34:00
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.

문자열 이야기 네 번째 입니다. 문자열 비교에 대해서 몇자 끄적여 봤습니다. 재밌게 글 쓰기가 정말 어렵네요. 어떻게 몇자 적어 내려가다보면 어느새 딱딱해져 있는 글을 보면 한숨이 나옵니다. 딴지 일보 연구를 좀 해봐야 할 듯 하네요.

About String Compare...

문자열을 서로 비교하는 문자열 비교는 상당히 자주 수행하는 문자열 연산 중 하나이다. 닷넷의 문자열은 value-type 이 아닌 reference-type 이므로 비교를 할 때 약간의 주의를 기울여야 한다. 대개의 경우 그냥 비교를 하면 큰 문제가 없지만 그렇지 않은 경우도 있기 때문이다. 이번 문자열 이야기 씨리즈는 문자열 비교에 대해서 간단히 몇 마디 해보고 문자열 비교에 관련된 팁을 하나 소개하고자 한다.

다시 한번 말한다. 닷넷의 문자열은 참조형(reference-type)이다. 이런 전차로 어린 String 클래스는 Equals 이란 메쏘드를 제공한다. 이 메쏘드는 문자열 참조 비교를 하는 것이 아니라 일반적으로 생각하는 문자열 비교를 수행해준다(너무 당연한가 ? -_-). 다음 코드를 보자.

string s1 = "aaa";
string s2 = String.Concat("aa", "a");
Console.WriteLine("Reference Compare : s1 = s2 ? {0}", Object.ReferenceEquals(s1, s2));
Console.WriteLine("String Compare : s1 = s2 ? {0}", s1.Equals(s2));

수행 결과 :
Reference Compare : s1 = s2 ? False
String Compare : s1 = s2 ? True

위 코드는 참조형 비교와 일반 문자열 비교를 잘 보여주고 있다. s2 변수가 참조하는 문자열은 Concat 메쏘드 호출에 의해 새로이 생성된 문자열 객체이다. 따라서 s1과의 참조 비교(ReferenceEquals 메쏘드)를 수행하면 같지 않다는 결과가 나온다. 반면 Equals 메쏘드에 의한 비교는 참조 비교가 아닌 문자열 비교이므로 s1, s2의 비교는 동일한 것으로 결과가 나온다.

Equality Operators

일반적인 닷넷 프로그래밍에서 문자열을 비교할 때, Equals 메쏘드나 ReferenceEquals 메쏘드를 사용하는 경우는 별로 없다고 본다. 대부분 프로그래밍 언어(C#, VB.NET 등)에서 제공하는 동등 연산자(Equality Operators)를 사용할 것이다. C#, Managed C++의 경우에는 == 연산자를 VB.NET의 경우는 = 연산자를 사용할 것이다.

C#의 경우 문자열에 == 연산자가 사용되면 String 클래스에 정의된 연산자 오버로드 메쏘드인 op_Equality 메쏘드가 호출되게 되어 있다. 그리고 이 메쏘드는 다시 Equals 메쏘드를 호출한다. 따라서 C#에서 문자열에 대해 == 연산자를 사용하면 참조 비교가 아닌 문자열 비교를 수행하는 것이다.

반면 Managed C++는 == 연산자는 단순한 참조 비교이다. 누가 C++ 아니랄까봐 ReferenceEquals 마저 호출하지 않고 참조값, 즉 포인터를 비교해 버린다. 따라서 Managed C++에서 참조 비교가 아닌 문자열 비교를 하기 위해서는 명시적으로 Equals 메쏘드를 호출해 주어야 한다.

VB.NET은 C#과 동일하게 = 연산자가 사용되면 문자열 비교를 수행한다. 다른 점은 String 클래스에 정의된 연산자 오버로드 메쏘드가 아닌 VB.NET 만의 런타임 라이브러리인 Microsoft.VisualBasic.dll 내에 포함된 StringType 클래스의 문자열 비교 메쏘드(StrCmp 메소드)를 호출하도록 되어 있고 이 메쏘드 내에서 다시 String.CompareOrdinal 메쏘드를 호출하도록 되어 있다. 왜 이렇게 되어 있는가는 필자도 확실하지 않다. 대충 이전 VB 언어와의 호환성 측면 때문이 아닐까 싶다. (VB.NET은 대략... 아웅~)

참고로 문자열 비교가 아닌 참조 비교를 하고 싶다면 언어에 무관하게 Object.ReferenceEquals 메쏘드를 호출하면 된다.

Tip about String Compare

문자열 비교에서 팁 하나를 소개하고자 한다. 문자열을 비교할 때 효율성을 고려해야 할 것이 한두 가지가 아닐 것이지만 흔히 지나치는 것 중 하나가 대소문자 구별 없이 비교를 하는 경우이다. 기본적으로 String.Equals 메소드는 대소문자를 구별하여 비교를 수행한다. 그리고 VB.NET 에서 = 연산자를 이용하여 문자열 비교를 하는 경우도 마찬가지다. 이 때문에 대소문자에 관계 없이 문자열을 비교하고자 할 때 흔히 사용하는 방법은 비교 대상을 모두 대문자로 바꾸거나 모두 소문자로 바꾸어 비교하곤 한다.

// 대소문자에 관계없는 문자열 비교 C# 코드 예제
if (s1.ToUpper() == s2.ToUpper()) {
    // 동일한 문자열
}
else {
    // 동일하지 않은 문자열
}

위 코드는 잘 작동하지만 효율적이라고 볼 수 없다. 왜냐면 ToUpper() 메쏘드 호출은 대문자로 변경된 새로운 문자열 객체를 반환한다는 점이다. 문자열은 변경되지 않는 immutable 임을  잊지 말자. 따라서 위 코드는 불필요하게 문자열 객체 2개를 만들어 내며 이 문자열 객체는 곧바로 GC 의 대상이 되는 쓰레기로 전락한다.

보다 효율적인 것은 String.Compare 메쏘드를 사용하는 것이다. 이 메쏘드는 비교 옵션을 매개변수로 취할 수 있다. 이 옵션 중에 IgnoreCase 옵션을 사용하면 대문자/소문자에 관계 없이 비교를 해준다. 물론 앞서의 예제처럼 불필요한 문자열 객체를 생성하는 일은 없다.

// 보다 효율적인 코드 예제
if (String.Compare(s1,s2, CompareOptions.IgnoreCase) == 0) {
    // 동일한 문자열
}
else {
    // 동일하지 않은 문자열
}

그까짓거 그냥 대~충~

대부분의 경우, 이러한 내용까지 모두 고려하고 코딩을 할라치면 머리에 쥐가 나기 마련이다. 대부분의 경우 이렇게까지 신경을 쓰며 프로그래밍을 해야 하는 경우는 드물다. 특히, 기업용 MIS 어플리케이션이나 SI 류의 프로젝트에서는 그까지꺼 그냥 대~충~ == 연산자 쓰고, 대~충~ ToUpper, ToLower 써가면서 프로그램을 해도 된다.

하지만 수십, 수백만명이 사용하는 대형 웹 사이트 어플리케이션이라면 이야기가 좀 달라진다. 사소한 코드 하나도 수십만명이 사용하게 되면 수십, 수백만개의 불필요한 문자열 객체들이 생성될 수도 있기 때문이다.

판단은 느그들이 알아서 하시길... 툐툐툐~~



Comments (read-only)
#re: 문자열 이야기 (4) - 문자열 비교 / 정성태 / 2005-06-14 오후 5:17:00
FxCop 돌려 보면...
빈 문자열 판단할 때,

if ( txt != "" )

이런 식으로 하지 말라고 하죠. ^^
if ( txt.Length() == 0 )
위와 같이 하라고....

근데.. 알면서도,,, 가끔 무심코 txt != "" 로 쓰게 됩니다. 습관이 무서워요. ^^

그나 저나... 이 사이트 너무 깔끔하고 좋아 보이네요. ㅠ.ㅠ 나도 이걸로 바꾸고 싶당.
#re: 문자열 이야기 (4) - 문자열 비교 / 땡초 / 2006-04-03 오전 1:25:00
txt.Length
무심코 쓰는 코드긴 하지만,
이글본후 txt.Length는 감회가 새롭네여^^
#re: 문자열 이야기 (4) - 문자열 비교 / 조승태 / 2008-03-05 오후 4:53:00
아.. 하루에 3개의 글을 보았을 뿐인데,,
눈에 쥐가 나려고 하네요. ^^;
오늘은 요까이~
좋은 글 감사합니다.
#re: 문자열 이야기 (4) - 문자열 비교 / yurika80 / 2008-05-16 오전 10:40:00
좋은글 잘 읽고 갑니다.