컴퓨터 엑셀 워드 포토숍 구글어스 WINDOWS JAVASCRIPT JAVA C++

 
Previous Post 블로그 디자인을 결국 잡지 형태로 변경보청기 등의 기계 장치 없이 '소리'를 크게 듣는 법[Perl/펄] 윈도우용 액티브펄(ActivePerl) 다운로드 설치법[건강] 예풍혈(턱관절 부근) 지압: 치통, 잇몸 질환, 두통, 시력 향상에 효과블로그가 구글에 잘 인덱스 안되던 이유를 좀 알게 됨예전 포스트들이 구글에서 잘 검색되지 않아 고민 중[RSS & Atom 리더] 한RSS (www.hanrss.com) 가 가장 쓰기 편했다[추억의 스냅샷] 핫메일(Hotmail)의 2000년 3월의 화면들구글 어스(Google Earth)에서 자금성(자장면집 아님), 피라미드 발견[Winamp/MP3플레이어] 최고의 윈앰프 스킨들 모음집

[Perl/펄] 한글 자소 분리: '똠방각하'를 'ㄸㅗㅁㅂㅏㅇㄱㅏㄱㅎㅏ'로 자동 변환

Friday, March 10, 2006

한국어 용언의 검색은 상당히 까다롭다. 불규칙 활용이 많기 때문만이 아니라, 한글이 알파벳과 달리 음절 단위의 문자이기 때문이다. 즉 풀어쓰기를 하지 않고 모아쓰기를 하기 때문이다.

가령 문서에서 "파랗다"의 모든 활용형들, 즉 "파란", "파라니" 등을 한꺼번에 검색할 수는 없다. 검색을 해야 한다면 자소 분리가 반드시 필요하다. 다시 말해, 풀어쓰기 형태로 만들어서 검색해야 한다. 검색할 문자열과 검색 대상이 될 문자열들을 모두 풀어놓아야 자소 단위로 검색을 할 수가 있다.


파랗다
파란
파라니
파랄
파래


가령, 필자가 알기로 "파랗다"의 어근은 "파ㄹ"이다.(아니면 말고^^;) 검색해야 할 문자열인 이것을 "ㅍㅏㄹ"로 풀어 놓은 후, 검색 대상이 되는 문서도 다음과 같이 풀어쓰기를 한다. 그런 후 "ㅍㅏㄹ"로 검색하면 "파랗다"의 모든 활용형들이 한꺼번에 검색이 된다.


ㅍㅏㄹㅏㅎㄷㅏ
ㅍㅏㄹㅏㄴ
ㅍㅏㄹㅏㄴㅣ
ㅍㅏㄹㅏㄹ
ㅍㅏㄹㅐ


그러면 풀어쓰기는 어떻게 해야 하는 걸까. 사람이 손으로 일일이 할 수는 없는 일이다. 여기서 소개하는 펄 프로그램은 이렇게 한글 풀어쓰기를 만드는 것이다.

이것은, 다른 분들이 만든 C++과 파스칼(델파이)로 된 자소 분리 프로그램을 필자가 펄로 변환한 것이다. 꼭 필요해서가 아니라 호기심으로 한 번 해 보았다. 이런 간단한 프로그램은 C++이나 파스칼보다 펄이 적합할 것이다.

한글 조합형의 경우, 비트 연산으로 자소 분리를 한다. 그러나 현재 윈도OS에서 사용되는 완성형의 경우는 비트 연산이 안되고 자소 분리가 사실상 불가능하다.

그래서 '완성형 한글을 유니코드 한글로 변환하여 자소를 분리'한 후, '다시 완성형 한글로 변환해 출력'하는 것이 이 프로그램의 골자다. 유니코드 한글 역시 완성형이기는 하지만, 글자들이 합리적으로 배열되어 있어, 간단한 계산만으로도 자소를 분리할 수 있다.



다음의 펄 코드를 jaso_bunri.pl 등의 적당한 이름으로 하드에 저장한다. 일부 불필요한 주석들이 붙어 있는데 신경 쓰지 않아도 된다.

#!/usr/bin/perl
########################################################################
# 완성형 한글의 한글 문자열을 초성/중성/종성으로 분리하여 출력
# v1.0a
########################################################################
use strict; use warnings;

