기술 노트

문자열 인코딩의 이해: ASCII, UTF-8, CP949, EUC-KR

anothel 2025. 1. 8. 08:59

1. 개요

문자열 데이터를 처리할 때, 인코딩 방식에 따라 문자의 바이트 구조가 달라진다. 특히, ASCII 문자는 단일 바이트로 표현되지만, UTF-8, ANSI, EUC-KR, CP949와 같은 인코딩에서는 비ASCII 문자가 여러 바이트로 표현될 수 있다.

이 글에서는 각 인코딩 방식의 바이트 구조와 특징을 비교하고, 이를 C 코드로 처리하는 방법을 소개한다. 특히 한글과 같은 다중 바이트 문자를 정확히 판별하고 처리하기 위한 실용적인 접근법을 제시한다.

2. 인코딩별 문자 구조와 판별법

2.1. ASCII

  • 범위
    • 0x00 ~ 0x7F (0 ~ 127)
  • 특징
    • 모든 문자가 1바이트로 표현된다.
  • 판별법
    • 바이트 값이 0x00 ~ 0x7F에 해당하면 ASCII 문자이다.

2.2. UTF-8

UTF-8은 가변 길이의 문자 인코딩 방식으로, 문자에 따라 1~4바이트를 사용한다. 첫 번째 바이트는 문자 길이를 결정하며, 이어지는 바이트들은 특정 비트 패턴을 따른다.

2.2.1. 첫 번째 바이트의 규칙

첫 번째 바이트 바이트 수 코드포인트 범위 설명
0xxxxxxx 1 U+0000 ~ U+007F ASCII 문자
110xxxxx 2 U+0080 ~ U+07FF 2바이트 문자
1110xxxx 3 U+0800 ~ U+FFFF 3바이트 문자
11110xxx 4 U+10000 ~ U+10FFFF 4바이트 문자

2.2.2. 바이트 패턴

  • 1바이트 (ASCII): 0xxxxxxx
    • 예: 'A' (U+0041) → 0x41
  • 2바이트: 110xxxxx 10xxxxxx
    • 예: 'Ç' (U+00C7) → 0xC3 0x87
  • 3바이트: 1110xxxx 10xxxxxx 10xxxxxx
    • 예: '한' (U+D55C) → 0xED 0x95 0x9C
  • 4바이트: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    • 예: '😊' (U+1F60A) → 0xF0 0x9F 0x98 0x8A

2.2.3. UTF-8의 제한 사항

  • 최소 바이트 수 사용
    • 가능한 최소 바이트로만 표현해야 한다.
  • Surrogate 영역 금지
    • U+D800 ~ U+DFFF는 UTF-8에서 사용할 수 없다.

2.3. ANSI와 CP949

ANSI는 특정 지역(로컬) 환경의 문자 세트를 표현하기 위한 인코딩이며, 한국어 환경에서는 CP949를 의미한다.

2.3.1. ANSI의 바이트 구조

  • 1바이트 문자: 0x00 ~ 0x7F
    • ASCII와 동일하다.
  • 2바이트 문자:
    • 첫 번째 바이트 (Lead Byte): 0x81 ~ 0xFE
    • 두 번째 바이트 (Trail Byte): 0x41 ~ 0xFE

2.3.2. CP949의 특징

  • KS X 1001(구 KS C 5601) 기반의 한국어 완성형 문자를 지원한다.
  • 한글 11,172자를 포함한 확장 문자를 지원한다.
  • EUC-KR을 기반으로 더 많은 문자를 표현한다.

2.4. EUC-KR

EUC-KR은 한국어 완성형 문자를 지원하기 위해 설계된 고정 길이 2바이트 문자셋이다.

2.4.1. 바이트 구조

  • 1바이트 문자: 0x00 ~ 0x7F (ASCII와 동일).
  • 2바이트 문자:
    • 첫 번째 바이트: 0xA1 ~ 0xFE
    • 두 번째 바이트: 0xA1 ~ 0xFE

2.4.2. EUC-KR과 CP949 비교

구분 EUC-KR CP949
1바이트 문자 ASCII (0x00 ~ 0x7F) ASCII (0x00 ~ 0x7F)
2바이트 문자  Lead: 0xA10xFE, Trail: 0xA10xFE Lead: 0x810xFE, Trail: 0x410xFE
지원 문자 수  한글 2,350자 한글 11,172자

