본문 바로가기

분류대기

[인코딩] UTF-8

[1. 개요]

UTF-8 인코딩 방식에 대해서 정리한다.

 

UTF-8 은 멀티바이트 형태의 문자 인코딩 방식이다.

  • 경우에 따라 문자 하나를 1바이트 혹은 2바이트, 3바이트 최대 4바이트로 인코딩 한다.
  • 첫번째 바이트의 bit 패턴을 통해서, 몇 바이트로 표현 된 문자인지 알 수 있다.

첫번째 byte 의 bit 패턴이 다음과 같을 때,

  • b0xxx_xxxx    => 1byte
  • b110x_xxxx    => 2byte
  • b1110_xxxx    => 3byte
  • b1111_0xxx    => 4byte

그리고 인코딩 된 바이트 수에 따라서 인코딩 패턴은 아래와 같다.

  • 1byte : b0xxx_xxxx
  • 2byte : b110x_xxxx, b10xxxxxx
  • 3byte : b1110_xxxx, b10xxxxxx, b10xxxxxx
  • 4byte : b1111_0xxx, b10xxxxxx, b10xxxxxx, b10xxxxxx

UTF-8 로 인코딩 된 문자열을 디코딩 하려면,

  • 편의 상, 유니코드로 변경하는 것을 디코딩이라 생각하도록 한다.
  • 1byte 로 인코딩 된 문자의 경우, 그 값을 그대로 사용
    => 단순히 이 값을 (wchar_t)에 저장 
  • 2byte 로 인코딩 된 문자의 경우, 하위 바이트의 하위 6bit 값과, 상위 바이트의 하위 5bit 를 결합하여 사용
    => 총 11bit 를 (wchar_t) 에 저장.
  • 3byte 로 인코딩 된 문자의 경우, MSB 를 제외 한 바이트들의 하위 6bit 값과, MSB 의 하위 4bit 를 결합하여 사용.
    => 총 16bit 를 (wchar_t) 에 저장.
  • 4byte 로 인코딩 된 문자의 경우, MSB 를 제외 한 바이트들의 하위 6bit 값과, MSB 의 하위 3bit 를 결합하여 사용.
    => 총 21bit 를 (wchar_t) 에 저장.

widechar 자료형을 의미하는 wchar_t 는 윈도우와 리눅스에서 그 바이트 크기가 다르다.

  • 윈도우: UTF-16, 2byte
  • 리눅스: UTF-32, 4byte
  • 고정 된 크기로 문자를 인코딩 한다는게 중요하다.

다만, 현대 한글은 UTF-8 로 최대 3byte 로 표현하고,

유니코드로 변경 시, 이 중 16bit 만 사용하므로, 모두 2byte 로 표현할 수 있다.

  • 유니코드로 현대 한글 자모는 [U+1100 ~ U+11FF] 범위 내 있고,
  • 현대 한글 음절은 [U+AC00 ~ U+D7A3] 범위 내 있다.

[2. 예제]

#include <iostream>
#include <string>

unsigned int utf8_to_unicode(const std::string& utf8) {
    unsigned int unicode = 0;
    size_t num_bytes = utf8.size();

    if (num_bytes == 1) {
        unicode = utf8[0];
    } else if (num_bytes == 2) {
        unicode = ((utf8[0] & 0x1F) << 6) | (utf8[1] & 0x3F);
    } else if (num_bytes == 3) {
        unicode = ((utf8[0] & 0x0F) << 12) | ((utf8[1] & 0x3F) << 6) | (utf8[2] & 0x3F);
    } else if (num_bytes == 4) {
        unicode = ((utf8[0] & 0x07) << 18) | ((utf8[1] & 0x3F) << 12) | ((utf8[2] & 0x3F) << 6) | (utf8[3] & 0x3F);
    } else {
        throw std::invalid_argument("Invalid UTF-8 sequence");
    }

    return unicode;
}

