본문 바로가기

CS/기타 전반적인 cs 정리

[Data] 문자 인코딩 정리 - ASCII, 유니코드, UTF-8 등

우리가 Text data를 다룰 때, 컴퓨터에 글자를 저장하기 위해서는 글자를 숫자로 변환해야 한다.
인코딩(Encoding) : 문자와 같이 사람이 해석할 수 있는 데이터를 이진 형식(binary format)으로 변환하여 컴퓨터가 처리할 수 있게 하는 과정 (글자 -> 숫자)
디코딩(Decoding) : 인코딩의 역과정으로, 이진 형식의 데이터를 사람이 해석할 수 있는 문자 형태로 변환하는 과정. (숫자 -> 글자)


https://sheepone.tistory.com/

ASCII

- 영문 알파벳을 표현하기 위한 초기의 대표적인 문자 인코딩 표준(문자 집합)
- 또는 이 자체를 인코딩으로 분류하기도 함.
- 초기에 문자를 표현하던 대표적인 방식.
- 영어 알파벳 대소문자, 숫자, 특수 문자 및 이스케이프 문자 포함.
 
- 한 문자를 1바이트로 취급.
- 맨 앞 1비트는 `패리티 비트 (Parity Bit)`로 사용해 오류를 검출하는 체크섬 방식 사용.
- 따라서, 0부터 127까지의 10진수 숫자를 표현 가능. (signed char)
- 예를 들어, 문자 'A'는 `65(01000001)`, 'a'는 `97(01100001)`.
 
- 영어를 위한 문자(American Standard Code) 인코딩이었기 때문에,
다른 문자들은 표현이 불가능하다는 치명적인 단점이 있었음.


ASCII의 확장 - 패러티 비트의 사용

- 이후에, 유럽 문자 등 각 나라에서 쓰이는 여러 문자들을 표현하기 위해,
ASCII는 패러티 비트까지 포함해 8비트를 모두 쓰는 것으로 확장됨.
- 즉 패러티 비트를 오류 검출 용도로  쓰지 않게 됨.
- 따라서, 0부터 255까지의 10진수 숫자를 표현 가능. (unsigned char)
- 앞의 7비트는 ASCII와 동일하며, 뒤의 1비트로 다른 언어의 문자들을 표현함.
 
- 각국의 문자를 표기하기 위해, 영어 이외의 국가 및 언어권들이 이렇게 확장된 방식의 독자적 인코딩을 개발 및 사용하게됨.


ANSI

- 이러한 ASCII에서 확장된 인코딩들 중에, 대표적인 것이 ANSI.
- 영어와 더불어 일부 유럽쪽 문자와 기타 특수문자들이 할당되어 있음.
- 사실 엄밀히는 ANSI는 ASCII를 제정한 미국의 협회 이름이고,
ANSI라고 하면 통상 OEM-US(Codepage 437) 인코딩을 의미함.

- 그 외에 ASCII 기반이 아닌 다른 문자 집합 기반의 인코딩들도 있음.
- 예시로, 위 사진은 IBM에서 개발된 EBCDIC 인코딩의 서유럽어 지원 변형 버전인 IBM500임.
- 보면 0~127 부분도 ASCII랑 다른 걸 볼 수 있음.
- EBCDIC 및 이를 기반으로 하는 인코딩들은 UTF-8이 표준이 되면서 거의 사용되지 않음.


EUC-KR

- 영어권 문자들에 비해 한글은 문자 수가 많아서, 1바이트만으로는 하나의 한글 문자 표현이 불가능함.
- 따라서 EUC-KR이라는 인코딩을 사용해, 2바이트를 사용하여 하나의 한글 문자를 표현함.
- EUC-KR은 한글은 KSC5601로 처리하고 영문은 KSC5636으로 처리하는 방식
- KSC5601은 인코딩은 아니고 일종의 표준 및 문자 집합인데, "'가'는 0xB0A1이다" 처럼 표준을 정해둔 것임. 한글을 고정 2바이트에 매핑한 문자 집합.
- 얘도 ASCII 기반이라 기존 ASCII 문자(0~127)도 그대로 호환 가능.
- 한글 표현 방식은 아래와 같음.

