C#에서 문자열을 비교하는 방법

문자열을 비교하여 "이 두 문자열이 같은가요?" 또는 "정렬할 때 이러한 문자열을 어떤 순서로 배치해야 하나요?"라는 두 가지 질문 중 하나에 대답합니다.

이러한 두 가지 질문은 문자열 비교에 영향을 주는 요소에 의해 복잡해 집니다.

  • 서수 또는 언어 비교를 선택할 수 있습니다.
  • 대/소문자를 구분할지 여부를 선택할 수 있습니다.
  • 문화권별 비교를 선택할 수 있습니다.
  • 언어적 비교는 문화권 및 플랫폼에 따라 다릅니다.

System.StringComparison 열거형 필드는 다음 선택 항목을 나타냅니다.

  • CurrentCulture: 문화권 구분 정렬 규칙과 현재 문화권을 사용하여 문자열을 비교합니다.
  • CurrentCultureIgnoreCase: 문화권 구분 정렬 규칙, 현재 문화권을 사용하여 문자열을 비교하고 비교되는 문자열의 경우를 무시합니다.
  • InvariantCulture: 문화권 구분 정렬 규칙과 고정 문화권을 사용하여 문자열을 비교합니다.
  • InvariantCultureIgnoreCase: 문화권 구분 정렬 규칙, 고정 문화권을 사용하여 문자열을 비교하고 비교되는 문자열의 경우를 무시합니다.
  • 서수: 서수(이진) 정렬 규칙을 사용하여 문자열을 비교합니다.
  • OrdinalIgnoreCase: 서수(이진) 정렬 규칙을 사용하여 문자열을 비교하고 비교되는 문자열의 경우를 무시합니다.

참고 항목

이 문서의 C# 예제는 Try.NET 인라인 코드 러너 및 놀이터에서 실행됩니다. 대화형 창에서 예제를 실행하려면 실행 버튼을 선택합니다. 코드를 실행하면 실행을 다시 선택하여 코드를 수정하고 수정된 코드를 실행할 수 있습니다. 수정된 코드는 대화형 창에서 실행되거나, 컴파일이 실패하면 대화형 창에 모든 C# 컴파일러 오류 메시지가 표시됩니다.

문자열을 비교할 때 순서를 정의합니다. 비교는 문자열 시퀀스를 정렬하는 데 사용됩니다. 시퀀스가 알려진 순서대로 되면 소프트웨어와 사람 모두 검색하기가 더 쉽습니다. 문자열이 같으면 다른 비교가 검사 수 있습니다. 이러한 동일성 검사 같음과 유사하지만 대/소문자 차이와 같은 일부 차이점은 무시될 수 있습니다.

기본 서수 비교

기본적으로 가장 일반적인 작업:

string root = @"C:\users";
string root2 = @"C:\Users";

bool result = root.Equals(root2);
Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");

result = root.Equals(root2, StringComparison.Ordinal);
Console.WriteLine($"Ordinal comparison: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");

Console.WriteLine($"Using == says that <{root}> and <{root2}> are {(root == root2 ? "equal" : "not equal")}");

기본 서수 비교는 문자열을 비교할 때 언어 규칙을 고려하지 않습니다. 두 문자열에서 각 Char 개체의 이진값을 비교합니다. 결과적으로, 기본 서수 비교도 대/소문자를 구분합니다.

String.Equals, ==!= 연산자를 사용한 같음 테스트는 String.CompareToCompare(String, String) 메서드를 사용한 문자열 비교와 다릅니다. 모두 대/소문자를 구분하여 비교합니다. 그러나 같음 테스트는 서수 비교를 수행하는 반면 CompareToCompare 메서드는 현재 문화권을 사용하여 문화권 인식 언어 비교를 수행합니다. 수행할 비교 유형을 명시적으로 지정하는 오버로드를 호출하여 코드의 의도를 명확하게 합니다.

대/소문자를 구분하지 않는 서수 비교