# utf8 출력은 use encoding "cp949", STDOUT => "utf8";
# 인코딩을 euc-kr 로 하면 '똠' '힣' 등이 나오지 않음. MS의 euc-kr 은 사실상 cp949임.
# 한글 인코딩은 perlko 라는 펄 문서를 참조.
use encoding "cp949";





# 이것이 프로그램 본체
foreach (<>) {
  utf8::upgrade($_);
  LF2LFCR($_);
  print hangul2jaso($_);
}




sub hangul2jaso { # 유니코드 한글 문자열을 입력 받음
  my ($a, $b, $c); # 자소 버퍼: 초성/중성/종성 순

                 # ㄱ      ㄲ      ㄴ      ㄷ      ㄸ      ㄹ      ㅁ      ㅂ      ㅃ      ㅅ      ㅆ      ㅇ      ㅈ      ㅉ      ㅊ      ㅋ      ㅌ      ㅍ      ㅎ
  my @ChoSung   = (0x3131, 0x3132, 0x3134, 0x3137, 0x3138, 0x3139, 0x3141, 0x3142, 0x3143, 0x3145, 0x3146, 0x3147, 0x3148, 0x3149, 0x314a, 0x314b, 0x314c, 0x314d, 0x314e);
                 # ㅏ      ㅐ      ㅑ      ㅒ      ㅓ      ㅔ      ㅕ      ㅖ      ㅗ      ㅘ      ㅙ      ㅚ      ㅛ      ㅜ      ㅝ      ㅞ      ㅟ      ㅠ      ㅡ      ㅢ      ㅣ
  my @JwungSung = (0x314f, 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, 0x3158, 0x3159, 0x315a, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, 0x3160, 0x3161, 0x3162, 0x3163);
                 #         ㄱ      ㄲ      ㄳ      ㄴ      ㄵ      ㄶ      ㄷ      ㄹ      ㄺ      ㄻ      ㄼ      ㄽ      ㄾ      ㄿ      ㅀ      ㅁ      ㅂ      ㅄ      ㅅ      ㅆ      ㅇ      ㅈ      ㅊ      ㅋ      ㅌ      ㅍ      ㅎ
  my @JongSung  = (0,      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);

  my @input_chars = unpack("U*", $_[0]); # 한글 유니코드 문자열을 16진수 배열로
  my $result;

  foreach (@input_chars) {
     # "AC00:가" ~ "D7A3:힣" 에 속한 글자면 분해.
     # regex로는 아스키 문자까지 한글로 오인함. 버그?
     if ($_ >= 0xAC00 && $_ <= 0xD7A3) {

        $c = $_ - 0xAC00;
        $a = $c / (21 * 28);
        $c = $c % (21 * 28);
        $b = $c / 28;
        $c = $c % 28;

        $a = int($a); $b = int($b); $c = int($c);
        $result .= chr($ChoSung[$a]) . chr($JwungSung[$b]);
        $result .= chr($JongSung[$c]) if ($c); # $c가 0이 아니면, 즉 받침이 있으면
     } else {
        $result .= chr($_);
     }
  }
  $result;
}


sub LF2LFCR {
  # 펄은, 파이프로 입력받으면 DOS 텍스트로 출력하고, DOS 파일로 입력받으면 UNIX 텍스트로 출력하는 등
  # 혼란스러운 면이 있는데, 이때 입력과 상관없이 무조건 DOS 텍스트로 출력케 하기 위한 함수
  $_[0] =~ s/\n/\r\n/g if ($_[0] !~ /\r\n/); # 도스 텍스트가 아니면
}



자소 분리할 글(가령 소설 같은 것)을 텍스트 파일로 만들어 0.txt 등의 이름을 붙여 주자. 여기서는, 이상(李箱)의 소설 '날개' 중의 일부를 사용했다:


이 18가구를 대표하는 대문이라는 것이 일각이 져서 외따로 떨어지기는 했으나 있다. 그러나 그것은 한 번도 닫힌 일이 없는 한길이나 마찬가지 대문인 것이다. 온갖 장사아치들은 하루 가운데 어느 시간에라도 이 대문을 통하여 드나들 수가 있는 것이다. 이네들은 문간에서 두부를 사는 것이 아니라 미닫이만 열고 방에서 두부를 사는 것이다. 이렇게 생긴 33번지 대문에 그들 18가구의 문패를 몰아다 붙이는 것은 의미가 없다. 그들은 어느 사이엔가 각 미닫이 위 백인당(百忍堂)이니 길상당(吉祥堂)이니 써붙인 한곁에다 문패를 붙이는 풍속을 가져버렸다.


