한글의 표현방법
한글은 초성, 중성, 종성의 합으로 한개 글자를 표현한다. 음운 하나당 3바이트 값으로 표현되며 3가지 값을 모두 합쳐서 표현하게 된다. ‘가’은 ‘ㄱ’ + ‘ㅏ’ 이 합쳐진 문자다. ‘ㄱ’은 U+1100(4352) ‘ㅏ’ U+1161(4449) 이다. 그러나 두 값을 단순히 합쳐서 표현해서 나온 문자(U+2261)는 ‘가’ 가 아니다. 이는 초성, 중성, 종성을 표현하는 유니코드 범위와 완성된 한글 음절 문자를 표현하는 문자의 유니코드 범위가 다르게 할당되어 있기 때문이다.
한글 음절 문자의 유니코드 범위는 0xAC00(’가’에 해당) 부터 0xD7A3(’힣’에 해당)에 속한다. 한글 음절 문자 유니코드의 범위를 확인해보면, 사전순으로 초성과 중성과 종성을 합한 문자가 나열된 것을 확인할 수 있다. 순서를 잘보면, 초성에는 자음 순서(’ㄱ’부터 ‘ㅎ’ 까지)로, 중성에는 모음 순서(’ㅏ’부터 ‘ㅣ’까지), 종성에는 자음순서로 조합되어 있다.
초성, 중성, 종성 문자는 0x1100에서 0x11C2 사이에 위치한다. 초성의 범위는 0x1100 ~ 0x1160, 중성의 범위는 0x1161 ~ 0x11A7, 종성의 범위는 0x11A8 ~ 0x11C2 이다. 초성은 19개, 중성은 21개 종성은 28개가 존재한다.
정리하면 주어진, 초성, 중성, 종성으로 한글 음절의 값을 계산하면 다음과 같은 공식이 나온다.
((초성 유니코드값) - 0x1100) * (21 * 28) + ((중성 유니코드값 - 0x1161) * 28) + (종성 유니코드값 - 0x11A8) + 0xAC00
초성의 위치는 중성과 종성에 의해 결정된다. 따라서 중성과 종성이 어떤 값을 가졌는지 먼저 계산하여 중성과 종성의 시작(0x1161, 0x11A8) 값으로 부터의 상대적인 크기를 구한 후 더해 주면 된다.
중성도 마찬가지로 종성의 위치에 의해 결정된다. 해당 중성이 위치하는 유니코드 값의 상대적 크기는 종성 유니코드의 개수 만큼 반복한 것을 종성 유니코드 시작값(0x1161)에 곱하면 된다.
주어진 공식으로 초성, 중성, 종성을 합쳐서 음절 하나를 만드는 메서드를 작성해보자. 다음과 같이 할 수 있을 것이다.
final char CHOSUNG_START = 0x1100; final char JUNGSUNG_START = 0x1161; final char CHOSUNG_START = 0x11A8; final char HANGUL_START = 0xAC00; public static char composeHangulSyllable(char chosung, char jungsung, char jongsung) { int unicode = (chosung - CHOSUNG_START) * 21 * 28 + (jungsung - JUNGSUNG_START) * 28 + (jongsung - JONGSUNG_START) + HANGUL_START + 1; return (char) unicode; }
초성 분리하기
그러면 위 원리를 바탕으로 초성을 분리하는 방법을 쉽게 이해할 수 있다. 초성을 주어진 한글 음절 문자에서 분리하려면, 21(중성의 개수)과 28(종성의 개수) 만큼 곱한 값을 해당 한글 음절 문자가 페이지내에서 존재하는 상대적인 위치 크기에서 나눠주면 된다. 이때 중성과 종성의 상대적 위치는 나누면 소수점 미만값으로 반환되므로 무시해도 된다.
public static char extractChoseong(char syllable) { int unicode = (syllable - HANGUL_START) / (21 * 28); return (char) (unicode + CHOSUNG_START); }
간단한 테스트. 잘 분리되는 것을 확인 할 수 있다.
public class MyClass { private static final char HANGUL_START = 0xAC00; private static final char CHOSUNG_START = 0x1100; public static void main(String args[]) { System.out.println(extractChoseong('가')); System.out.println(extractChoseong('각')); System.out.println(extractChoseong('힣')); System.out.println(extractChoseong('법')); System.out.println(extractChoseong('햏')); } public static char extractChoseong(char syllable) { int unicode = (syllable - HANGUL_START) / (21 * 28); return (char) (unicode + CHOSUNG_START); } }
주의할 점
타자로 쳐서 나오는 모음, 자음 값은 위의 초성, 중성, 종성 문자와는 별개로 구분되는 또다른 문자이다. 해당 문자들은 0x3130–0x318F에 위치하는 문자들이다
겉으로만 보면 사실상 똑같아서 구분이 어려운데, 초성 기역과, 문자 기역의 유니코드 값을 확인해보면 다르다는 것을 알 수 있다.
예시로 확인해보자. 첫번째 글자는 초성 기역, 두번째 글자는 한글 문자 기역이다. 생긴 것은 같지만, 다른 문자로 인식되어 처리된다. (https://r12a.github.io/uniview/?charlist=ᄀㄱ)
요약
요약: 초성, 중성, 종성과 완성형 한글의 유니코드 페이지 위치를 잘 구분하자