[백업][가리사니] 한글 분해 (자바 예제 포함)
charset, java

이 문서는 가리사니 개발자 포럼에 올렸던 글의 백업 파일입니다. 오래된 문서가 많아 현재 상황과 맞지 않을 수 있습니다.

아주 오래전에 만들어둔 한글 분해인데... 최근 필요할 일이 있을 수 있어 올립니다.

유니코드의 한글은 아래와 같이 할당되어있습니다. 자음 ㄱ : 12593 / ㄲ : 12594 / ㄳ : 12595 / ㄴ : 12596 ... ㅎ : 12622 모음 ㅏ : 12623 / ㅐ : 12624 / ㅑ : 12625 / ㅒ : 12626 ... ㅣ : 12643 완성자 가 : 44032 / 각 : 44033/ 갂 : 44034/ 갃 : 44035 ... 힣 : 55203

초성순서 총 19자 ㄱ ㄲ ㄴ ㄷ ㄸ ㄹ ㅁ ㅂ ㅃ ㅅ ㅆ ㅇ ㅈ ㅉ ㅊ ㅋ ㅌ ㅍ ㅎ 중성순서 총 21자 ㅏ ㅐ ㅑ ㅒ ㅓ ㅔ ㅕ ㅖ ㅗ ㅘ ㅙ ㅚ ㅛ ㅜ ㅝ ㅞ ㅟ ㅠ ㅡ ㅢ ㅣ 종성순서 총 28자 (받침없음 포함) (받침없음) ㄱ ㄲ ㄳ ㄴ ㄵ ㄶ ㄷ ㄹ ㄺ ㄻ ㄼ ㄽ ㄾ ㄿ ㅀ ㅁ ㅂ ㅄ ㅅ ㅆ ㅇ ㅈ ㅊ ㅋ ㅌ ㅍ ㅎ

위 초성/중성/종성을 반복하여 표시됩니다. 중성 21자 종성 28자를 곱하면 588(=21 * 28)자 가 됩니다. 즉 가의 코드가 '가' : 44032 라면 다음 초성 ㄲ의 첫 글자인 '까'의 코드는 44620(=44032 + 588(=21 * 28))이 됩니다. 이것을 응용하면 중성까지 나눠진 상태에서 종성이 0(28로 나누어 떨어지는 경우)일 경우 종성(받침)이 없는 글자가 됩니다. 설명이.. 이상한 것 같으니.. 아래 코드예제를 적어두었습니다.

예제

너무 오래전에 짠거라.. 네이밍이과.. 소스가 개판.. ㅠㅠ...입니다..; 요즘 스타일로 짜면 배열이 아니라 static에 switch 함수를 두고 자바8의 스트림으로 빼는 것도 좋다고 생각합니다.

// 일반 분해
private final static char[] KO_INIT_S =
{
	'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ',
	'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
}; // 19

private final static char[] KO_INIT_M =
{
	'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ',
	'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ'
}; // 21
private final static char[] KO_INIT_E =
{
	0, 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ',
	'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
}; // 28

// 완전 분해
private final static char[][] KO_ATOM_S =
{
	{ 'ㄱ' }, { 'ㄱ', 'ㄱ' }, { 'ㄴ' }, { 'ㄷ' }, { 'ㄷ', 'ㄷ' }, { 'ㄹ' }, { 'ㅁ' },
	{ 'ㅂ' }, { 'ㅂ', 'ㅂ' }, { 'ㅅ' }, { 'ㅅ', 'ㅅ' }, { 'ㅇ' }, { 'ㅈ' }, { 'ㅈ', 'ㅈ' }, { 'ㅊ' }, { 'ㅋ' }, { 'ㅌ' },
	{ 'ㅍ' }, { 'ㅎ' }
};
private final static char[][] KO_ATOM_M =
{
	{ 'ㅏ' }, { 'ㅐ' }, { 'ㅑ' }, { 'ㅒ' }, { 'ㅓ' }, { 'ㅔ' }, { 'ㅕ' }, { 'ㅖ' },
	{ 'ㅗ' }, { 'ㅗ', 'ㅏ' }, { 'ㅗ', 'ㅐ' }, { 'ㅗ', 'ㅣ' }, { 'ㅛ' }, { 'ㅜ' }, { 'ㅜ', 'ㅓ' }, { 'ㅜ', 'ㅔ' },
	{ 'ㅜ', 'ㅣ' }, { 'ㅠ' }, { 'ㅡ' }, { 'ㅡ', 'ㅣ' }, { 'ㅣ' }
};
private final static char[][] KO_ATOM_E =
{
	{}, { 'ㄱ' }, { 'ㄱ', 'ㄱ' }, { 'ㄱ', 'ㅅ' }, { 'ㄴ' }, { 'ㄴ', 'ㅈ' },
	{ 'ㄴ', 'ㅎ' }, { 'ㄷ' }, { 'ㄹ' }, { 'ㄹ', 'ㄱ' }, { 'ㄹ', 'ㅁ' }, { 'ㄹ', 'ㅂ' }, { 'ㄹ', 'ㅅ' }, { 'ㄹ', 'ㅌ' },
	{ 'ㄹ', 'ㅍ' }, { 'ㄹ', 'ㅎ' }, { 'ㅁ' }, { 'ㅂ' }, { 'ㅂ', 'ㅅ' }, { 'ㅅ' }, { 'ㅅ', 'ㅅ' }, { 'ㅇ' }, { 'ㅈ' },
	{ 'ㅊ' }, { 'ㅋ' }, { 'ㅌ' }, { 'ㅍ' }, { 'ㅎ' }
};
// 쌍자음이나 이중모음을 분해
private final static char[][] KO_ATOM_P =
{
	{ 'ㄱ' }, { 'ㄱ', 'ㄱ' }, { 'ㄱ', 'ㅅ' }, { 'ㄴ' }, { 'ㄴ', 'ㅈ' },
	{ 'ㄴ', 'ㅎ' }, { 'ㄷ' }, { 'ㄸ' }, { 'ㄹ' }, { 'ㄹ', 'ㄱ' }, { 'ㄹ', 'ㅁ' }, { 'ㄹ', 'ㅂ' }, { 'ㄹ', 'ㅅ' },
	{ 'ㄹ', 'ㄷ' }, { 'ㄹ', 'ㅍ' }, { 'ㄹ', 'ㅎ' }, { 'ㅁ' }, { 'ㅂ' }, { 'ㅂ', 'ㅂ' }, { 'ㅂ', 'ㅅ' }, { 'ㅅ' },
	{ 'ㅅ', 'ㅅ' }, { 'ㅇ' }, { 'ㅈ' }, { 'ㅈ', 'ㅈ' }, { 'ㅊ' }, { 'ㅋ' }, { 'ㅌ' }, { 'ㅍ' }, { 'ㅎ' }, { 'ㅏ' }, { 'ㅐ' },
	{ 'ㅑ' }, { 'ㅒ' }, { 'ㅓ' }, { 'ㅔ' }, { 'ㅕ' }, { 'ㅖ' }, { 'ㅗ' }, { 'ㅗ', 'ㅏ' }, { 'ㅗ', 'ㅐ' }, { 'ㅗ', 'ㅣ' },
	{ 'ㅛ' }, { 'ㅜ' }, { 'ㅜ', 'ㅓ' }, { 'ㅜ', 'ㅔ' }, { 'ㅜ', 'ㅣ' }, { 'ㅠ' }, { 'ㅡ' }, { 'ㅡ', 'ㅣ' }, { 'ㅣ' }
};

