티스토리 뷰

ASCII와 UTF-8, 문자열 인코딩

세댕댕이 2022. 8. 13. 18:10

문자열 인코딩(Character Encoding)이란 2진법을 사용하는 컴퓨터가 인간의 언어를 일정한 규칙에 따라 2진수로 변환하는 방식을 뜻한다.

- 2진수와 문자를 일대일 대응하도록 만들어 처리하는 방식을 사용한다

- 아스키코드, EUC-KR, UTF-8, UTF-16 등...

 

문자 집합(Charset)은 사용할 수 있는 문자들의 집합을 뜻한다

- 유니코드, ISO-8859, ASCII 등이 이에 해당.

 

문자열 인코딩은 문자를 코드로 표현하는 방식을 일컫는다. 유니코드 문자 집합을 표현하는 문자열 인코딩 방식으로 UTF-8, UTF-16 등이 있는 것. 대체로 문자 집합과 문자열 인코딩은 혼용해서 사용되어진다.

 

 

# ASCII

- 처음으로 표준을 정립한 문자열 인코딩 방식

- 0부터 127까지 총 128개의 숫자로 표현

- 7bit(문자) + 1bit(Parity Bit) 합 8bit(1byte)로 표현한다.

- 영어 이외의 다른 나라 문자를 표현할 수가 없다는 한계점

- 'A' (65), 'a' (97) / 'Z' (90) 'z' (122)

 

# EUC-KR

- ASCII로는 한글 표현이 안되니 독자적으로 만들어낸 한국어 문자집합.

- 아스키 코드 문자를 표현할 때는 1바이트를 사용하기 때문에 아스키 코드와 호환됨.

- 한글은 2바이트로 표현

- 모든 글자가 완성된 형태로만 존재하는 "완성형" 코드. (초성+중성+종성의 조합으로 만들 수 없음)

-> 때문에 표현할 수 없는 한글이 일부 존재함.

 

# 유니코드

- 국가별로 독자적인 문자 집합과 인코딩 방식이 생겨나니 너무 복잡

-> 국제 표준화 기구(ISO)에서 동일한 규칙으로 모든 언어를 표현할 수 있는 유니코드 문자집합을 발표

 

# UTF-8 (국룰)

- 8bit(1byte)로 인코딩 한다는 것을 뜻한다. 아스키코드와 완벽하게 호환

- 표현하려는 문자에 따라 최소 1바이트에서 6바이트까지 할당 (하지만 호환을 위해 최대 4바이트만 사용한다)

-> "가변 길이 문자 인코딩"

- 보통 일반적인 문자는 3바이트 내로 처리되고, 그 이상은 이모지나 고대 문자 등 표현에 사용된다

- 거의 모든 환경에서 문자열 처리 표준으로 사용. JSON은 UTF-8 인코딩만 지원.

- 한글은 대개 3바이트로 표현된다

 

++++ 잠깐!! ++++

char은 2byte 자료형인데 한글은 3byte로 표현된다면 char 타입에 한글을 넣을 수 없는 것일까?

