본문으로 바로가기

UTF8 구조 및 유니코드 변환 소스 코드

category 프로그래밍 2019. 6. 6. 19:32
반응형

오늘 포스팅할 주제는 UTF-8입니다. 

UTF-8은 유니코드를 표현하기 위한 가변 길이 문자 인코딩 방식으로 한 문자를 나타내기 위해서 1byte에서 4byte의 값을 사용합니다. 구조는 어떻게 되어 있으며, 유니코드 변환 방법을 소스 코드를 통해 확인해보겠습니다.




UTF8의 구조


UTF-8은 자리수에 따라서 표현되는 문자가 다릅니다. ASCII의 경우 1byte로 표시되고 라틴문자 그리스문자 등은 2byte로 표시되는데, 자리수에 따라 아래와 같이 표현이 됩니다.


1byte 이진법 표현 : 0xxxxxxx(0x000000 ~ 0x00007F)

ASCII 영역에서 사용하는 영역과 동일합니다.

숫자 1을 표현하게 되면 0000 0031로 나타내게 됩니다.


2byte 이진법 표현: 110xxxxx 10xxxxxx(0x000080 ~ 0x0007FF)

2Byte로 표현하는 경우 첫 byte는 110이 앞에 붙고 2번째 byte의 경우 10이 붙습니다. 


3byte 이진법 표현: 1110xxxx 10xxxxxx 10xxxxxx(0x000800 ~ 0x00FFFF)

3byte로 표현하는 경우 첫 byte는 1110, 2번째 Byte는 10, 3번째 Byte에는 10이 붙습니다.


4byte 이진법 표현: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

(0x010000 ~ 0x10FFFF)

4byte로 표현하는 경우 첫 byte는 11110, 2번째는 10, 3번째는 10, 4번째는 10이 붙습니다.



UTF-8 표현 규칙 예시


Arabic은 UTF-8로 표현을 하면 2byte로 표현이 가능합니다. 

 0xD88B ؋ 

 

 위에 UTF8의 구조에서 2byte로 표현된 문자를 2진법으로 표시하게 되면 110x xxxx 10xx xxxx가 된다고 하였는데, 0xD88B을 2진법으로 표현하게 되면 1101 1000 1000 1011로 표현이 됩니다. 0110 0000 1011가 유니코드가 됩니다.