String.Equals(String, StringComparison) 메서드를 사용하면 대/소문자를 구분하지 않는 서수 비교를 위해 StringComparison.OrdinalIgnoreCaseStringComparison 값을 지정할 수 있습니다. 인수의 값을 StringComparison.OrdinalIgnoreCaseStringComparison 지정하는 경우 대/소문자를 구분하지 않는 서수 비교를 수행하는 정적 String.Compare(String, String, StringComparison) 메서드도 있습니다. 이러한 비교는 다음 코드에 나와 있습니다.

string root = @"C:\users";
string root2 = @"C:\Users";

bool result = root.Equals(root2, StringComparison.OrdinalIgnoreCase);
bool areEqual = String.Equals(root, root2, StringComparison.OrdinalIgnoreCase);
int comparison = String.Compare(root, root2, comparisonType: StringComparison.OrdinalIgnoreCase);

Console.WriteLine($"Ordinal ignore case: <{root}> and <{root2}> are {(result ? "equal." : "not equal.")}");
Console.WriteLine($"Ordinal static ignore case: <{root}> and <{root2}> are {(areEqual ? "equal." : "not equal.")}");
if (comparison < 0)
    Console.WriteLine($"<{root}> is less than <{root2}>");
else if (comparison > 0)
    Console.WriteLine($"<{root}> is greater than <{root2}>");
else
    Console.WriteLine($"<{root}> and <{root2}> are equivalent in order");

이러한 메서드는 대/소문자를 구분하지 않는 서수 비교를 수행할 때 고정 문화 권의 대/소문자 구분 규칙을 사용합니다.

언어 비교

많은 문자열 비교 메서드(예: String.StartsWith)는 기본적으로 현재 문화권 언어 규칙을 사용하여 입력 순서를 지정합니다. 이 언어 비교를 "단어 정렬 순서"라고도 합니다. 언어 비교를 수행할 때 일부 무수 유니코드 문자에는 특수 가중치가 할당될 수 있습니다. 예를 들어 하이픈 "-"에는 작은 가중치가 할당되어 "co-op" 및 "coop"이 정렬 순서대로 나란히 표시될 수 있습니다. 일부 인쇄되지 않는 컨트롤 문자는 무시될 수 있습니다. 또한 일부 유니코드 문자는 인스턴스 시 Char 퀀스에 해당할 수 있습니다. 다음 예제에서는 "거리에서 춤을 춘다"라는 문구를 독일어로 사용하고 한 문자열에 "ss"(U+0073 U+0073)를 사용하고 다른 문자열에는 'ß'(U+00DF)를 사용합니다. 언어적으로(Windows의 경우) "ss"는 "en-US" 및 "de-DE" 문화권에서 독일어 Esszet: 'ß' 문자와 같습니다.

string first = "Sie tanzen auf der Straße.";
string second = "Sie tanzen auf der Strasse.";

Console.WriteLine($"First sentence is <{first}>");
Console.WriteLine($"Second sentence is <{second}>");

bool equal = String.Equals(first, second, StringComparison.InvariantCulture);
Console.WriteLine($"The two strings {(equal == true ? "are" : "are not")} equal.");
showComparison(first, second);

string word = "coop";
string words = "co-op";
string other = "cop";