/** 한글부분을 초성으로 교체합니다. */
public static String toKoChosung(String text)
{
	if (text == null) { return null; }

	// 한글자가 한글자와 그대로 대응됨.
	// 때문에 기존 텍스트를 토대로 작성된다.
	char[] rv = text.toCharArray();
	char ch;

	for (int i = 0 ; i < rv.length ; i++)
	{
		ch = rv[i];
		if (ch >= '가' && ch <= '힣')
		{
			rv[i] = KO_INIT_S[(ch - '가') / 588]; // 21 * 28
		}
	}

	return new String(rv);
}

/** 한글부분을 자소로 분리합니다. <br>많다 = [ㅁㅏㄶㄷㅏ] */
public static String toKoJaso(String text)
{
	if (text == null) { return null; }
	// StringBuilder의 capacity가 0으로 등록되는 것 방지.
	if (text.length() == 0) { return ""; }

	// 한글자당 최대 3글자가 될 수 있다.
	// 추가 할당 없이 사용하기위해 capacity 를 최대 글자 수 만큼 지정하였다.
	StringBuilder rv = new StringBuilder(text.length() * 3);

	for (char ch : text.toCharArray())
	{
		if (ch >= '가' && ch <= '힣')
		{
			// 한글의 시작부분을 구함
			int ce = ch - '가';
			// 초성을 구함
			rv.append(KO_INIT_S[ce / (588)]); // 21 * 28
			// 중성을 구함
			rv.append(KO_INIT_M[(ce = ce % (588)) / 28]); // 21 * 28
			 // 종성을 구함
			if ((ce = ce % 28) != 0)
			{
				rv.append(KO_INIT_E[ce]);
			}
		}
		else
		{
			rv.append(ch);
		}
	}

	return rv.toString();
}

/** 한글부분을 자소로 완전 분리합니다. <br>많다 = [ㅁㅏㄴㅎㄷㅏ]*/
public static String toKoJasoAtom(String text)
{
	if (text == null) { return null; }
	// StringBuilder의 capacity가 0으로 등록되는 것 방지.
	if (text.length() == 0) { return ""; }

	// 한글자당 최대 6글자가 될 수 있다.
	// 추가 할당 없이 사용하기위해 capacity 를 최대 글자 수 만큼 지정하였다.
	StringBuilder rv = new StringBuilder(text.length() * 6);

	for (char ch : text.toCharArray())
	{
		if (ch >= '가' && ch <= '힣')
		{
			// 한글의 시작부분을 구함
			int ce = ch - '가';
			// 초성을 구함
			rv.append(KO_ATOM_S[ce / (588)]); // 21 * 28
			// 중성을 구함
			rv.append(KO_ATOM_M[(ce = ce % (588)) / 28]); // 21 * 28
			// 종성을 구함
			if ((ce = ce % 28) != 0)
			{
				rv.append(KO_ATOM_E[ce]);
			}
		}
		// 쌍자음과 이중모음 분리
		else if (ch >= 'ㄱ' && ch <= 'ㅣ')
		{
			rv.append(KO_ATOM_P[ch - 'ㄱ']);
		}
		else
		{
			rv.append(ch);
		}
	}

	return rv.toString();
}