[PHP] 한국어 조사 처리 함수

Updated: 코드에 버그가 있어 2017년 12월 14일에 수정했습니다.

트위터에서 녹풍님의 [PHP] 명사 뒤에 나오는 ‘은는이가을를’ 판단할 수 있게 해 주는 함수를 봤는데, 조사가 없는 글자를 판단하기 위해 죄다 문자열에 넣어두는 식으로 구현했더군요. 그런데 유니코드는 완성형과 달리 규칙적인 문자 체계라서 코드만으로도 조사의 여부를 판단할 수 있기 때문에 굳이 이런 식으로 번거롭게 작성하지 않아도 됩니다.

그래서 대략 10년 전쯤에 작성했던  한국어 조사를 판단해주는 함수의 자바스크립트로 코드를 가져와서 PHP로 만들어보았습니다. 또한 아마도 메시지 템플릿을 처리할 때 편하게 사용할 수 있도록 "{은}"과 같이 중괄호를 조사 양쪽에 씌워두면 해당 부분을 자동으로 판단해서 치환해주도록 바꿨습니다. 편의를 위해 “은” 또는 “는”, “이” 또는 “가”, “을” 또는 “를” 중 무엇을 사용해도 상관없도록 했습니다.

클로저를 사용했기 때문에 PHP 5.3 이상이 필요하지만, preg_replace_callback에 사용된 콜백 함수를 따로 빼면 조금 더 하위 버전에서도 사용할 수 있습니다. 파일의 인코딩 및 mb_* 계열 함수 내부 인코딩은 UTF-8으로 설정되어 있다고 가정했습니다.

function josa($str) {
    $josa = '이가은는을를과와';

    return preg_replace_callback(
        "/(.)\\{([{$josa}])\\}/u",
        function($matches) use($josa) {
            list($_, $last, $pp) = $matches;

            $pp1 = $pp2 = $pp;
            $idx = mb_strpos($josa, $pp);
            ($idx % 2) ? ($pp1 = mb_substr($josa,--$idx,1)) : ($pp2 = mb_substr($josa,++$idx,1));

            if (strlen($last) > 1) {
                $last_ucs2 = mb_convert_encoding($last, 'UCS-2BE', 'UTF-8');
                $code = (hexdec(bin2hex($last_ucs2)) - 16) % 28;
            } else {
                $code = (strpos('2459', $last) > -1) ? 0 : 1;
            }

            return $last.($code ? $pp1 : $pp2);
        },
        $str
    );
}

// 사용예
echo josa('한글{와} 컴퓨터의 한글2015{은} 언제 출시될까.'), "\n";
echo josa('영등포{은} 문제없습니다.'), "\n";

데모 링크에서 실제 동작하는 예제를 볼 수 있습니다.

  1. 안녕하세요. 오래된 글이라 답변을 받을수 있을지는 모르겠지만 남겨 봅니다.
    좋은 코드 감사합니다. 엄청 필요한 코드였어요.
    근데 ㄷ, ㅍ 등 일부 처리가 되지 않는 케이스가 있는데 왜인지 알수가 없어서요.
    예를들면 josa(‘영등포{은}’) 이라고 실행시 영등포는을 기대 했는데 영등포은 을 그대로 출력 하더라고요.
    혹시나 하는마음에댓글 남겨 봅니다.
    저만 그런가요?ㅜㅜ

    1. 안녕하세요. 댓글을 보고 확인해보니 코드에 버그가 있었습니다. ㅠㅠ UTF-8 한글 코드의 배열이 제 생각하고 달랐는데 그걸 간과하고 코드를 작성했던 게 문제였습니다. 지금은 수정해두었고 코드 아래 데모 링크도 추가해두었으니 확인해보세요~

댓글을 남겨주세요