그런 후 윈도의 명령 프롬프트에서
jaso_bunri.pl 0.txt
라고 치고 엔터키를 누른다. (펄은 컴파일할 필요 없고 그냥 실행시키면 된다. 리눅스/맥 등에서도 별다른 수정없이 이 펄 프로그램이 작동할 것이다)

위의, 이상의 소설 '날개'를 자소 분리한 결과가 아래다:


ㅇㅣ 18ㄱㅏㄱㅜㄹㅡㄹ ㄷㅐㅍㅛㅎㅏㄴㅡㄴ ㄷㅐㅁㅜㄴㅇㅣㄹㅏㄴㅡㄴ ㄱㅓㅅㅇㅣ ㅇㅣㄹㄱㅏㄱㅇㅣ ㅈㅕㅅㅓ ㅇㅚㄸㅏㄹㅗ ㄸㅓㄹㅇㅓㅈㅣㄱㅣㄴㅡㄴ ㅎㅐㅆㅇㅡㄴㅏ ㅇㅣㅆㄷㅏ. ㄱㅡㄹㅓㄴㅏ ㄱㅡㄱㅓㅅㅇㅡㄴ ㅎㅏㄴ ㅂㅓㄴㄷㅗ ㄷㅏㄷㅎㅣㄴ ㅇㅣㄹㅇㅣ ㅇㅓㅄㄴㅡㄴ ㅎㅏㄴㄱㅣㄹㅇㅣㄴㅏ ㅁㅏㅊㅏㄴㄱㅏㅈㅣ ㄷㅐㅁㅜㄴㅇㅣㄴ ㄱㅓㅅㅇㅣㄷㅏ. ㅇㅗㄴㄱㅏㅈ ㅈㅏㅇㅅㅏㅇㅏㅊㅣㄷㅡㄹㅇㅡㄴ ㅎㅏㄹㅜ ㄱㅏㅇㅜㄴㄷㅔ ㅇㅓㄴㅡ ㅅㅣㄱㅏㄴㅇㅔㄹㅏㄷㅗ ㅇㅣ ㄷㅐㅁㅜㄴㅇㅡㄹ ㅌㅗㅇㅎㅏㅇㅕ ㄷㅡㄴㅏㄷㅡㄹ ㅅㅜㄱㅏ ㅇㅣㅆㄴㅡㄴ ㄱㅓㅅㅇㅣㄷㅏ. ㅇㅣㄴㅔㄷㅡㄹㅇㅡㄴ ㅁㅜㄴㄱㅏㄴㅇㅔㅅㅓ ㄷㅜㅂㅜㄹㅡㄹ ㅅㅏㄴㅡㄴ ㄱㅓㅅㅇㅣ ㅇㅏㄴㅣㄹㅏ ㅁㅣㄷㅏㄷㅇㅣㅁㅏㄴ ㅇㅕㄹㄱㅗ ㅂㅏㅇㅇㅔㅅㅓ ㄷㅜㅂㅜㄹㅡㄹ ㅅㅏㄴㅡㄴ ㄱㅓㅅㅇㅣㄷㅏ. ㅇㅣㄹㅓㅎㄱㅔ ㅅㅐㅇㄱㅣㄴ 33ㅂㅓㄴㅈㅣ ㄷㅐㅁㅜㄴㅇㅔ ㄱㅡㄷㅡㄹ 18ㄱㅏㄱㅜㅇㅢ ㅁㅜㄴㅍㅐㄹㅡㄹ ㅁㅗㄹㅇㅏㄷㅏ ㅂㅜㅌㅇㅣㄴㅡㄴ ㄱㅓㅅㅇㅡㄴ ㅇㅢㅁㅣㄱㅏ ㅇㅓㅄㄷㅏ. ㄱㅡㄷㅡㄹㅇㅡㄴ ㅇㅓㄴㅡ ㅅㅏㅇㅣㅇㅔㄴㄱㅏ ㄱㅏㄱ ㅁㅣㄷㅏㄷㅇㅣ ㅇㅟ ㅂㅐㄱㅇㅣㄴㄷㅏㅇ(百忍堂)ㅇㅣㄴㅣ ㄱㅣㄹㅅㅏㅇㄷㅏㅇ(吉祥堂)ㅇㅣㄴㅣ ㅆㅓㅂㅜㅌㅇㅣㄴ ㅎㅏㄴㄱㅕㅌㅇㅔㄷㅏ ㅁㅜㄴㅍㅐㄹㅡㄹ ㅂㅜㅌㅇㅣㄴㅡㄴ ㅍㅜㅇㅅㅗㄱㅇㅡㄹ ㄱㅏㅈㅕㅂㅓㄹㅕㅆㄷㅏ.





