개발하면서 한글 문자를 초성, 중성, 종성으로 분리해야할 필요가 생겼는데요.
구글링하면서 알게된 내용을 정리해보려해요.
우선 한글은 유니코드라는 글로벌하게 약속된 체계에 의해 고유한 식별값이 부여되어 있는데요.
'가' 라는 글자는 44032 이고, '힣' 이라는 글자는 55203 으로 할당되어 있어요.
그런데 이 식별값이 그냥 부여된 게 아니라는 것이 중요한 포인트입니다.
바로 완전한글 결합공식 에 의해 생성된 식별값이였던거죠!!!
(초성 인덱스 * 21 + 중성 인덱스) * 28 + 종성 인덱스 + 0xAC00
0xAC00(44032) 은 유니코드 체계에서 한글이 시작되는 지점으로 '가' 를 나타내요.
위 공식에 쓰이는 초성, 중성, 종성 배열은 아래와 같아요.
초성 배열(19개)
"ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ" , "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"
중성 배열(21개)
"ㅏ", "ㅐ", "ㅑ", "ㅒ", "ㅓ", "ㅔ", "ㅕ", "ㅖ", "ㅗ", "ㅘ", "ㅙ", "ㅚ", "ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ", "ㅢ", "ㅣ"
종성 배열(28개)
"", "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ", "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"
종성 배열의 첫 번째 요소는 종성이 없는 경우를 나타내기 위해 빈 문자열로 들어간 것을 알 수 있어요.
예를 들어서 '민' 이라는 글자를 초성, 중성, 종성으로 분리해볼께요.
민의 유니코드 값은 '48124' 예요.
초성 분리
초성을 분리하는 방법은 아래 식에서 '초성 인덱스'만 뽑아내고 초성배열에서 값을 얻으면 되는거예요.
0xAC00 는 44032 를 의미하기 때문에 숫자 값으로 바꿔서 적을께요.
(초성 인덱스 * 21 + 중성 인덱스) * 28 + 종성 인덱스 + 44032 = 48124
먼저 양 변을 44032 으로 빼줍니다.
(초성 인덱스 * 21 + 중성 인덱스) * 28 + 종성 인덱스 = 4092
그리고 양 변을 28로 나눈 몫만 남겨줍니다.
초성 인덱스 * 21 + 중성 인덱스 = 146
종성은 총 28개로 인덱스 기준으로 보면 0 ~ 27까지의 숫자를 갖게 되고 이를 28로 나눠주면 몫은 항상 0이 나오게 되어서 식에서 사라지게 되요.
이번에는 양 변을 21로 나눈 몫만 남겨줍니다.
초성인덱스 = 6
종성과 비슷하게 종성은 총 21개로 인덱스 기준으로 보면 0 ~ 20까지의 숫자를 갖게 되어 이를 21로 나눠주면 몫은 항상 0이 나오게 되어서 식에서 사라지게 되요.
결과적으로 초성인덱스로 6을 얻었는데요.
초성 배열[6] 의 값을 한 번 볼까요?
'ㅁ'
임을 확인할 수 있어요.
중성 분리
초성을 분리했던 방법을 이해하셨다면 이를 조금만 응용하면 중성도 분리할 수 있어요.
(초성 인덱스 * 21 + 중성 인덱스) * 28 + 종성 인덱스 + 44032 = 48124
우선 시작은 44032 를 빼주고 시작하면 되겠죠.
(초성 인덱스 * 21 + 중성 인덱스) * 28 + 종성 인덱스 = 4092
그리고 양 변을 28로 나눠줍니다.
초성 인덱스 * 21 + 중성 인덱스 = 146
여기서 초성분리와 조금 달라지는데요.
지금 얻고자하는 값이 중성인덱스이기 때문에 초성인덱스를 제거해야겠죠.
이를 위해서는 나머지 연산자(mod, %)를 이용해서 나머지 값을 얻으면 되요.
말 그대로 나머지를 얻기 위한 연산자라서 몫은 버리게 되는데요.
양 변에 %21 을 적용하게되면 초성인덱스는 몫으로 뽑히기 때문에 버려지게 되요.
한편 중성 인덱스의 값은 0 ~ 20 까지이기 때문에 값이 그대로 유지된답니다.
중성인덱스 = 20
중성배열[20] 의 값을 보면
'ㅣ'
임을 알 수 있어요.
종성 분리
마지막 종성 분리예요.
(초성 인덱스 * 21 + 중성 인덱스) * 28 + 종성 인덱스 + 44032 = 48124
종성분리 역시 양 변을 44032 로 빼주고 시작할게요.
(초성 인덱스 * 21 + 중성 인덱스) * 28 + 종성 인덱스 = 4092
오옷! 종성 인덱스가 바로 보이네요. 중성 분리에서 사용했던 나머지 연산자(mod, %) 를 적용하면 바로 얻을 수 있겠죠.
양 변에 %28 을 적용하면 (초성 인덱스 * 21 + 중성 인덱스) 는 몫으로 뽑히기 때문에 버려지게 되요.
종성 인덱스 = 4
종성배열[4] 의 값을 보면
'ㄴ'
임을 알 수 있어요.
이렇게 해서 '민' 이라는 글자에서 초성, 중성, 종성을 분리하여 'ㅁ', 'ㅣ', 'ㄴ' 을 얻을 수 있었는데요.
아래는 제가 테스트해 본 코드인데요. 혹시 위 내용에서 이해가 잘 안되셨던 부분이 있다면 아래 코드 참고하셔서 직접 테스트 해보시면 도움이 되실 것 같습니다.
public class Main {
public static void main(String[] args) {
String[] chosungs = {"ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ" , "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"};
String[] jungsungs = {"ㅏ", "ㅐ", "ㅑ", "ㅒ", "ㅓ", "ㅔ", "ㅕ", "ㅖ", "ㅗ", "ㅘ", "ㅙ", "ㅚ", "ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ", "ㅢ", "ㅣ"};
String[] jongsungs = {"", "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ", "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"};
int uniBase = "민".charAt(0) - 44032;
char chosung = (char)(uniBase / 28 / 21);
char jungsung = (char)(uniBase / 28 % 21);
char jongsung = (char)(uniBase % 28);
System.out.println((int)chosung + ", " + chosungs[chosung]);
System.out.println((int)jungsung + ", " + jungsungs[jungsung]);
System.out.println((int)jongsung + ", " + jongsungs[jongsung]);
}
}
끝까지 읽어주셔서 감사합니다.
참고)
https://needjarvis.tistory.com/644
아래는 제가 개발중인 싱잉볼 명상앱 '소함' 이예요.
아침 저녁으로 하루 두 번 10분간 명상을 추천드려요.
한 번 해보시면 다음날 확실히 마음이 편안해지는 것을 느끼실 수 있을 거예요.
소함 명상으로 나를 위한 최고의 휴식을 가져보세요.
'프로그래밍 > Java' 카테고리의 다른 글
자바 제네릭 파헤치기 - Generic Method (0) | 2023.07.22 |
---|---|
자바 제네릭 파헤치기 - Generic Class (0) | 2023.07.19 |
[Effective Java 3/E] 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2023.07.16 |
[Effective Java 3/E] 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2023.07.15 |
한글 Levenshtein Distance 구현 (0) | 2022.01.25 |