3. 다양한 인코딩 처리 방법

다음은 C 코드로 ASCII, UTF-8, ANSI(CP949), EUC-KR 문자를 판별하고 처리하는 예제이다.

#include <stdio.h>

typedef struct CharCounter {
    unsigned int asciiCount;       // ASCII 문자 개수
    unsigned int cp949TwoByteCount; // CP949 2바이트 문자 개수
    unsigned int utf8ThreeByteCount; // UTF-8 3바이트 문자 개수
    unsigned int utf8FourByteCount; // UTF-8 4바이트 문자 개수
} CharCounter;

void AnalyzeCharEncoding(CharCounter *counter, unsigned char currentByte) {
    static unsigned char currentState = 0;    // 현재 상태 (0: 기본, 1: CP949 첫 바이트, 2: UTF-8 처리 중)
    static unsigned int utf8BytesRemaining = 0; // UTF-8 남은 바이트 수
    static unsigned char firstByte = 0;      // CP949 첫 바이트 저장

    if (currentState == 1) { // CP949 두 번째 바이트 확인
        if (currentByte >= 0x41 && currentByte <= 0xFE) { // CP949 두 번째 바이트 조건
            counter->cp949TwoByteCount++;
        }
        currentState = 0; // 상태 초기화
    } else if (utf8BytesRemaining > 0) { // UTF-8 처리 중
        if ((currentByte & 0xC0) == 0x80) { // UTF-8 연속 바이트 확인
            utf8BytesRemaining--;
            if (utf8BytesRemaining == 0) { // UTF-8 문자 완성
                counter->utf8ThreeByteCount++;
                currentState = 0; // 상태 초기화
            }
        } else {
            // UTF-8 형식이 아니면 CP949 두 번째 바이트로 판단
            if (currentByte >= 0x41 && currentByte <= 0xFE) {
                counter->cp949TwoByteCount++;
            }
            currentState = 0; // 상태 초기화
        }
    } else if ((currentByte & 0xF0) == 0xE0) { // UTF-8 3바이트 문자 시작
        utf8BytesRemaining = 2; // 남은 바이트 수 설정
        currentState = 2; // UTF-8 상태로 설정
    } else if ((currentByte >= 0x81 && currentByte <= 0xFE)) { // CP949 첫 바이트
        currentState = 1; // CP949 상태 설정
        firstByte = currentByte; // 첫 바이트 저장
    } else if (currentByte <= 0x7F) { // ASCII 문자
        counter->asciiCount++;
    } else {
        // 알 수 없는 문자 → 상태 초기화
        currentState = 0;
    }
}

int main() {
    CharCounter counter = {0};
    unsigned char testInput[] = {0x41, 0xE0, 0x41, 0xE0, 0x80, 0x80, 0xB0, 0xA1, 0x43, 0};
    int index = 0;

    while (testInput[index]) {
        AnalyzeCharEncoding(&counter, testInput[index]);
        index++;
    }

    printf("ASCII 문자 개수: %d\n", counter.asciiCount);
    printf("CP949 2바이트 문자 개수: %d\n", counter.cp949TwoByteCount);
    printf("UTF-8 3바이트 문자 개수: %d\n", counter.utf8ThreeByteCount);
    printf("UTF-8 4바이트 문자 개수: %d\n", counter.utf8FourByteCount);

    return 0;
}

4. 결론

문자열 데이터를 처리할 때, 각 인코딩의 특징과 바이트 구조를 이해하는 것이 중요하다.

  • ASCII: 단일 바이트 문자.
  • UTF-8: 가변 길이 인코딩으로 유니코드와 확장 문자를 지원한다.
  • ANSI/CP949: 지역별 문자 지원. 한국어 환경에서는 CP949로 사용한다.
  • EUC-KR: 고정 길이 2바이트 문자셋으로, CP949에 비해 한계가 있다.

각 인코딩의 규칙을 정확히 이해하고, 적절한 처리를 통해 다양한 문자셋을 지원하는 안정적인 프로그램을 개발할 수 있다.

 

728x90