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

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

Updated: "로"와 "으로"도 지원하도록 수정했습니다. (2021. 01. 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));
      $pp1 .= ($pp1 === '으') ? $pp2 : '';

      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";
echo josa('집{로} 가는 길'), "\n";

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

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

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

  2. 안녕하세요. 정말 유용하게 잘 사용하고 있습니다. 좋은 정보 감사합니다.
    사용하다보니 한가지 추가되었으면 좋겠다 싶은 것이 있어서요.
    '을를이가'... 같은 주격 조사와 목적격 조사는 처리가 완벽합니다만,
    혹시 '로' '으로' 같은 것도 처리가 가능할까요?
    이 댓글을 보실지는 모르겠지만 혹시나 싶어서 글 남겨봅니다.
    감사합니다.

    1. 오랫동안 잊어먹고 있다가 오늘 우연히 이 글을 발견해서 요청에 따라 코드를 업데이트 해보았습니다. 혹시 다른 분이라도 필요하신 분이 있다면 유용하게 사용하셨으면 합니다.

Leave a Reply to taggonCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.