왜 ASCII에서 소문자는 대문자 바로 뒤에 오지 않을까?

4 days ago 9
  • ASCII에서 Z는 90, a는 97에 배치되어 있으며, 그 사이 6개 문자 덕분에 대문자와 소문자의 코드 차이가 32로 맞춰짐
  • 32는 2^5라서 A 65와 a 97처럼 대응하는 대소문자는 항상 00100000 비트 하나만 다름
  • 이 배치 덕분에 32의 비트 반전값과 AND하면 대문자화, 32와 OR하면 소문자화, 32와 XOR하면 대소문자 반전이 가능함
  • 알파벳 순번은 문자 코드에 31을 AND해 하위 5비트만 남기면 얻을 수 있으며, A/a는 1, Z/z는 26이 됨
  • ASCII는 7비트로 128개 코드 포인트만 표현하는 초기 문자 인코딩이며, 오늘날 쓰이는 Unicode의 첫 128개 코드 포인트는 ASCII와 동일함

ASCII 배치와 32 차이

  • ASCII 표에서 대문자 Z의 코드값은 90이고, 소문자 a는 바로 다음 값이 아니라 97에 배치됨
  • 그 사이에는 [ \ ] ^ _ `의 6개 문자가 있음
  • 영어 알파벳은 26자이고, 이 6개 문자를 더하면 26 + 6 = 32가 됨
  • 32는 2^5에 해당하는 값이라, 대문자와 소문자 대응 관계가 특정 비트 하나의 차이로 정렬됨
  • 예를 들어 A는 65이자 01000001, a는 97이자 01100001이며, 두 값의 차이는 32임

ASCII와 Unicode의 관계

  • ASCII는 초기 문자 인코딩 방식 중 하나이며, 7비트만 사용해 2^7 = 128개의 코드 포인트를 표현함
  • 128개 코드 포인트는 사람이 사용하는 모든 문자를 담기에는 부족하며, 특히 수만 개 문자를 가진 중국어 같은 언어까지 담기에는 충분하지 않음
  • 오늘날 표준 문자 집합으로는 Unicode가 쓰이며, UTF-8과 UTF-16 같은 여러 인코딩을 가짐
  • Unicode의 첫 128개 코드 포인트는 ASCII와 동일함

대소문자를 가르는 5번째 비트

  • 대문자와 대응하는 소문자를 이진수로 비교하면 항상 32에 해당하는 비트가 바뀜
65 = 01000001 = A 97 = 01100001 = a 66 = 01000010 = B 98 = 01100010 = b 67 = 01000011 = C 99 = 01100011 = c
  • 32는 이진수에서 00100000이며, 이 비트 하나가 대문자와 소문자 차이를 만듦
  • ASCII의 알파벳 배치는 비트 연산으로 대소문자 변환을 쉽게 할 수 있게 되어 있음

비트 연산으로 대소문자 처리하기

  • 대문자로 변환

    • 문자를 대문자로 만들려면 32의 비트 반전값과 비트 AND를 수행함
0 1 1 0 0 0 0 1 (97 = 'a') & 1 1 0 1 1 1 1 1 (mask) ------------------- 0 1 0 0 0 0 0 1 (65 = 'A')
  • a에 적용하면 97이 65로 바뀌어 A가 됨
  • 이미 대문자인 A에 같은 연산을 적용하면 그대로 A로 남음
  • 소문자로 변환

    • 문자를 소문자로 만들려면 32와 비트 OR를 수행함
0 1 0 0 0 0 0 1 (65 = 'A') | 0 0 1 0 0 0 0 0 (32) ------------------- 0 1 1 0 0 0 0 1 (97 = 'a')
  • A에 적용하면 65가 97로 바뀌어 a가 됨
  • 이미 소문자인 a에 같은 연산을 적용하면 그대로 a로 남음
  • 대소문자 반전

    • 대소문자를 뒤집으려면 32와 비트 XOR를 수행함
0 1 1 0 0 0 0 1 (97 = 'a') ^ 0 0 1 0 0 0 0 0 (32) ------------------- 0 1 0 0 0 0 0 1 (65 = 'A') 0 1 0 0 0 0 0 1 (65 = 'A') ^ 0 0 1 0 0 0 0 0 (32) ------------------- 0 1 1 0 0 0 0 1 (97 = 'a')
  • a는 A로, A는 a로 바뀜

하위 5비트로 알파벳 순번 얻기

  • 알파벳 순번은 문자 코드에 31을 비트 AND해서 구할 수 있음
0 1 0 0 0 0 0 1 (65 = 'A') & 0 0 0 1 1 1 1 1 (31) ------------------- 0 0 0 0 0 0 0 1 (1) 0 1 1 1 1 0 1 0 (122 = 'z') & 0 0 0 1 1 1 1 1 (31) ------------------- 0 0 0 1 1 0 1 0 (26)
  • 31은 이진수로 00011111이라, 앞쪽 비트를 지우고 하위 5비트만 남김
  • ASCII에서 알파벳 문자의 하위 5비트는 알파벳 위치와 맞아떨어짐
  • A/a는 00001로 끝나 1이 되고, B/b는 00010으로 끝나 2가 되며, Z/z는 11010으로 끝나 26이 됨
  • ASCII 문자 코드에서 c & 31은 c % 32와 같음
  • 32가 2의 거듭제곱이기 때문에, 31로 마스킹하면 32 단위 묶음을 제거하고 남은 부분만 보존함
'A' = 65 → 65 % 32 = 1 'B' = 66 → 66 % 32 = 2 ... 'Z' = 90 → 90 % 32 = 26 'a' = 97 → 97 % 32 = 1 'b' = 98 → 98 % 32 = 2 ... 'z' = 122 → 122 % 32 = 26
Read Entire Article