더보기
 

✅ 1바이트와 2바이트를 구분하는 방법 (EUC-KR)

EUC-KR에서 한글을 2바이트로 인코딩하고, ASCII 문자는 1바이트로 인코딩되기 때문에, 1바이트와 2바이트를 구분하는 기준은 바이트의 값에 따라 결정됩니다. 구체적으로, 한글은 특정 범위의 바이트 값을 갖기 때문에, 이 값을 확인하면 1바이트 문자와 2바이트 문자를 구별할 수 있습니다.


🔹 ASCII와 한글을 구별하는 방법

  • ASCII 문자는 0x00 ~ 0x7F (즉, 0~127 범위)의 값을 가집니다.
  • 한글 문자는 0xA1 ~ 0xFE 범위의 값이 포함된 2바이트 문자입니다.

1. 1바이트 문자 (ASCII):

  • ASCII 문자는 0x00 ~ 0x7F 사이의 값을 갖습니다. 즉, 첫 번째 바이트가 이 범위에 해당하면 1바이트 문자로 간주합니다.
    • 예: 영어 알파벳, 숫자, 기호
    • 예시: 0x41(A), 0x31(1), 0x2E(.) 등은 모두 1바이트로 처리됩니다.

2. 2바이트 문자 (한글):

  • 한글 문자첫 번째 바이트가 0xA1 이상, 0xFE 이하인 값을 가질 때 2바이트 문자로 처리됩니다.
    • 두 번째 바이트도 특정 범위(0xA1 ~ 0xFE)에 포함되어야 한글 음절을 형성합니다.
    • 예시: **'한'**은 0xB0 0xA1, **'글'**은 0xB0 0xA2와 같이 2바이트로 인코딩됩니다.

🔹 구체적인 판별 방법

  1. 첫 번째 바이트가 0x00 ~ 0x7F 범위일 경우: 1바이트 문자
    • 이 경우에는 ASCII 문자이므로, 1바이트만 읽습니다.
  2. 첫 번째 바이트가 0xA1 ~ 0xFE 범위일 경우: 2바이트 문자
    • 첫 번째 바이트가 0xA1 ~ 0xFE 범위에 해당하면, 두 번째 바이트도 읽어야 한글이 완성됩니다.

🔹 예시

  1. 문자열: "Hello 한글" (EUC-KR로 인코딩된 예시)
    • 'H': 0x48 → 1바이트 (ASCII 문자)
    • 'e': 0x65 → 1바이트 (ASCII 문자)
    • 'l': 0x6C → 1바이트 (ASCII 문자)
    • 'l': 0x6C → 1바이트 (ASCII 문자)
    • 'o': 0x6F → 1바이트 (ASCII 문자)
    • ' ' (공백): 0x20 → 1바이트 (ASCII 문자)
    • '한': 0xB0 0xA1 → 2바이트 (한글)
    • '글': 0xB0 0xA2 → 2바이트 (한글)
    이 경우, **"Hello"**는 모두 1바이트 문자(ASCII)이고, **"한글"**은 2바이트 문자입니다. 따라서 한글 부분을 읽을 때는 2바이트를 읽어야 합니다.

🔹 결론

EUC-KR에서 1바이트 문자와 2바이트 문자를 구별하는 방법첫 번째 바이트 값을 확인하는 것입니다.
1바이트 문자는 0x00 ~ 0x7F 범위, 2바이트 문자는 0xA1 ~ 0xFE 범위에 해당합니다.
한글을 읽을 때는 두 번째 바이트도 확인하여 2바이트 문자인지 판단해야 합니다.

따라서 문자열을 읽을 때, 첫 번째 바이트 값에 따라 1바이트 또는 2바이트로 읽어들이면 됩니다!


ASCII 확장 인코딩 사용의 문제점

