본문 바로가기
프로그래밍/C#

C# 컴퓨터가 문자를 표현하는 방법, 문자 인코딩

by bantomak 2025. 2. 20.
반응형

string에서 byte[]로 변환하기

string 타입에서 ToCharArray() 함수를 통해서 바로 Char배열로 변환하는 함수는 존재하지만 ToByteArray() 함수는 존재하지 않는다. 그 이유는 무엇일까? 그 이유는 byte[]로 변환하기 위해서는 결정해야 할 사항들이 있기 때문이다. 변환하기 위해서는 반드시 문자 인코딩을 설정해야 한다. 왜냐면 인코딩이 무엇이냐에 따라서 결괏값이 달라지기 때문이다.

문자 집합과 문자 인코딩은 위에 그림과 같이 생각하면 이해가 빠르다.

문자 집합(Character Set, Charset)

문자 집합은 문자들의 모음이다. 즉, 어떤 문자들이 포함될지를 정의한 목록이라고 생각하면 이해가 빠르다. 예를 들어 영어 알파벳(A-Z), 숫자(0-9), 특수 기호(!, @, # 등) 등을 포함하는 문자 집합이 있을 수 있고, 한글이나 일본어, 중국어와 같이 다양한 문자들을 포함하는 문자 집합이 있을 수 있다.

 

대표적인 문자 집합

  • ASCII
    • 7비트(128개 문자)로 구성
    • 영어 알파벳, 숫자, 일부 특수 문자만 포함
    • 예: 'A' → 65, 'B' → 66, 'a' → 97
  • ISO 8859-1
    • 8비트(256개 문자)로 구성
    • 유럽 언어에서 사용되는 문자 추가
  • Unicode (유니코드)
    • 전 세계 모든 문자를 포함하는 방대한 문자 집합
    • UTF-8, UTF-16 등의 인코딩을 통해 표현됨

문자 인코딩(Character Encoding)

문자 인코딩은 문자 집합의 문자들을 컴퓨터에서 저장하고 전송할 수 있도록 이진(바이너리) 코드로 변환하는 방식을 말한다. 즉, 문자 집합이 문자들을 정의하는 '목록'이라면, 문자 인코딩은 그것을 "어떻게 저장할 것인가"를 정의하는 방식을 말한다. 결국 문자별로 매핑된 숫자를 의미한다고 보면 된다.

 

대표적인 문자 인코딩

  • ASCII 인코딩
    • 7비트 사용(0 ~ 127)
    • 예: 'A' → 01000001 (65)
  • ISO-8859-1 (Latin-1)
    • 8비트 사용 (0 ~ 255)
    • 예:'é' → 11101001 (233)
  • UTF-8 (유니코드 인코딩)
    • 가변 길이 인코딩 (1~4바이트)
    • 영어는 1바이트, 한글은 3바이트, 일부 이모지는 4바이트 사용
    • 'A' → 01000001 (1바이트)
    • '한' → 11101100 10001101 10001000 (3바이트)
  • UTF-16
    • 2바이트(16비트) 또는 4바이트 사용
    • 가변 길이 인코딩이지만, 일반적으로 2바이트로 표현
    • 예: 'A' → 00000000 01000001
    • 예: '한' → 11011000 00001010 11011111 10001000

문자 집합과 인코딩 관계에 대해서

  • 문자 집합(Character Set)은 문자의 모음을 정의하는 개념
  • 문자 인코딩(Character Encoding)은 해당 문자들을 바이너리 데이터로 변환하는 방식을 제공

예제 코드

C#은 기본적으로 UTF-16 인코딩을 사용해서 문자를 표현한다. 해당 문자를 16진수로 표현해 보자.

static void Main()
{
    // String을 Char[]로 변환 
    string str = "Hello 한국";
    char[] uchars = str.ToCharArray();

    // String은 바이트로 직접 변환할 수 없으며,
    // Encoding을 통해 변환 가능. 16바이트 생성
    byte[] ubytes = System.Text.Encoding.Unicode.GetBytes(str);

    // 보다 컴팩트한 UTF8 인코딩. 12바이트 생성
    byte[] utf8bytes = System.Text.Encoding.UTF8.GetBytes(str);

    var r = Encoding.Unicode.GetString(ubytes);
    var r1 = Encoding.UTF8.GetString(utf8bytes);
}

C# char 타입의 인코딩

  • char 타입은 2바이트(16비트) 크기를 가지며, 개별 문자를 표현한다.
  • 내부적으로 UTF-16 인코딩을 사용하여 문자를 저장한다.
  • UTF-16은 가변길이 인코딩 방식으로, 일반적인 BMP(Basic Multilingual Plane) 문자는 2바이트(16비트)로 표현되고, 일부 확장문자(이모지, 고유 문자 등)는 서로게이트 페어(Surrogate Pair, 4바이트)를 사용한다.

static void Main()
{
    // String을 Char[]로 변환 
    string str = "Hello 한국";
    char[] uchars = str.ToCharArray();

    // String은 바이트로 직접 변환할 수 없으며,
    // Encoding을 통해 변환 가능. 16바이트 생성
    byte[] ubytes = System.Text.Encoding.Unicode.GetBytes(str);

    foreach (char c in uchars)
    {
        Console.WriteLine($"'{c}'{""}:{(int)c,5}:{(int)c:X4}:{(int)c:B16}");
    }
}

UTF-16으로 인코딩된 문자열 정보를 확인 가능하다.

결과를 보면 한글(또는 기타 비영어권 문자)의 바이트 순서가 반대로 나오는 현상을 확인할 수 있다. 이는 Little Endian (LE) 방식 때문에 발생하는 현상이다.

리틀 엔디안과 빅 엔디안

  • Little Endian(LE) 기본
    • 낮은 바이트가 먼저 저장됨
    • 한 (U+D55C) > D5 5C
    • 오른쪽에서 왼쪽으로 바이트 저장
  • Big Endian(BE)
    • 높은 바이트가 먼저 저장됨
    • 한 (U+D55C) > 5C D5
    • 왼쪽에서 오른쪽으로 바이트 저장

C#에서는 기본적으로 Little Endian을 사용하므로, 한글(UTF-16)이 저장될 때 바이트 순서가 반대로 보이는 것처럼 느껴지는 것이다. 아래의 예시를 보면 바이트 순서가 서로 반대인 것을 확인할 수 있다.

정리하자면

문자열을 바이트로 변환하는 데에는 여러 가지 결정사항이 필요하다. 어떤 인코딩으로 변환해서 바이트로 표현할지가 물음표로 남아있기 때문이다. 이에 대해서 어떤 인코딩이 있는지 그리고 인코딩을 어떻게 결정할지에 대해서 더 고민해 보도록 하자.

함께 읽으면 좋은 글

 

C# 간단하게 10진수를 2진수, 16진수로 변환하기

간단하게 10진수를 2진수, 16진수로 변환하기문자열 보간을 이용하면 Convert와 같은 함수를 쓰지 않고 2진수, 16진수로 변환이 가능하다. static void Main(string[] args){ Console.WriteLine($"{133:B}"); Console.Write

jettstream.tistory.com

댓글