아래는 윤동주 시인의 서시를 자소 분리한 것이다:


ㅅㅓㅅㅣ (序詩)


ㅈㅜㄱㄴㅡㄴ ㄴㅏㄹㄲㅏㅈㅣ ㅎㅏㄴㅡㄹㅇㅡㄹ ㅇㅜㄹㅓㄹㅓ

ㅎㅏㄴ ㅈㅓㅁ ㅂㅜㄲㅡㄹㅓㅁ ㅇㅓㅄㄱㅣㄹㅡㄹ,

ㅇㅣㅍㅅㅐㅇㅔ ㅇㅣㄴㅡㄴ ㅂㅏㄹㅏㅁㅇㅔㄷㅗ

ㄴㅏㄴㅡㄴ ㄱㅚㄹㅗㅇㅝㅎㅐㅆㄷㅏ.

ㅂㅕㄹㅇㅡㄹ ㄴㅗㄹㅐㅎㅏㄴㅡㄴ ㅁㅏㅇㅡㅁㅇㅡㄹㅗ

ㅁㅗㄷㅡㄴ ㅈㅜㄱㅇㅓㄱㅏㄴㅡㄴ ㄱㅓㅅㅇㅡㄹ ㅅㅏㄹㅏㅇㅎㅐㅇㅑㅈㅣ

ㄱㅡㄹㅣㄱㅗ ㄴㅏㅎㅏㄴㅌㅔ ㅈㅜㅇㅓㅈㅣㄴ ㄱㅣㄹㅇㅡㄹ ㄱㅓㄹㅇㅓ ㄱㅏㅇㅑㄱㅔㅆㄷㅏ.

ㅇㅗㄴㅡㄹ ㅂㅏㅁㅇㅔㄷㅗ ㅂㅕㄹㅇㅣ ㅂㅏㄹㅏㅁㅇㅔ ㅅㅡㅊㅣㅇㅜㄴㄷㅏ.





분리한 자소를 다시 조합?

자소 분리한 문자열을 다시 원래대로 합쳐주는 함수는 만들지 못했다. 다시 합치기 위해서는 이렇게 한 줄로 쭉 풀어주는 것이 아니라, 자소들을 음절 단위로 배열 속에 보관해 두어야 할 것이다.

CPAN(세계적인 펄 코드 창고)에서 "hangul"로 검색해 보면, 일본인이 만든 한글 처리 모듈이 하나 나온다. (한국의 대학에서는 자신들이 개발한 한글 처리 프로그램을 내부인끼리만 사용하지 외부에 공개하지 않았다. 그래서 한글 처리 프로그램을 만들기 위해 외국인의 도움을 받아야 할 때가 있었다.)

SADAHIRO Tomoyuki > Lingua-KO-Hangul-Util-0.22 > Lingua::KO::Hangul::Util

이 모듈을 사용하면 한글 자소 분리는 물론이고 다시 조합하는 것도 가능할 것이다.



관련 포스트: [Perl/펄] 윈도용 ActivePerl 설치법





추가 사항:
"jaso_bunri.pl 0.txt" 이렇게 하면 콘솔 화면 상으로만 출력된다. 자소 분리된 출력을 파일로 만들기 위해서는 리다이렉션을 사용한다. 즉 아래와 같이 하면 된다.

jaso_bunri.pl 0.txt > tt.txt