- 각 나라마다 자기들 문자를 표현하기 위해 서로 다른 인코딩들을 사용한다 했잖음.
- 그러니 여러 언어의 문자들을 한 문서에서 표현하는 경우 문제가 발생함.
- 예를 들어, 한국어 화자를 대상으로 작성된 아랍어 공부 자료의 경우,
어떤 인코딩을 쓰냐에 따라 필연적으로 둘 중 하나는 깨지게 됨.
- 또한 한글처럼 여러 문자를 조합해 한 글자를 표현하는 인코딩들은 경우에 따라 그 자체로 깨지거나 이상하게 표현되기도 함.
 
- 이런 문제를 해결하기 위해, 다양한 언어권의 문자들을 통합적으로 표현 가능한 통합 인코딩의 필요성이 대두됨.
=> 전 세계의 모든 문자 체계를 한데 몰아넣은 문자 인코딩 표준, 유니코드(Unicode)가 등장함.


https://home.unicode.org/

Home

home.unicode.org

 

유니코드(Unicode)

- 전 세계의 모든 문자를 하나의 표준으로 통합해, 일관되게 표현되도록 한 문자 인코딩 표준
- 인코딩이 아님. 유니코드는 문자 집합을 포함한 표준이고, 유니코드를 사용한 인코딩은 여러 가지가 있음.
- 초기엔 2바이트 숫자를 문자 하나에 1대1 매팽시키는 방식이었고,
프로젝트 출범 몇 년 후 4바이트 숫자를 문자 하나에 매핑시키는 방식으로 변경함. (아래 접은글 참고)

더보기

하지만 인터넷으로 전 세계의 컴퓨터가 연결되기 시작하자 이야기가 달라졌는데, 다른 국가에 이메일을 보냈더니 글자가 와장창 깨졌던 것. 인터넷 웹 페이지도 마찬가지였다. 이런 세계적 흐름에서 2바이트(16비트, 65,535자) 공간에 세상의 모든 문자를 넣어서 전산화해 보자는 야심찬 목표로 출범한 프로젝트가 유니코드이다. 다만 프로젝트가 출범한 지 몇 년 되지 않아서 세상의 모든 문자를 넣기에 65,535자로는 부족하다는 사실이 밝혀졌고[1], 결국 공간을 4바이트(32비트, 약 42억 자)로 넉넉하게 늘리면서 정말로 세상의 모든 문자를 할당할 수 있을 정도가 되었다.

 

참고서나 서적 등에서 유니코드를 '2바이트 문자 체계'라고 소개하고 있다면, 내용이 20년 넘게 업데이트되지 않았다는 것이다. 최신 버전의 유니코드에는 한자만 해도 9만 7천 자 이상이 수록돼 있다.

 

(출처 : 나무위키)

- 거대한 (2바이트 숫자, 문자)의 표가 있다고 생각해도 됨.

 
- 근데 아래에 소개될 USC-2, USC-4 처럼 유니코드를 그대로 사용해 인코딩하기엔 여러 단점이 있었음.
- 따라서, 유니코드를 적당히 변환(Transformation)한 방식의 인코딩들이 개발됨(UTF-8 등).


유니코드의 초기 인코딩 - 고정 길이 인코딩

- 초기엔 고정 길이 인코딩 방식을 사용했음.
- USC-2를 사용해 2바이트로 각 문자를 인코딩하도록 했으나, 2바이트로는 65,536개의 문자밖에 표현하지 못함. 특히 한자권 문자를 포현하기 위해선 2바이트론 한계가 있었음.
- 이를 해결하기 위해 4바이트로 각 문자를 인코딩하도록 한 USC-4가 고안되었음. 이는 4바이트를 사용해 훨씬 많은 문자를 표현할 수 있었으나, 당연하게도 파일 크기가 커지고 메모리 사용이 많아지며 통신 속도가 느려짐.


- 많은 문자들을 표현할 수 있으면서도 메모리 사용을 효율적으로 하기 위해, 가변 길이 인코딩 방식이 도입됨.

