C#은 string 객체를 char 컬렉션으로 사용할 수 있게 해주는 편리한 장치를 제공합니다.
var msg = "안녕하세요";
char first = msg[0];
ReadonlySpan<char> firstTwo = msg[..2];
char key = '안';
bool contains = msg.Contains(key);
// 또는
foreach(char c in msg)
{
if (c == key )
{
contains = true; break;
}
}
닷넷은 이러한 장치들의 성능을 개선하는 방향으로 버전업 되고 있습니다.
Ordinal value
그런데, 이런 성능을 제대로 향유하기 위해서는 그 문자열을 구성하는 모든 문자가 단수 코드 유닛(single code unit)인 지 확인해야 합니다. (예를 들면, 비밀 번호 문자열).
왜냐하면, 닷넷 char 객체는 단수 코드를 갖는 유니코드 값을 나타내기 때문입니다.
char AGrave = '\u00C0';
문자열에 있는 모든 문자가 "단수 코드 값"을 가진 것이 확실하다면, 문자열을 char 컬렉션으로 처리하는 것이 아무런 문제가 없고 효율적입니다.
Character value
문제는 Unicode 문자들 중에는 복수의 코드 유닛을 갖는 것도 있는데, 이들은 char 로 표현할 수 없고, string 으로 표현해야 합니다.
string AGraveCombining = "\u0041\u0300";
AGraveCombining 은 싱글 코드 유닛 문자 두 개를 합쳐 놓은 것인데, 문화권(CultureInfo)에 따라 서로 다른 하나의 문자를 가리킬 수 있습니다.
이 경우, string 객체를 char 컬렉션으로 처리하면 하나의 문자가 두 개의 문자가 되는 문제가 발생합니다.
AGraveCombining : Á // 키보드에서 \u0030 이 입력되지 않아, \u0193 을 사용.
AGraveCombining[0] : A
AGraveCombining[1] : `
한글은?
다행히 한글은 이러한 문제가 발생하지 않습니다.
왜냐하면, 한글의 자음과 모음,
그리고 자모를 초/중/종성으로 조합한 모든 음절 마다 하나의 코드를 부여했기 때문입니다.
…
참고로, 한글은 완성형 방식으로 유니코드에 포함되었다는 점을 알 수 있습니다. 이 방식의 단점은 'ㄱ’으로 시작하는 이름 찾기, 받침이 'ㅇ’인 단어 찾기 등과 같이 한글스러운 검색이 용이하지 않거나 불가능하게 된 것입니다.
이모지는?
이모지는 문제가 될 수 있습니다.
string text = "😘";
foreach (char c in text)
{
Console.WriteLine(c);
}
결과
�
�
결과적으로, 우리가 일반적으로 사용하는 ANSI 알파벳과 특수 문자, 한/중/일 문자로만 구성된 문자열의 각 문자를 Ordinal Value 로 처리하는데 아무런 문제가 없습니다.
그러나, 문자열에 이모지가 포함될 것으로 예상되는 경우에도 Ordinal value 로 처리하면, 하나의 문자가 두 개의 코드로 쪼개지고, 쪼개진 각 코드가 유니코드 테이블에 없는 경우, 이상한 문자로 표현되는 현상이 발생합니다.
이러한 현상을 막으려면, 문자열을 Character value 의 집합으로 처리해야 할 필요가 있습니다.
StringInfo 클래스
비단 이모지 뿐만 아니라, 복수의 코드 유닛 문자를 사용하는 언어의 문자들은 string 을 char 집합이 아닌, 단일 문자를 나타내는 string의 집합으로 처리해야 합니다.
이를 위해, 닷넷은 문자열을 유니코드 문자의 집합으로 취급하게 해주는 도구를 제공합니다.
StringInfo Class (System.Globalization) | Microsoft Learn
사용법은 위 문서에 잘 나와 있어 별도로 설명하지는 않지만, 문서 가장 하단에 나타난 주의 사항 - 닷넷 프레임워크 버전 별로, 지원하는 Unicode 표준이 다르다는 점은 주의할 필요가 있을 것 같습니다.