현재 유니코드로 대부분의 문자열을 추출하기 때문에 본 포스팅도 유니코드를 기반의 한글 자모 추출 내용이다. 우리가 한글에서 자모를 추출하기 위해서는 유니코드에서 한글이 어느위치에 있는지를 알아야 한다. (참고로 파이썬에 관련된 자모 추출이 궁금하면 포스팅 맨 하단의 링크글로 들어가면 된다.)
유니코드는 마구잡이로 한글을 넣지 않았고 초성, 중성, 종성의 조합으로 값을 넣게 되었는데 반대로 이 조합을 알면 현재의 한글을 초성, 중성, 중성으로 분리할 수 있다는 말이 된다. 비슷한 개념으로 영어의 소문자(97~122)와 대문자(65~90)의 변환을 32로 빼고 더하는 것으로 변환할 수 있는 것과 유사하다. 유니코드에서 한글의 시작점은 AC00값이며, 이 값은 한글로 "가"이다. 즉 이 값을 기준으로 유니코드의 위치값을 계산하면 초중종성 즉 자모를 추출할 수 있게 된다.
초중종성 순서
우선 값을 계산하기에 앞서 초성, 중성, 종성의 순서를 알아야 한다. 아래는 초/중/종성을 배열 형태로 만든 것이다.
초성 위치값
String[] CHO = {"ㄱ","ㄲ","ㄴ","ㄷ","ㄸ","ㄹ","ㅁ","ㅂ","ㅃ",
"ㅅ","ㅆ","ㅇ","ㅈ","ㅉ","ㅊ","ㅋ","ㅌ","ㅍ","ㅎ"};
중성 위치값
String[] JOONG = {"ㅏ","ㅐ","ㅑ","ㅒ","ㅓ","ㅔ","ㅕ","ㅖ","ㅗ","ㅘ",
"ㅙ","ㅚ","ㅛ","ㅜ","ㅝ","ㅞ","ㅟ","ㅠ","ㅡ","ㅢ","ㅣ"};
종성 위치값
String[] JONG = {"","ㄱ","ㄲ","ㄳ","ㄴ","ㄵ","ㄶ","ㄷ","ㄹ","ㄺ","ㄻ","ㄼ",
"ㄽ","ㄾ","ㄿ","ㅀ","ㅁ","ㅂ","ㅄ","ㅅ","ㅆ","ㅇ","ㅈ","ㅊ","ㅋ","ㅌ","ㅍ","ㅎ"};
참고로 종성의 시작은 "없다"로 시작이 되기에 위와 같이 공백 값을 배열에 넣었다
한글이 유니코드에 저장되는 규칙
한글은 다음과 같은 규칙으로 유니코드값이 생성된다.
(초성 * 21 + 중성) * 28 + 종성 + 0xAC00
위의 배열값을 기반으로 "안"이라는 말을 뽑자면, 초성의 "ㅇ"는 11번째, "ㅏ"는 0번째, "ㄴ"는 4번째위치에 존재한다. 이 값을 그대로 자바에서 코딩을 하면
위와 같이 정상적으로 "안"이라는 글자가 출력된 것을 확인할 수 있다. 한글을 만드는 법을 알았으니 이제 자모를 추출하는 방법을 구해보도록 한다.
초성 공식
초성 = ((문자유니코드 - 0xAC00)/28)/21
현재의 한글 유니코드 값에 - 0xAC00 즉 한글 시작점을 뺀 후, 중성 개수와 종성 개수를 나눈다. 안의 유니코드 값과 함게 안에서 "ㅇ"을 추출하는 것을 해본다면
char uniVal = "안".charAt(0);
char cho = (char)((uniVal-0xAC00)/28/21);
System.out.println(uniVal + "[" + (int)cho + "]");
// 결과 안[11]
위와 같이 실행을 하게 되면 "안[11]"이라는 값이 나오고, 즉 초성으로 11번째 인덱스라는 값을 추출한 것을 확인할 수 있다.
중성 공식
중성 = (문자유니코드 - 0xAC00) / 28 % 21
현재의 한글 코드 값에 한글 시작점을 뺀 후, 종성 개수로 나눈 후, 중성의 나머지 값을 출력한다.
char uniVal = "안".charAt(0);
char joong = (char)((uniVal-0xAC00)/28%21);
System.out.println(uniVal + "[" + (int)joong + "]");
// 안[0]
위 코드를 실행하면 안[0] 이라는 값이 출력되는데 "ㅏ"는 0번째 값이기 때문에 정상적으로 나온 것을 확인할 수 있다.
종성 공식
종성 = (문자유니코드 - 0xAC00) % 28
종성 공식은 매우 간단하다. 차이값에 28의 나머지를 구하면 된다.
char uniVal = "안".charAt(0);
char jong = (char)((uniVal-0xAC00)%28);
System.out.println(uniVal + "[" + (int)jong + "]");
// 안[4]
위 코드를 실행하면 "ㄴ"의 종성 위치인 4번째 값이 출력되는 것을 확인할 수 있다. 이제 이 인덱스 값을 기반으로 초성/중성/종성을 호출하면 자모를 추출 할 수 있게 된다.
자모 추출 최종 소스
public class Main {
final static String[] CHO = {"ㄱ","ㄲ","ㄴ","ㄷ","ㄸ","ㄹ","ㅁ","ㅂ","ㅃ",
"ㅅ","ㅆ","ㅇ","ㅈ","ㅉ","ㅊ","ㅋ","ㅌ","ㅍ","ㅎ"};
final static String[] JOONG = {"ㅏ","ㅐ","ㅑ","ㅒ","ㅓ","ㅔ","ㅕ","ㅖ","ㅗ","ㅘ",
"ㅙ","ㅚ","ㅛ","ㅜ","ㅝ","ㅞ","ㅟ","ㅠ","ㅡ","ㅢ","ㅣ"};
final static String[] JONG = {"","ㄱ","ㄲ","ㄳ","ㄴ","ㄵ","ㄶ","ㄷ","ㄹ","ㄺ","ㄻ","ㄼ",
"ㄽ","ㄾ","ㄿ","ㅀ","ㅁ","ㅂ","ㅄ","ㅅ","ㅆ","ㅇ","ㅈ","ㅊ","ㅋ","ㅌ","ㅍ","ㅎ"};
public static void main(String[] args) {
String text = "안녕하세요. 닭가슴살은 퍽퍽하고, 스타벅스 자바칩프라푸치노 맛나요ㅋㅋ";
for(int i = 0; i < text.length(); i++) {
char uniVal = text.charAt(i);
// 한글일 경우만 시작해야 하기 때문에 0xAC00부터 아래의 로직을 실행한다
if(uniVal >= 0xAC00) {
System.out.print(uniVal + "=>");
uniVal = (char)(uniVal - 0xAC00);
char cho = (char)(uniVal/28/21);
char joong = (char) ((uniVal)/28%21);
char jong = (char) (uniVal % 28); // 종성의 첫번째는 채움이기 때문에
System.out.println(CHO[cho] + JOONG[joong] + JONG[jong]);
} else {
System.out.println(uniVal + "=>" + uniVal);
}
}
}
}
자모 추출 공식을 보면 현재유니코드 - 0xAC00은 Default이기 때문에 시작부터 0xAC00을 뺀 값을 변수로 가면 소스가 좀 더 심플해진다. 자모가 결합된 문자가 아닌 경우 else로 처리한다. "ㅋㅋㅋ"와 같이 자음만 있는 한글의 경우 0xAC00보다 낮은 유니코드값을 갖기 때문에 그대로 else로 출력한다.
실행결과
안=>ㅇㅏㄴ
녕=>ㄴㅕㅇ
하=>ㅎㅏ
세=>ㅅㅔ
요=>ㅇㅛ
.=>.
=>
닭=>ㄷㅏㄺ
가=>ㄱㅏ
슴=>ㅅㅡㅁ
살=>ㅅㅏㄹ
은=>ㅇㅡㄴ
=>
퍽=>ㅍㅓㄱ
퍽=>ㅍㅓㄱ
하=>ㅎㅏ
고=>ㄱㅗ
,=>,
=>
스=>ㅅㅡ
타=>ㅌㅏ
벅=>ㅂㅓㄱ
스=>ㅅㅡ
=>
자=>ㅈㅏ
바=>ㅂㅏ
칩=>ㅊㅣㅂ
프=>ㅍㅡ
라=>ㄹㅏ
푸=>ㅍㅜ
치=>ㅊㅣ
노=>ㄴㅗ
=>
맛=>ㅁㅏㅅ
나=>ㄴㅏ
요=>ㅇㅛ
ㅋ=>ㅋ
ㅋ=>ㅋ
연관포스팅
'인공지능 및 데이터과학 > 자연어처리' 카테고리의 다른 글
Word2Vec #1, (개념, CBOW와 Skip-gram) (0) | 2021.05.10 |
---|---|
형태소 분석기, Okt(Open Korean Text) (구)트위터 형태소분석기 (2) | 2021.01.16 |
[Java] 자바 아스키코드및 유니코드로 문자 변환 (0) | 2021.01.12 |
형태소 분석의 개념과 konlpy로 사용 하기 (0) | 2021.01.10 |
[Python] 파이썬을 이용한 자모(초성/중성/종성) 분리 및 결합하기 (0) | 2020.12.22 |