이러면 tt.txt (아무 이름이나 상관 없지만 기존의 파일을 덮어쓰게 되므로 유의) 라는 텍스트 파일 안에 출력 결과가 들어 가게 된다.








추가 사항:
만약 그냥 jaso_bunri.pl 이라고 하면 커서만 깜빡이고 아무 반응이 없을 것이다. 그러나 그 상태에서도 펄이 입력을 기다리고 있으므로, 키보드로 직접 한글을 입력하거나, 한글 텍스트를 클립보드로 복사한 후 콘솔창에 대고 마우스 오른쪽 버튼을 클릭하여 붙여 주거나 한 후, Ctrl+Z 키를 눌러 주면, 자소가 분리된다.

Microsoft Windows 2000 [Version 5.00.2195]
(C) Copyright 1985-2000 Microsoft Corp.

D:\Z>jaso_bunri.pl

완성
^Z

ㅇㅘㄴㅅㅓㅇ

D:\Z>



파이프(pipe) 입력도 가능하다. 한글 원화 기호(\) 위에 있는, 즉 '백슬래시 키' 위에 있는 작대기(|)로 파이핑을 할 수 있다.
아래는 dir 명령을 자소 분리한 것. (단, 펄의 파이핑에 조금 문제가 있는데, "perl -S" 이것을 펄 파일 앞에 붙어 주어야 한다.)

dir | perl -S jaso_bunri.pl
이 명령의 결과가 아래의 창:

D ㄷㅡㄹㅏㅇㅣㅂㅡㅇㅢ ㅂㅗㄹㄹㅠㅁ: Foo
ㅂㅗㄹㄹㅠㅁ ㅇㅣㄹㄹㅕㄴ ㅂㅓㄴㅎㅗ: 2873-D621

D:\Z ㄷㅣㄹㅔㄱㅌㅓㄹㅣ

2006-03-10  01:49p      <DIR>          .
2006-03-10  01:49p      <DIR>          ..
              0ㄱㅐ ㅍㅏㅇㅣㄹ               0 ㅂㅏㅇㅣㅌㅡ
              2 ㄷㅣㄹㅔㄱㅌㅓㄹㅣ  44,346,966,016 ㅂㅏㅇㅣㅌㅡ ㄴㅏㅁㅇㅡㅁ



참고로, 위의 dir의 원래 출력은 이것
D:\Z>dir
D 드라이브의 볼륨: Foo
볼륨 일련 번호: 2873-D621

D:\Z 디렉터리

2006-03-10  01:49p      <DIR>          .
2006-03-10  01:49p      <DIR>          ..
              0개 파일               0 바이트
              2 디렉터리  44,346,966,016 바이트 남음

D:\Z>

8 Comments:
At 4:27 PM, Anonymous Anonymous said...

트랙백을 남기려고 했는데 트랙백 주소를 못찾겠네요...

http://blog.superwtk.com/archives/353

 
At 10:26 AM, Blogger mwultong said...

죄송합니다. 제가 지금 블로그를 하고 있는 이 blogger.com 에는 트랙백 기능이 없습니다..

(∩_∩;)

 
At 1:24 PM, Anonymous Anonymous said...

음절을 다루는 클래스를 작성했습니다 :-D

http://blog.superwtk.com/archives/375

 
At 2:47 PM, Blogger mwultong said...

자바로 "한글 음절(Syllable) 다루기" 소스를 올려주셨네요.

(∩_∩)

 
At 11:39 AM, Blogger yyazzi said...

흥미롭네요

 
At 11:50 AM, Blogger mwultong said...

반갑습니다^_^

 
At 12:43 AM, Blogger anowage said...

합치는 것은 : https://github.com/Finfra/jaso_hap

 
At 11:02 AM, Blogger mwultong said...

좋은 정보 감사합니다^_^

 

Post a Comment

<< Home RSS 2.0 feed

구글 Google 에서 제공하는 무료 블로그 서비스인 블로거 Blogger 의 인터넷 주소는 www.blogger.com 입니다. Blogger 에 블로그를 만들면, blogspot.com 이라는 주소에 블로그가 생성됩니다.
블로그를 직접 방문하지 않고도 최신 게시물을 구독하려면 RSS 2.0 feed 주소를 리더기에 등록하시면 됩니다.
Categories
Previous Posts
Monthly Archives
Top