UTF8 Character length table


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const uint8 UTF8_Byte[0x100=
{
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x00-0x0F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x10-0x1F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x20-0x2F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x30-0x3F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x40-0x4F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x50-0x5F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x60-0x6F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x70-0x7F */
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0/* 0x80-0x8F */
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0/* 0x90-0x9F */
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0/* 0xA0-0xAF */
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0/* 0xB0-0xBF */
   0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2/* 0xC0-0xCF */
   2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2/* 0xD0-0xDF */
   3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3/* 0xE0-0xEF */
   4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0/* 0xF0-0xFF */
};
 


유니코드의 한 문자를 표현하기 위해서 1Byte에서 4Byte의 값을 사용한다고 하였는데요. 위에 Table은 UTF8의 특정 영역에 몇 Byte로 한 문자를 표현했는지 나타내는 Table입니다.


아래 URL은 UTF8 Table을 영역별로 볼 수 있는 사이트입니다.

https://www.utf8-chartable.de/unicode-utf8-table.pl


특정 영역에 몇 Byte로 표현했는지 표현 방법은 간단합니다. 위에 URL에서 UTF8의 앞자리만 확인을 하면 됩니다. 우선 위 Table에 0x00 ~ 0x0F 영역의 경우 1로 표기가 되어있는데, ASCII 영역의 경우 1byte를 사용하기 때문에 1을 사용하게 됩니다.


중간에 있는 영역의 경우 0으로 표기되어있는데, UTF8 Table을 확인해보면 알겠지만 0x80 ~ 0xC1을 앞자리에 포함하고 있는 Character는 없습니다. 


0xC2 ~ 0xDF 영역은 2로 표기가 되어있는데 쉽게 이해하기 위해서 아래 사진을 보겠습니다. 아래 사진은 제가 첨부한 UTF8 Table URL에서 가져온 일부 영역입니다.



ASCII 영역 이후로는 바로 0xC280 영역이 나옵니다. 즉 앞자리가 0xC2인 경우는 2byte의 HEX로 표현이 됩니다.



0xDFBF 이후로는 3byte의 HEX로 표현이 됩니다. 즉 앞자리가 0xC2 ~ 0xDF 사이인 경우에 2byte로 표현이 됩니다.




소스 코드(UTF8 -> 유니코드)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
uint8 buff[4= {0xd70x87};
uint8 UTF8_Byte_val;
 
const uint8 UTF8_Byte_table[0x100=
{
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x00-0x0F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x10-0x1F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x20-0x2F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x30-0x3F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x40-0x4F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x50-0x5F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x60-0x6F */
   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1/* 0x70-0x7F */
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0/* 0x80-0x8F */
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0/* 0x90-0x9F */
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0/* 0xA0-0xAF */
   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0/* 0xB0-0xBF */
   0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2/* 0xC0-0xCF */
   2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2/* 0xD0-0xDF */
   3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3/* 0xE0-0xEF */
   4,4,4,4,4,0,0,0,0,0,0,0,0,0,0,0/* 0xF0-0xFF */
};
 
uint32 UTF8_Type_Check(uint8 num);
 
int main(void)
{
    printf("%x\r\n", UTF8_Type_Check(0));
 
    return 0;
}
 
uint32 UTF8_Type_Check(uint8 num)
{
    uint32 UTF8toUnicode = 0;
    uint8 UTF8_byte = UTF8_Byte_table[buff[num]];
 
    switch (UTF8_byte) {
        case 4:    
            UTF8toUnicode += (buff[num++& 0x07); UTF8toUnicode <<= 6;
            UTF8toUnicode += (buff[num++& 0x3f); UTF8toUnicode <<= 6;
            UTF8toUnicode += (buff[num++& 0x3f); UTF8toUnicode <<= 6;
            UTF8toUnicode += (buff[num] & 0x3f);
            UTF8toUnicode = 4;
            break;
 
        case 3:    
            UTF8toUnicode += (buff[num++& 0x0f);     UTF8toUnicode <<= 6;
            UTF8toUnicode += (buff[num++& 0x3f);     UTF8toUnicode <<= 6;
            UTF8toUnicode += (buff[num] & 0x3f);
            UTF8_Byte_val = 3;
            break;
 
        case 2:    
            UTF8toUnicode += (buff[num++& 0x1f); UTF8toUnicode <<= 6;
            UTF8toUnicode += (buff[num] & 0x3f);
            UTF8_Byte_val = 2;
            break;
 
        case 1:    
            UTF8toUnicode += buff[num];
            UTF8_Byte_val = 1;
            break;
    }
 
    return UTF8toUnicode;
}


Buffer안에 UTF-8의 값을 넣어주면 유니코드로 변환해주는 코드입니다.


UTF8_Byte_table[buff[num]];

위의 Table에서는 Buffer에 앞자리만 확인을 하여 UTF-8이 몇 Byte인지 확인을 해주고, Switch안에서는 Byte의 자리수에 따라 유니코드로 변환을 해줍니다.


UTF8toUnicode += (buff[num++] & 0x1f); UTF8toUnicode <<= 6;

UTF8toUnicode += (buff[num] & 0x3f);



110x xxxx 10xxx xxxx 1번째 Byte엔 0x1F를 2번째 Byte엔 0x3F를 &연산을 해주었는데, 위에 표현 규칙을 적용한 것입니다. 

x xxxxxxx xxxx 파란색 5자리를 좌로 6번째에 이동하고 초록색 7자리를 더해 유니코드가 완성이 되었습니다.



반응형