파이썬 알고리즘 인터뷰

UTF-8

- Go 개발자 롭 파이크와 유닉스 개발자 켄 톰슨이 함께 작업한 결과물.
- 가변 길이 문자 인코딩을 사용해, 숫자와 영어 알파벳 등 자주 사용되는 문자는 기존 ASCII와 하위 호환성을 가져 1바이트로, 그 외의 문자들은 2~4바이트로 문자 하나를 표현하는 유니코드 인코딩.

나무위키

- 위처럼, 문자열 처리를 담당하는 파서가 첫 번째 바이트의 비트 패턴을 보고, 몇 바이트를 읽어 하나의 문자로 봐야 할지 결정함.
- 1바이트를 사용하는 경우 : 영어 알파벳, 숫자 등 기존 ASCII와 호환됨(0~127)
- 2바이트 사용하는 경우 : 많은 유럽 문자와 중동지역 문자 등
- 3바이트 사용하는 경우 : 한글, 한자, 가나 문자 등의 동아시아권 문자 등
- 4바이트를 사용하는 경우 : 희귀 문자, 고대 문자, 이모지 등
 
- 오늘날 대부분의 시스템과 웹(특히 HTML5), 데이터베이스와 파일 시스템에서 UTF-8을 사용.
- 우리도 UTF-8을 기본으로 두고 개발하면 됨.
- Byte Ordering을 고려할 필요도 없음.

UTF-16

- 2바이트와 4바이트로 표현하는 가변 길이 문자 유니코드 인코딩
- 자주 사용하는 문자는 2바이트, 그렇지 않은 문자는 4바이트로 표현
- 2바이트를 사용하는 경우 : 영어 알파벳, 숫자, 한글, 유럽 문자, 중동지역 문자, 대부분의 한자, 가나 문자 등
- 4바이트를 사용하는 경우 : 일부 한자, 희귀 문자, 고대 문자, 이모지 등
 
- UTF-8에서 자주 사용되는 문자들을 1바이트로 표현 가능한 것에 비해,
UTF-16에서는 자주 사용되는 문자들도 2바이트를 사용해야 함.
-> 파일 크기가 커지고 메모리 사용이 많아지며 통신 속도가 느려짐.
- 추가로, Byte Ordering을 고려해야 하는 문제가 있음. (필요시 추가로 공부하기)
- ASCII 기반 인코딩들(ANSI 등)과도 호환되지 않는 문제가 있음.
 
=> 이러한 이유들로, 인터넷에서 정보 교환용으로 UTF-16이나 UCS-2 등 16비트 기반 인코딩은 사용하지 말라는 권고를 쉽게 접할 수 있다.
 
- 그럼에도 불구하고, UTF-16의 가장 큰 의의는 UCS-2와의 하위 호환성이다. (UTF-8과 UCS-2는 호환되지 않음)
- UCS-2는 원래 2바이트(65536개) 이상의 코드포인트가 필요하지 않다는 전제하에 설계된 인코딩이나 이후 확장 과정에서 이는 현실적이지 않은 가정임이 드러나 이후 4바이트 기반의 UCS-4와 가변 길이 인코딩인 UTF-8이 도입되었다. 하지만 이는 UCS-4의 지나친 대역폭/저장 공간 낭비의 문제와 더불어 아래에서 일부 플랫폼이 이미 UCS-2를 기본 인코딩으로 사용함으로 인해 하위 호환성을 깨지 않으면서 인코딩을 변경하는게 현실적으로 불가능해졌기에 2바이트 기반의 가변 길이 인코딩의 필요성이 제기되었고, 그 결과 UTF-16이 나온 것이다. (나무위키)
 
- 일부 운영체제나 프로그램 내부에서는 UTF-16이 사용되기도 함. (.NET, Java 등)

UTF-32