showComparison(word, words);
showComparison(word, other);
showComparison(words, other);
void showComparison(string one, string two)
{
    int compareLinguistic = String.Compare(one, two, StringComparison.InvariantCulture);
    int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal);
    if (compareLinguistic < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using invariant culture");
    else if (compareLinguistic > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using invariant culture");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using invariant culture");
    if (compareOrdinal < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison");
    else if (compareOrdinal > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison");
}

Windows에서는 .NET 5 이전에는 언어 비교에서 서수 비교로 변경하면 "cop", "coop" 및 "co-op"의 정렬 순서가 변경됩니다. 두 개의 독일어 문장도 서로 다른 비교 형식을 사용하여 다르게 비교됩니다. .NET 5 이전에는 .NET 세계화 API에서 NLS(국가 언어 지원) 라이브러리를 사용했습니다. .NET 5 이상 버전에서 .NET 세계화 API는 지원되는 모든 운영 체제에서 .NET의 세계화 동작을 통합하는 ICU(유니코드용 국제 구성 요소) 라이브러리를 사용합니다.

특정 문화권을 사용한 비교

다음 예제에서는 en-US 및 de-DE 문화권에 대 한 개체를 저장 CultureInfo 합니다. 비교는 문화권별 비교를 보장하기 위해 CultureInfo 개체를 사용하여 수행됩니다. 사용된 문화권은 언어 비교에 영향을 줍니다. 다음 예에서는 "en-US" 문화권과 "de-DE" 문화권을 사용하여 두 개의 독일어 문장을 비교한 결과를 보여줍니다.

string first = "Sie tanzen auf der Straße.";
string second = "Sie tanzen auf der Strasse.";

Console.WriteLine($"First sentence is <{first}>");
Console.WriteLine($"Second sentence is <{second}>");

var en = new System.Globalization.CultureInfo("en-US");

// For culture-sensitive comparisons, use the String.Compare
// overload that takes a StringComparison value.
int i = String.Compare(first, second, en, System.Globalization.CompareOptions.None);
Console.WriteLine($"Comparing in {en.Name} returns {i}.");

var de = new System.Globalization.CultureInfo("de-DE");
i = String.Compare(first, second, de, System.Globalization.CompareOptions.None);
Console.WriteLine($"Comparing in {de.Name} returns {i}.");

bool b = String.Equals(first, second, StringComparison.CurrentCulture);
Console.WriteLine($"The two strings {(b ? "are" : "are not")} equal.");

string word = "coop";
string words = "co-op";
string other = "cop";

showComparison(word, words, en);
showComparison(word, other, en);
showComparison(words, other, en);
void showComparison(string one, string two, System.Globalization.CultureInfo culture)
{
    int compareLinguistic = String.Compare(one, two, en, System.Globalization.CompareOptions.None);
    int compareOrdinal = String.Compare(one, two, StringComparison.Ordinal);
    if (compareLinguistic < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using en-US culture");
    else if (compareLinguistic > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using en-US culture");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using en-US culture");
    if (compareOrdinal < 0)
        Console.WriteLine($"<{one}> is less than <{two}> using ordinal comparison");
    else if (compareOrdinal > 0)
        Console.WriteLine($"<{one}> is greater than <{two}> using ordinal comparison");
    else
        Console.WriteLine($"<{one}> and <{two}> are equivalent in order using ordinal comparison");
}

문화권별 비교는 일반적으로 사용자가 입력한 문자열을 사용자가 입력한 다른 문자열과 비교하여 정렬하는 데 사용됩니다. 이러한 문자열의 문자 및 정렬 규칙은 사용자 컴퓨터의 로캘에 따라 달라질 수 있습니다. 똑같은 문자가 포함된 문자열이라도 현재 스레드의 문화권에 따라 다르게 정렬될 수 있습니다.

배열의 언어적 정렬 및 문자열 검색

다음 예에서는 현재 문화권에 따라 언어 비교를 사용하여 배열에서 문자열을 정렬하고 검색하는 방법을 보여줍니다. System.StringComparer 매개 변수를 사용하는 정적 Array 메서드를 사용합니다.

다음 예제에서는 현재 문화권을 사용하여 문자열 배열을 정렬하는 방법을 보여 줍니다.

string[] lines = new string[]
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};

Console.WriteLine("Non-sorted order:");
foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

Console.WriteLine("\n\rSorted order:");

// Specify Ordinal to demonstrate the different behavior.
Array.Sort(lines, StringComparer.CurrentCulture);

foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

배열이 정렬되면 이진 검색을 사용하여 항목을 검색할 수 있습니다. 이진 검색은 컬렉션의 중간에서 시작하여 컬렉션의 절반에서 검색 문자열이 포함되어 있는지 확인합니다. 이후의 각 비교는 컬렉션의 나머지 부분을 절반으로 세분합니다. 배열은 StringComparer.CurrentCulture을 사용하여 정렬됩니다. 로컬 함수 ShowWhere는 문자열이 발견된 위치에 대한 정보를 표시합니다. 문자열을 찾을 수 없는 경우 반환된 값은 발견된 그 위치를 나타냅니다.

string[] lines = new string[]
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};
Array.Sort(lines, StringComparer.CurrentCulture);

string searchString = @"c:\public\TEXTFILE.TXT";
Console.WriteLine($"Binary search for <{searchString}>");
int result = Array.BinarySearch(lines, searchString, StringComparer.CurrentCulture);
ShowWhere<string>(lines, result);

Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}");

