Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 이클립스 플러그인 개발
- IPTV
- Callable
- String
- Runnable
- ACAP
- StringUtils
- Executor
- Log4J
- RSA
- ORM
- 자바
- xlet
- Postman
- PKCS
- Freemaker
- Instrumentation
- mac
- PKCS#8
- 암호학
- 자바 암호화
- Executors
- Java
- AES
- sha1
- date
- DAMO
- JCE
- 한글조사
- 한글조사처리
Archives
- Today
- Total
오늘은 어디로 갈까...
한글(Hangul) 0x00 본문
문자하면 빼 놓을 수 없는게 한글이다.(한국에 사니...)
오늘은 한글에 대해서 알아보도록 하자.
지난번 시간에 자바는 유니코드 기반이라고 했으니, 유니코드 사이트(http://www.unicode.org/)에 가서 열심히 읽어보자.
유니코드 한국어 명세서(Unicode Korean specific)를 보면 네개의 영역이 있다.
첫번째, 0x1100-0x11F9 까지의 한글 자모 영역은 한글에서 사용되는 자모들을 초성 자음/중성 모음/종성 자음으로 각각 나누어 한글자씩 대응시킨것으로 이 영역에 있는 글자들을 이용하면 한글 고어까지 표현이 가능하나, 다른 캐릭터셋(ChracterSet)으로 변환이 어렵다는 등의 문제를 가지고 있다.
두번째, 0x3130-0x318E 까지의 한글 호환 자모 영역은 초/중/종성을 구분하지 않고 그냥 사용되는 모든 자모들을 한데 묶여 놓은것이다. 이 영역은 0x3130-0x314E(현대자음 40개), 0x314F-0x3163(현대모음 21개), 0x3164(채움코드), 0x3165-0x318E(옛글자모로) 나눌 수 있다.
세번째, 0xAC00-0xD7A3 까지의 한글 영역은 현대 한글 자모로 표현 가능한 모든 한글 문자들(11172자=19*21*28))이 들어 있습니다.
네번째, 0xFF00-0xFFEF 까지의 반각 자모 영역이다.
여기서는 두번째와 세번째 영역을 사용해서 처리하도록한다.
자, 뭔소리인지 이해가 안가는가? 그럼 직접 출력해보자.
이젠 느낌이 오는가? 그러면 달려보자~
1. Hangul & HangulTest 클래스 만들기
- Hangul 클래스 만들기
- HangulTest 클래스 만들기
2. 한글인지 판단하기.
- 입력한 문자가 한글인지 판단하는 메소드를 만들어보자.
2. 한글에서 초성/중성/종성을 추출하기.
- 한글 자모 영역(0x3130-0x318E)과 한글 영역(0xAC00-0xD7A3) 간단한 계산식으로 쉽게 변환이 가능하다.
- 한글(한문자) = 0xAC00 + (초성_Index * 21 * 28) + (중성_Index * 28) + 종성_Index
- 초성_Index = (한글 - 0xAC00) / (21 * 28)
- 중성_Index = ((한글 - 0xAC00) % (21 * 28)) / 28
- 종성_Index = ((한글 - 0xAC00) % (21 * 28)) % 28
- 구현해보도록 하자
3. 초성/중성/초성을 결합하여 한글자 만들기.
- 이번에 나눠진 자모들을 합쳐서 글자를 만들어보자.
- 잘 작동할까? 테스트해보자.
4. 한글 조사 처리
- 위에서 만든 메소드를 가지고 조사(은/는, 이/가, 와/과 등...) 처리를 해보는것을 만들어보자.
- 이것도 테스트해보도록 하자. "마한은 멋진 나라이다. 수지가 여행을 간다."
오늘은 한글에 대해서 알아보도록 하자.
지난번 시간에 자바는 유니코드 기반이라고 했으니, 유니코드 사이트(http://www.unicode.org/)에 가서 열심히 읽어보자.
유니코드 한국어 명세서(Unicode Korean specific)를 보면 네개의 영역이 있다.
Hangul Jamo | 한글 자모 영역 |
0x1100-0x11F9 | U1100.pdf |
Hangul Compatibility Jamo | 한글 호환 자모 영역 |
0x3130-0x318E | U3130.pdf |
Hangul Syllables | 한글 영역 |
0xAC00-0xD7A3 | UAC00.pdf |
Halfwidth Jamo | 반각 자모 |
0xFF00-0xFFEF | UAC00.pdf |
첫번째, 0x1100-0x11F9 까지의 한글 자모 영역은 한글에서 사용되는 자모들을 초성 자음/중성 모음/종성 자음으로 각각 나누어 한글자씩 대응시킨것으로 이 영역에 있는 글자들을 이용하면 한글 고어까지 표현이 가능하나, 다른 캐릭터셋(ChracterSet)으로 변환이 어렵다는 등의 문제를 가지고 있다.
두번째, 0x3130-0x318E 까지의 한글 호환 자모 영역은 초/중/종성을 구분하지 않고 그냥 사용되는 모든 자모들을 한데 묶여 놓은것이다. 이 영역은 0x3130-0x314E(현대자음 40개), 0x314F-0x3163(현대모음 21개), 0x3164(채움코드), 0x3165-0x318E(옛글자모로) 나눌 수 있다.
세번째, 0xAC00-0xD7A3 까지의 한글 영역은 현대 한글 자모로 표현 가능한 모든 한글 문자들(11172자=19*21*28))이 들어 있습니다.
네번째, 0xFF00-0xFFEF 까지의 반각 자모 영역이다.
여기서는 두번째와 세번째 영역을 사용해서 처리하도록한다.
자, 뭔소리인지 이해가 안가는가? 그럼 직접 출력해보자.
// 한글 호환 자모 영역 for (int i = 0x3130;i <= 0x318E; i++) { System.out.print((char)i); } System.out.println(); // 한글 영역 for (int i = 0xAC00;i <= 0xD7A3; i++) { System.out.print((char)i); }
이젠 느낌이 오는가? 그러면 달려보자~
1. Hangul & HangulTest 클래스 만들기
- Hangul 클래스 만들기
- HangulTest 클래스 만들기
2. 한글인지 판단하기.
- 입력한 문자가 한글인지 판단하는 메소드를 만들어보자.
package kr.kangwoo.util.hangul; public class Hangul { private static final char HANGUL_SYLLABLES_BEGIN = 0xAC00; private static final char HANGUL_SYLLABLES_END = 0xD7A3; private static final char HANGUL_COMPATIBILITY_JAMO_BEGIN = 0x3130; private static final char HANGUL_COMPATIBILITY_JAMO_END = 0x318E; /** * <p>입력한 문자가 한글인지 판단한다.</p> * <p>(코드값이 한글 영역(0xAC00-0xD7A3)인지 판단)</p> * * @param c * @return 한글이면 <code>true</code>, 아니면 <code>false</code> */ public static boolean isHangulSyllables(char ch) { return ch >= HANGUL_SYLLABLES_BEGIN && ch <= HANGUL_SYLLABLES_END; } /** * <p>입력한 문자가 한글 호환 자모인지 판단한다.</p> * <p>(코드값이 한글 호환 자모 영역(0x1100-0x318E)인지 판단)</p> * * @param ch * @return 한글 호환 자모이면 <code>true</code>, 아니면 <code>false</code> */ public static boolean isHangulCompatibilityJamo(char ch) { return ch >= HANGUL_COMPATIBILITY_JAMO_BEGIN && ch <= HANGUL_COMPATIBILITY_JAMO_END; } }- 입력한 문자가 한글인지 판단하는 메소드가 정상 작동하는지 테스트해보자.
package kr.kangwoo.util.hangul; import org.junit.Assert; import org.junit.After; import org.junit.Before; import org.junit.Test; public class HangulTest { @Before public void setUp() throws Exception { } @After public void tearDown() throws Exception { } @Test public void isHangulSyllables() { Assert.assertEquals(Hangul.isHangulSyllables('ㄱ'), false); Assert.assertEquals(Hangul.isHangulSyllables('ㅏ'), false); Assert.assertEquals(Hangul.isHangulSyllables('A'), false); Assert.assertEquals(Hangul.isHangulSyllables('a'), false); Assert.assertEquals(Hangul.isHangulSyllables('Z'), false); Assert.assertEquals(Hangul.isHangulSyllables('z'), false); Assert.assertEquals(Hangul.isHangulSyllables('1'), false); Assert.assertEquals(Hangul.isHangulSyllables('\"'), false); Assert.assertEquals(Hangul.isHangulSyllables('가'), true); } @Test public void isHangulCompatibilityJamo() { Assert.assertEquals(Hangul.isHangulCompatibilityJamo('ㄱ'), true); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('ㅀ'), true); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('ㅎ'), true); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('ㅏ'), true); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('ㅣ'), true); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('ㆌ'), true); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('A'), false); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('a'), false); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('Z'), false); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('z'), false); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('1'), false); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('\"'), false); Assert.assertEquals(Hangul.isHangulCompatibilityJamo('가'), false); } }
2. 한글에서 초성/중성/종성을 추출하기.
- 한글 자모 영역(0x3130-0x318E)과 한글 영역(0xAC00-0xD7A3) 간단한 계산식으로 쉽게 변환이 가능하다.
- 한글(한문자) = 0xAC00 + (초성_Index * 21 * 28) + (중성_Index * 28) + 종성_Index
- 초성_Index = (한글 - 0xAC00) / (21 * 28)
- 중성_Index = ((한글 - 0xAC00) % (21 * 28)) / 28
- 종성_Index = ((한글 - 0xAC00) % (21 * 28)) % 28
- 구현해보도록 하자
/** * 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' */ private static final char[] HANGUL_CHOSEONG = {0x3131, 0x3132, 0x3134, 0x3137, 0x3138, 0x3139, 0x3141, 0x3142, 0x3143, 0x3145, 0x3146, 0x3147, 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e}; /** * 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ' */ private static final char[] HANGUL_JUNGSEONG = {0x314f, 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, 0x3158, 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, 0x3160, 0x3161, 0x3162, 0x3163}; /** * ' ', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' */ private static final char[] HANGUL_JONGSEONG = {0x0000, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d, 0x313e, 0x313f, 0x3140, 0x3141, 0x3142, 0x3144, 0x3145, 0x3146, 0x3147, 0x3148, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e}; public static final int HANGUL_CHOSEONG_SIZE = HANGUL_CHOSEONG.length; // 19 public static final int HANGUL_JUNGSEONG_SIZE = HANGUL_JUNGSEONG.length; // 21 public static final int HANGUL_JONGSEONG_SIZE = HANGUL_JONGSEONG.length; // 28 private static final Map<Character, Integer> HANGUL_CHOSEONG_CODE_TABLE = new HashMap<Character, Integer>(); private static final Map<Character, Integer> HANGUL_JUNGSEONG_CODE_TABLE = new HashMap<Character, Integer>(); private static final Map<Character, Integer> HANGUL_JONGSEONG_CODE_TABLE = new HashMap<Character, Integer>(); static { for (int i = 0; i < HANGUL_CHOSEONG.length; i++) { HANGUL_CHOSEONG_CODE_TABLE.put(HANGUL_CHOSEONG[i], i); } for (int i = 0; i < HANGUL_JUNGSEONG.length; i++) { HANGUL_JUNGSEONG_CODE_TABLE.put(HANGUL_JUNGSEONG[i], i); } for (int i = 0; i < HANGUL_JONGSEONG.length; i++) { HANGUL_JONGSEONG_CODE_TABLE.put(HANGUL_JONGSEONG[i], i); } } /** * <p>초성을 추출한다.</p> * * @param ch * @return */ public static char getChoseong(char ch) { if (isHangulSyllables(ch) == false) { throw new IllegalArgumentException("입력값이 잘못되었습니다. (" + ch + ")"); } int hCode = (ch - HANGUL_SYLLABLES_BEGIN) / (HANGUL_JUNGSEONG_SIZE * HANGUL_JONGSEONG_SIZE); return HANGUL_CHOSEONG[hCode]; } /** * <p>중성을 추출한다.</p> * * @param ch * @return */ public static char getJungseong(char ch) { if (isHangulSyllables(ch) == false) { throw new IllegalArgumentException("입력값이 잘못되었습니다. (" + ch + ")"); } int hCode = ((ch - HANGUL_SYLLABLES_BEGIN) % (HANGUL_JUNGSEONG_SIZE * HANGUL_JONGSEONG_SIZE)) / HANGUL_JONGSEONG_SIZE; return HANGUL_JUNGSEONG[hCode]; } /** * <p>종성을 추출한다.</p> * * @param ch * @return */ public static char getJongseong(char ch) { if (isHangulSyllables(ch) == false) { throw new IllegalArgumentException("입력값이 잘못되었습니다. (" + ch + ")"); } int hCode = ((ch - HANGUL_SYLLABLES_BEGIN) % (HANGUL_JUNGSEONG_SIZE * HANGUL_JONGSEONG_SIZE)) % HANGUL_JONGSEONG_SIZE; return HANGUL_JONGSEONG[hCode]; } /** * <p>초/중/종성을 추출한다. * </p> * @param ch * @return [초성, 중성, 종성] or [초성, 중성] */ public static char[] getJamo(char ch) { char[] jamo = new char[3]; jamo[0] = getChoseong(ch); jamo[1] = getJungseong(ch); jamo[2] = getJongseong(ch); if (jamo[2] == HANGUL_JONGSEONG[0]) { char[] temp = new char[2]; temp[0] = jamo[0]; temp[1] = jamo[1]; jamo = temp; } return jamo; } /** * <p>종성이 존재하는지 여부를 판단한다.</p> * * @param ch * @return 종성이 존재하면 <code>true</code>, 아니면 <code>false</code> */ public static boolean hasJongseong(char ch) { return getJongseong(ch) != HANGUL_JONGSEONG[0]; }- 테스트 해보자
@Test public void getChoseong() { Assert.assertEquals(Hangul.getChoseong('한'), 'ㅎ'); Assert.assertEquals(Hangul.getChoseong('지'), 'ㅈ'); Assert.assertEquals(Hangul.getChoseong('나'), 'ㄴ'); try { Assert.assertEquals(Hangul.getChoseong('a'), 'a'); Assert.fail("Failed"); } catch(IllegalArgumentException e) { Assert.assertTrue(true); } } @Test public void getJungseong() { Assert.assertEquals(Hangul.getJungseong('한'), 'ㅏ'); Assert.assertEquals(Hangul.getJungseong('글'), 'ㅡ'); try { Hangul.getJungseong('a'); Assert.fail("한글이 아닐 경우 검증 오류"); } catch(IllegalArgumentException e) { Assert.assertTrue(true); } } @Test public void getJongseong() { Assert.assertEquals(Hangul.getJongseong('한'), 'ㄴ'); Assert.assertEquals(Hangul.getJongseong('글'), 'ㄹ'); Assert.assertEquals(Hangul.getJongseong('사'), (char)0x0000); try { Hangul.getJongseong('a'); Assert.fail("한글이 아닐 경우 검증 오류"); } catch(IllegalArgumentException e) { Assert.assertTrue(true); } try { Hangul.getJongseong('ㄱ'); Assert.fail("한글이 아닐 경우 검증 오류"); } catch(IllegalArgumentException e) { Assert.assertTrue(true); } } @Test public void getJamo() { Assert.assertArrayEquals(Hangul.getJamo('한'), new char[] {'ㅎ', 'ㅏ', 'ㄴ'}); Assert.assertArrayEquals(Hangul.getJamo('지'), new char[] {'ㅈ', 'ㅣ'}); try { Hangul.getJamo('a'); Assert.fail("Failed"); } catch(IllegalArgumentException e) { Assert.assertTrue(true); } } @Test public void hasJongseong() { Assert.assertTrue(Hangul.hasJongseong('한')); Assert.assertFalse(Hangul.hasJongseong('지')); }
3. 초성/중성/초성을 결합하여 한글자 만들기.
- 이번에 나눠진 자모들을 합쳐서 글자를 만들어보자.
/** * <p>초성, 중성을 겹합하여 한글자로 만든다.</p> * * @param choseong 초성 * @param jungseong 중성 * @return */ public static char toHangul(char choseong, char jungseong) { return toHangul(choseong, jungseong, HANGUL_JONGSEONG[0]); } /** * <p>초성, 중성, 종성을 결합하여 한글자로 만든다.</p> * * @param choseong 초성 * @param jungseong 중성 * @param jongseong 종성 * @return */ public static char toHangul(char choseong, char jungseong, char jongseong) { Integer choseongIndex = HANGUL_CHOSEONG_CODE_TABLE.get(choseong); if (choseongIndex == null) { throw new IllegalArgumentException("초성이 잘못되었습니다. (" + choseong + ")"); } Integer jungseongIndex = HANGUL_JUNGSEONG_CODE_TABLE.get(jungseong); if (jungseongIndex == null) { throw new IllegalArgumentException("중성이 잘못되었습니다. (" + jungseong + ")"); } if (jongseong == ' ') { jongseong = HANGUL_JONGSEONG[0]; } Integer jongseongIndex = HANGUL_JONGSEONG_CODE_TABLE.get(jongseong); if (jongseongIndex == null) { throw new IllegalArgumentException("종성이 잘못되었습니다. (" + jongseong + ")"); } return (char)(HANGUL_SYLLABLES_BEGIN + (choseongIndex * (HANGUL_JUNGSEONG_SIZE * HANGUL_JONGSEONG_SIZE)) + (jungseongIndex * HANGUL_JONGSEONG_SIZE) + jongseongIndex); }
- 잘 작동할까? 테스트해보자.
@Test public void toHangul() { Assert.assertEquals(Hangul.toHangul('ㅈ', 'ㅣ'), '지'); Assert.assertEquals(Hangul.toHangul('ㄴ', 'ㅏ'), '나'); } @Test public void toHangul2() { Assert.assertEquals(Hangul.toHangul('ㅎ', 'ㅏ', 'ㄴ'), '한'); Assert.assertEquals(Hangul.toHangul('ㄱ', 'ㅡ', 'ㄹ'), '글'); }
4. 한글 조사 처리
- 위에서 만든 메소드를 가지고 조사(은/는, 이/가, 와/과 등...) 처리를 해보는것을 만들어보자.
/** * <p>글자에 알맞는 조사를 선택한다.</p> * * <pre> * Hangul.getJosa('한', '은', '는') = '은' * Hangul.getJosa('지', '이', '가') = '가' * </pre> * * @param ch * @param aux1 종성이 있을때 붙일 조사 * @param aux2 종성이 없을때 붙일 조사 * @return */ public static char getJosa(char ch, char aux1, char aux2) { return hasJongseong(ch) ? aux1 : aux2; } /** * <p>글자에 알맞는 조사를 선택한다.</p> * * <pre> * Hangul.getJosa('한', "은", "는") = "은" * Hangul.getJosa('지', "이", "가") = "가" * </pre> * * @param ch * @param aux1 종성이 있을때 붙일 조사 * @param aux2 종성이 없을때 붙일 조사 * @return */ public static String getJosa(char ch, String aux1, String aux2) { return hasJongseong(ch) ? aux1 : aux2; }
- 이것도 테스트해보도록 하자. "마한은 멋진 나라이다. 수지가 여행을 간다."
@Test public void getJosa() { Assert.assertEquals(Hangul.getJosa('한', '은', '는'), '은'); Assert.assertEquals(Hangul.getJosa('지', '이', '가'), '가'); } @Test public void getJosa2() { Assert.assertEquals(Hangul.getJosa('한', "은", "는"), "은"); Assert.assertEquals(Hangul.getJosa('지', "이", "가"), "가"); }