C# - StringComparer

땡구의 개발일지·2025년 11월 13일

C# 공부 박살내기

목록 보기
41/42
post-thumbnail

문자열 비교에 사용되는 클래스

🚩 StringComparer

StringComparer 클래스(Microsoft Learn)

  • StringComparer는 문자열 비교(정렬, 탐색, 키 비교 등)에 사용되는 비교자(Comparer)와 해시 정책(EqualityComparer)을 제공하는 클래스다.

  • IComparer<string>IEqualityComparer<string>를 동시에 구현한 문자열 전용 비교자.


🎯 장점:

  • 정렬(예: Array.Sort)이나 컬렉션(예: Dictionary, HashSet)에 비교 규칙과 해시 규칙을 일관되게 적용할 수 있음.

  • 문화권(culture) 민감성, 대소문자 구분, 바이트순(ordinal) 비교 등 다양한 비교 방식 제공.


✅ StringComparer vs StringComparison

  • StringComparison : string의 인스턴스 메서드들(Equals, IndexOf, CompareTo 등)에 주는 옵션(enum). (ex. StringComparison.OrdinalIgnoreCase)

  • StringComparer : 컬렉션이나 정렬 API에 넣어 쓰는 비교자/해시 제공 객체. (StringComparer.OrdinalIgnoreCase 등)

두 개념은 같은 비교 규칙을 표현하지만, 용도가 다르다. 컬렉션에서는 StringComparer를 쓴다.


📦 주요 내장 비교자

  • StringComparer.Ordinal

    • 바이트/코드유닛 기준(UTF-16 코드 단위)으로 비교함.
    • 가장 빠름. 문화권 규칙 없음. 대소문자 구분.
    • 권장: 내부 식별자, 파일 경로 비교(일관성 필요), 성능 우선인 경우.
  • StringComparer.OrdinalIgnoreCase

    • Ordinal 기반의 대소문자 무시 비교.
    • 빠름. 대소문자, 식별자 무시가 필요할 때 쓴다.
  • StringComparer.CurrentCulture / CurrentCultureIgnoreCase

    • 문화권에 따른 비교에 쓰임
    • UI에 표시되는 정렬(예: 사용자에게 보여주는 목록)에 적합.
    • 느림.
  • StringComparer.InvariantCulture / InvariantCultureIgnoreCase

    • 문화권 독립적이지만 인간 친화적 정렬(예: "ä"의 정렬 규칙 등)을 제공.
    • CurrentCulture보다는 일관성 있는 결과(언제나 동일), 하지만 Ordinal보다 느림.
  • StringComparer.Create(CultureInfo culture, bool ignoreCase)

    • 특정 지역(culture)을 지정해 생성 가능.

💡 왜 Ordinal이 빠른가?

  • Ordinal 비교는 문자열의 UTF-16 코드 단위(즉, 정수 값)를 바로 비교.

  • 구현이 단순 → 분기 적고 CPU 캐시 친화적 → 빠름.

  • 문화권 비교(CompareInfo)는 언어 규칙, 접두사/접미사, 합성 문자(normalization), 복잡한 유니코드 규칙 등을 처리해야 하므로 추가 로직과 테이블 접근이 필요함. 이 때문에 더 느림.


📌 StringComparer가 컬렉션 동작에 주는 영향

  • Dictionary 생성 시
    new Dictionary<string, TValue>(StringComparer.OrdinalIgnoreCase)
    • 위와 같이 적용하면, 키 비교와 키 해시가 동일한 규칙으로 동작한다!

예: 대소문자 무시 딕셔너리

var dict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
dict["Apple"] = 1;
Console.WriteLine(dict.ContainsKey("apple")); // true

📝 실전 코드 예제

(A) 배열 정렬에 적용

string[] arr = new[] { "d", "cc", "bbb", "aaaa" };

// 문화권 무시, 빠른 사전식 정렬
Array.Sort(arr, StringComparer.Ordinal);
// 또는
Array.Sort(arr, StringComparer.OrdinalIgnoreCase);

(B) Dictionary / HashSet에 적용

var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
set.Add("Hello");
Console.WriteLine(set.Contains("hello")); // true

(C) 사용자 로컬 정렬(화면 표시용)

// 사용자의 문화권에 따른 정렬을 원할 때
Array.Sort(arr, StringComparer.CurrentCulture);

❓ 언제 어떤 비교자를 선택할까?

  • 아이덴티티 / 내부 키 / 파일명 비교 / 성능 최우선 → StringComparer.Ordinal 또는 OrdinalIgnoreCase

    • 이유: 빠르고 예측 가능(문화권에 따라 달라지지 않음).
  • UI에서 사용자에게 보여줄 정렬(사람 기준) → StringComparer.CurrentCulture 또는 InvariantCulture

    • 이유: 언어별 정렬 규칙을 따름.
  • 네트워크/저장된 키 비교(교차 플랫폼/언어 간 일관성) → StringComparer.Ordinal

    • 이유: 문화권 독립적이고 안정적인 결과.
  • 대소문자 무시 필요 → OrdinalIgnoreCase 우선 고려. 성능과 일관성 면에서 보통 최선.


❗ 주의사항

  • 문화권 비교로 키를 만들면 안 되는 상황에서는 쓰면 안됨
    • 예: DB 키, 파일 시스템 내부 식별자, 프로토콜 식별자 등.
  • 문화권 비교는 동일 문자열이라도 다른 시스템/환경에서 다른 결과를 낼 수 있음.

🎯 요약

  • StringComparer는 비교(정렬)와 해시(동등성)를 일관성 있게 제공하는 클래스.
  • 성능: Ordinal 계열이 가장 빠름 → 식별자/키/대량 정렬에 사용.
  • 사용자 친화적 정렬: CurrentCulture / InvariantCulture를 사용.
  • 컬렉션에 비교자를 넘길 때는 항상 StringComparer 사용(특히 대소문자 무시 컬렉션).
profile
개발 박살내자

0개의 댓글