void ShowWhere<T>(T[] array, int index)
{
    if (index < 0)
    {
        index = ~index;

        Console.Write("Not found. Sorts between: ");

        if (index == 0)
            Console.Write("beginning of sequence and ");
        else
            Console.Write($"{array[index - 1]} and ");

        if (index == array.Length)
            Console.WriteLine("end of sequence.");
        else
            Console.WriteLine($"{array[index]}.");
    }
    else
    {
        Console.WriteLine($"Found at index {index}.");
    }
}

컬렉션의 서수 정렬 및 검색

다음 코드에서는 System.Collections.Generic.List<T> 컬렉션 클래스를 사용하여 문자열을 저장합니다. 문자열은 List<T>.Sort 메서드를 사용하여 정렬됩니다. 이 메서드에는 두 문자열을 비교하고 정렬하는 대리자가 필요합니다. String.CompareTo 메서드는 비교 함수를 제공합니다. 이 샘플을 실행하고 순서를 관찰합니다. 이 정렬 작업은 서수 대/소문자 구분 정렬을 사용합니다. 정적 String.Compare 메서드를 사용하여 여러 비교 규칙을 지정합니다.

List<string> lines = new List<string>
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};

Console.WriteLine("Non-sorted order:");
foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

Console.WriteLine("\n\rSorted order:");

lines.Sort((left, right) => left.CompareTo(right));
foreach (string s in lines)
{
    Console.WriteLine($"   {s}");
}

정렬된 후에는 이진 검색을 사용하여 문자열 목록을 검색할 수 있습니다. 다음 샘플에서는 동일한 비교 함수를 사용하여 정렬된 목록을 검색하는 방법을 보여줍니다. 로컬 함수 ShowWhere는 검색된 텍스트가 어디에 있는지를 보여줍니다.

List<string> lines = new List<string>
{
    @"c:\public\textfile.txt",
    @"c:\public\textFile.TXT",
    @"c:\public\Text.txt",
    @"c:\public\testfile2.txt"
};
lines.Sort((left, right) => left.CompareTo(right));

string searchString = @"c:\public\TEXTFILE.TXT";
Console.WriteLine($"Binary search for <{searchString}>");
int result = lines.BinarySearch(searchString);
ShowWhere<string>(lines, result);

Console.WriteLine($"{(result > 0 ? "Found" : "Did not find")} {searchString}");

void ShowWhere<T>(IList<T> collection, int index)
{
    if (index < 0)
    {
        index = ~index;

        Console.Write("Not found. Sorts between: ");

        if (index == 0)
            Console.Write("beginning of sequence and ");
        else
            Console.Write($"{collection[index - 1]} and ");

        if (index == collection.Count)
            Console.WriteLine("end of sequence.");
        else
            Console.WriteLine($"{collection[index]}.");
    }
    else
    {
        Console.WriteLine($"Found at index {index}.");
    }
}

정렬 및 검색 시 항상 동일한 형식의 비교를 사용하도록 하세요. 정렬 및 검색에 대해 서로 다른 비교 형식을 사용하면 예기치 않은 결과가 발생합니다.

System.Collections.Hashtable, System.Collections.Generic.Dictionary<TKey,TValue>, System.Collections.Generic.List<T> 등의 컬렉션 클래스는 요소 또는 키의 형식이 string인 경우 System.StringComparer 매개 변수를 사용하는 생성자를 포함합니다. 일반적으로 가능하면 이러한 생성자를 사용하고 StringComparer.Ordinal 또는 StringComparer.OrdinalIgnoreCase를 지정해야 합니다.

참고 항목