- 유니코드 문자 하나에 32비트를 사용하는 고정 길이 문자 인코딩.
- 인터넷에서 정보 교환용으로는 사실상 거의 이용되지 않음. 
- Byte Ordering를 고려해야 하는 문제 또한 존재.
- 문자열 index 접근 시간이 아예 일정하기에, 특정 프로그램 내부에서 사용되기도 함.


문자 인코딩의 중요성

 
- 주어진 Binary number에 대해, 어떤 인코딩을 쓰냐에 따라 해석이 달라짐
`ex. 1100 0001`
=> ASCII: ‘Á’ (A with acute)에 대응
=> EUC-KR: 'ㅂ'에 대응
=> UTF-8: 유효하지 않은 문자
`ex. 0100 0001`
=> ASCII: ‘A’
=> EBCDIC: ‘J’
=> UTF-8: ‘A’


파이썬의 경우

- 파이썬2 이전에는 한글을 비롯한 특수 문자들이 모두 별도로 인코딩되는 구조여서, 콘솔 출력에 값이 깨지는 문제가 있기도 했음.
- 그러나 파이썬3에 이르러서는 문자 처리가 모두 유니코드 기반으로 전환돼 다국어를 출력하는 데 아무런 문제가 없다.

파이썬이 내부적으로 UTF-8을 사용하지 않는 이유

- 그러나 파이썬이 내부적으로 UTF-8 인코딩을 사용하지는 않는데,
이는 인덱스를 통해 개별 문자에 접근하기가 어렵기 때문임.
 
- 파이썬에서는 문자열 슬라이싱을 비롯해 원하는 문자에 인덱스로 접근할 수 있는 다양한 방식이 제공됨. 
- 만약 문자열을 UTF-8과 같은 가변 길이 문자 인코딩으로 인코딩해둔다면, 각 문자마다 바이트 길이가 달라지게 되므로, 
전체 문자열을 스캔하지 않는 한 원하는 인덱스에 빠르게 접근할 수 없음.
- 따라서 고정 길이 인코딩 방식을 사용함.
 
- 파이썬은 문자열 단위로 다른 고정 길이 인코딩 방식을 적용해 모든 문자를 다루면서 용량을 최적화하는데,
- 만약 모든 문자열이 ASCII 범위 내에 있다면 Latin-1 인코딩(고정 1바이트 인코딩)을 사용.
- 이외의 대부분 문자열은 USC-2를 사용해 2바이트 인코딩을 함.
- 특수 기호, 그림 이모티콘, 희귀 언어 등이 포함된 문자열이라면 USC-4로 4바이트 인코딩을 함.
(내용 정리 출처 : 파이썬 알고리즘 인터뷰)


추가로 읽어볼만한 흥미로운 글들

https://namu.wiki/w/%EB%AC%B8%EC%9E%90%20%EA%B9%A8%EC%A7%90

문자 깨짐

컴퓨터 등 IT 기기에서 문자 가 올바르게 표시되지 않는 현상을 의미한다. 일본어 에서는 문자 깨짐을 모지

namu.wiki

https://blog.naver.com/sjhmc9695/221513045879

포렌식 분석에 필요한 기본적인 문자 인코딩 지식(ASCII, Unicode, UTF-8, UTF-16 등)

이번 주제는 포렌식을 공부하기전 반드시 선행되어야하는 문자 인코딩 방식에 대해 서술해볼까한다. 문자 ...

blog.naver.com

https://redisle.tistory.com/14
=> 이 글이 끝판왕으로 다 정리되어 있으니 꼭 나중에 정독해보자.
https://facerain.github.io/python-string-encoding/python-string-encoding/
=> 윗윗 글을 기반으로 한번 더 정리한 글.


Ref. 
https://brunch.co.kr/@simplebrunch/13
https://www.youtube.com/watch?v=6hvJr0-adtg&t=4s
https://blog.naver.com/bbmobile/221360230141
https://kwgyu25it.tistory.com/entry/KSC5601-EUC-KR-UTF-8-Differences
https://honorlog.tistory.com/43
- 파이썬 알고리즘 인터뷰
- Chat GPT