- JVM은 문자열을 기본적으로 UTF-16(BE)을 이용하여 저장한다고 한다. 왜냐고 물으면 잘 모르겠다;; 다음 글을 참고 하자 (https://lordofkangs.tistory.com/86) 그냥 성능이 더 좋기 때문이라고 생각하면 되겠다.

- 우리는 BMP에 한글이 포함되어 있기 때문에 UTF-16 기준 영어와 한글이 U+FFFF(2byte) 범위 안에 모두 들어가있다. 그렇기 때문에 2byte인 char 타입에 한글을 모두 넣을 수 있는 것이다. 그동안 전혀 몰랐고, 또 상당히 신기한 내용이다.

String str = "가나다";
System.out.println(str.getBytes(StandardCharsets.UTF_8).length); // 9
System.out.println(str.getBytes(StandardCharsets.UTF_16LE).length); // 6
System.out.println(str.getBytes(StandardCharsets.UTF_16BE).length); // 6

// 참고
System.out.println(str.getBytes(StandardCharsets.UTF_16).length); // "8" ?

UTF-8을 이용했을 때에는 한글자당 3byte로 계산되어 9

UTF-16LE/BE를 이용했을 때는 한글자당 2byte로 계산되어 6이 나오는 것을 확인할 수 있다.

 

근데 이상한게 LE/BE 구분 없이 UTF-16을 사용하면 8이 나온다. 왜지? 

 

 

내 추측으로는 그냥 문자열 앞에 LE/BE를 구분할 수 있는 표식이 뭔가 더 붙어서 그런게 아닐까 싶다 (뇌피셜)

 

 

# UTF-16 (JVM)

- 16bit(2byte)로 인코딩 한다는 것을 뜻한다. 2byte 또는 4byte만 사용하기 때문에 아스키 코드와 호환 X

- 유니코드는 문자의 종류에 따라 기본 다국어평면(Basic Mutillingual Plane, BMP), 보충 다국어 평면(SMP), 상형문자 보충 평면(SIP), 특수 목적 보충 평면(SSP) 4개의 평면이 존재하는데, 바이트 수는 표현하려는 문자가 어느 평면에 속해있는지에 따라 결정된다.

- BMP는 U+0000 ~ U+FFFF 범위에 속하는 문자가 있는데, 이 속에는 한글, 한자를 포함한 여러 다국어가 존재. 2바이트로 인코딩 된다.

- BMP 범위를 벗어나는 문자는 4바이트로 표현한다. 즉, 일반 글자는 2바이트, 특별한 문자는 4바이트를 사용해 인코딩 한다. (Java에서 한글은 2byte로 저장된다!!!)

 

# UTF-32

- 항상 4바이트 고정 인코딩 방식. (쓸 일 없다)

 

 

* UTF-16, UTF-32는 바이트 순서 표시(BOM, Byte Order Mark)가 추가로 사용된다

- 문자열 맨 앞 2바이트에 0xFE 혹은 0xFF 어떤 문자가 먼저 오는지에 따라 리틀 엔디언(LE), 빅 엔디언(BE)를 구분하고, 두 방식에 따라 문자열 인코딩 시 바이트 데이터를 조합하는 순서가 바뀌게 된다.

- BOM을 사용하여 표현 순서를 정하는 이유는 CPU 설계에 따라 바이트 값을 처리하는 순서가 다르기 때문.

-> 리틀 엔디언은 작은 단위가 먼저 나오고, 빅 엔디언은 큰 단위가 먼저 나온다.

--> 즉, 앞에서부터 읽을지 뒤에서부터 읽을지를 정해야 한다는 것!! 인코딩 방식이 여러개라 디코딩 문제가 발생할 가능성이 높다.

 

 

* UTF-8은 1바이트 단위로 글자를 변환하기 때문에 LE, BE 구분을 할 필요가 없다. 떄문에 BOM도 사용하지 않음. (BE 단일 인코딩 방식)

 

 

** 0x12345678 을 읽는다고 한다면 ---

빅 엔디언: 일반적으로 사람이 읽는 순서 (앞->뒤) -> 0x12 0x34 0x56 0x78

리틀 엔디언: 뒤에서부터 앞으로 읽는 순서 -> 0x78 0x56 0x34 0x12

 

0x00 -> 1byte, 0000 0000 (0)

0xff -> 1byte, 1111 1111 (255)

 

 

* MySQL에서의 UTF-8 타입은 utf8과 utf8mb4가 있는데, utf8은 3byte 까지는 정상 처리하나, 4byte 영역 문자를 표현하지 못한다. 때문에 UTF-8과 완벽하게 호환되는 문자집합을 사용하고자 하면 utf8mb4를 사용해야 한다 (이모지 사용)

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
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
글 보관함