void convert_utf8_to_unicode(const std::string& input) {
    size_t i = 0;
    while (i < input.size()) {
        unsigned char first_byte = input[i];

        // 첫 바이트의 비트 패턴에 따라 바이트 시퀀스 길이를 결정
        size_t num_bytes = 0;
        if ((first_byte & 0x80) == 0x00) {
            num_bytes = 1;  // 1바이트 문자 (ASCII)
        } else if ((first_byte & 0xE0) == 0xC0) {
            num_bytes = 2;  // 2바이트 문자
        } else if ((first_byte & 0xF0) == 0xE0) {
            num_bytes = 3;  // 3바이트 문자
        } else if ((first_byte & 0xF8) == 0xF0) {
            num_bytes = 4;  // 4바이트 문자
        } else {
            throw std::invalid_argument("Invalid UTF-8 sequence");
        }

        // 현재 바이트와 그 다음 바이트들로 유니코드 코드 포인트 계산
        std::string utf8_char = input.substr(i, num_bytes);
        unsigned int unicode = utf8_to_unicode(utf8_char);

        // 유니코드 출력
        std::cout << "UTF-8 -> Unicode: U+" << std::hex << unicode << std::endl;
        i += num_bytes;
    }
}

int main()
{
    std::string str = "안녕하세요";
    convert_utf8_to_unicode(str);
    return 0;
}
출력
UTF-8 -> Unicode: U+c548
UTF-8 -> Unicode: U+b155
UTF-8 -> Unicode: U+d558
UTF-8 -> Unicode: U+c138
UTF-8 -> Unicode: U+c694

[3. 예제, 디코딩]

#include <iostream>
#include <string>

std::string wstring_to_utf8(const std::wstring& wstr)
{
    std::string result;  // 변환된 UTF-8 문자열을 저장할 변수

    // 주어진 wide string(wstr)에서 각 wchar_t 문자를 하나씩 처리
    for (wchar_t wc : wstr) {
        // 1바이트로 표현 가능한 ASCII 문자 (0x00 ~ 0x7F)
        if (wc < 0x80) {
            result.push_back(static_cast<char>(wc));  // ASCII 문자이므로 그대로 추가
        }
        // 2바이트로 표현 가능한 문자 (0x80 ~ 0x7FF)
        else if (wc < 0x800) {
            result.push_back(0xC0 | (wc >> 6));  // 상위 6비트를 0xC0와 OR하여 첫 바이트
            result.push_back(0x80 | (wc & 0x3F));  // 하위 6비트를 0x80과 OR하여 두 번째 바이트
        }
        // 3바이트로 표현 가능한 문자 (0x800 ~ 0xFFFF)
        else {
            result.push_back(0xE0 | (wc >> 12));  // 상위 4비트를 0xE0과 OR하여 첫 바이트
            result.push_back(0x80 | ((wc >> 6) & 0x3F));  // 중간 6비트를 0x80과 OR하여 두 번째 바이트
            result.push_back(0x80 | (wc & 0x3F));  // 하위 6비트를 0x80과 OR하여 세 번째 바이트
        }
    }
    return result;  // 변환된 UTF-8 문자열 반환
}

int main()
{
    std::wstring wstr = L"안녕하세요";
    std::string str = wstring_to_utf8(wstr);

    std::cout << str << std::endl;
    std::cout << "wstr length " << wstr.length() << std::endl;
    std::cout << "str  length " << str.length() << std::endl;
    return 0;
}
출력
안녕하세요
wstr length 5
str  length 15

 

'분류대기' 카테고리의 다른 글

[리눅스] goto 사용 시 컴파일  (0) 2025.01.14
[C++] struct byte align  (0) 2024.12.26
swagger-editor 설치  (0) 2024.06.12
CUDA 설치하기, [윈도우, 리눅스]  (0) 2024.05.14
[내용 채워야 됨] CUDA 사용해보기  (0) 2024.02.22