12.2.4 MorphemeParser

형태소 분석기는 웹 검색 엔진 만들기에서 만든 것과 차이가 없습니다. 여기에서도 즐겨찾기에 등록한 RSS 피드의 항목을 탐색하면 페이지 내용을 형태소 분석기로 분석합니다.

형태소 분석기는 정적 클래스로 제공합시다.

public static class MorphemeParser
{
}

형태소 분석기에는 필터링할 문자열과 접미사를 선언합니다.

static string[] filters = {" ",",",".","?","!","\r","\n","\t","!","?",
                              "[","]","<",">","{","}","\"","'",
                              "a","b","c","d","e","f","g","h","i",
                              "j","k","l","m","n","o","p","q","r","s",
                              "t","u","v","w","x","y","z"};
static string[] pf_filters = {"는","은","을","를","가","에","게",
                                 "에게","입니다","합니다"};

컨텐츠를 입력 인자로 받아 형태소 목록을 반환하는 메서드를 제공합시다.

public static List<Morpheme> Parse(string source)

형태소 목록을 보관할 컬렉션을 생성합니다.

List<Morpheme> mlist = new List<Morpheme>();

입력받은 소스에서 필터링할 문자열을 기준으로 문자열을 분할합니다.

string[] elems = source.Split(filters,StringSplitOptions.RemoveEmptyEntries);

분할한 각 문자열을 분석하여 분석 결과를 형태소 항목 컬렉션을 반환합니다.

foreach (string elem in elems)
{
    ParseElem(elem, mlist);
}
return mlist;

분할한 각 문자열을 분석하는 메서드를 작성합시다.

private static void ParseElem(string elem, List<Morpheme> mlist)

필터링할 접미사로 등록한 각 접미사가 문자열에 포함하는지 파악하여 접미사를 필터링하는 작업을 수행합니다. 만약 접미사를 포함하고 있는 문자열이면 접미사를 뺀 부분 문자열로 형태소를 만들고 접미사를 포함하고 있지 않다면 문자열 자체로 형태소를 만듭니다. 여기에서는 접미사를 포함하는지 점검하는 부분과 접미사를 뺀 부분 문자열로 형태소를 만들거나 문자열 자체로 형태소를 만드는 부분은 별도의 메서드로 만들어 사용할게요.

foreach (string pf_filter in pf_filters)
{
    if (CheckPostfix(pf_filter, elem))
    {
        MakeElem(pf_filter, elem, mlist);
        return;
    }
}
RecordElem(elem, mlist);

필터링할 접미사가 포함하고 있는지 확인하는 메서드를 작성합시다.

private static bool CheckPostfix(string pf_filter, string elem)

먼저 접미사를 포함하는지 확인합니다. 그리고 포함하고 있다면 원본 문자열 길이에서 접미사 문자열의 길이를 빼어 뒷 부분의 문자열을 얻어옵니다. 그리고 얻어온 문자열과 접미사 문자열이 같으면 참을 반환합니다. 그렇지 않으면 거짓을 반환합니다.

if (elem.Contains(pf_filter))
{
    int pos = elem.Length - pf_filter.Length;
    string sub = elem.Substring(pos);
    if (sub == pf_filter)
    {
        return true;
    }
}
return false;

접미사를 뺀 문자열로 형태소를 만드는 메서드를 구현합시다.

private static void MakeElem(string pf_filter, string elem, List<Morpheme> mlist)

먼저 접미사를 뺀 문자열을 만듭니다. 그리고 형태소를 만들어 컬렉션에 추가하는 메서드를 호출합니다.

int pos = elem.Length - pf_filter.Length;
string sub = elem.Substring(0, pos);
RecordElem(sub, mlist);

형태소를 만들어 컬렉션에 추가하는 메서드를 구현합시다.

private static void RecordElem(string elem, List<Morpheme> mlist)

먼저 형태소 목록의 각 형태소의 이름과 추가할 이름이 같은 것이 있으면 형태소의 개수속성을 1 증가합니다. 같은 것이 없다면 형태소 개체를 생성하여 형태소 컬렉션에 추가합니다.

foreach (Morpheme mo in mlist)
{
    if (mo.Name == elem)
    {
        mo.Count++;
        return;
    }
}
Morpheme mor = new Morpheme(elem, 1);
mlist.Add(mor);

▷ MorphemeParser.cs

using System;
using System.Collections.Generic;
 
namespace RSSBrowserLib
{
    /// <summary>
    /// 형태소 분석기 - 정적 클래스
    /// </summary>
    public static class MorphemeParser
    {
        static string[] filters = {" ",",",".","?","!","\r","\n","\t","!","?", "[","]","<",">","{",
                                      "}","\"","'", "a","b","c","d","e","f","g","h", "i", "j","k","l","m",
                                      "n","o","p","q","r","s", "t","u","v","w","x","y","z"};
       static string[] pf_filters={"는","은","을","를","가","에","게","에게","입니다","합니다"};
        /// <summary>
        /// 형태소 분석 정적 메서드
        /// </summary>
        /// <param name="source">원본</param>
        /// <returns>형태소 컬렉션</returns>
        public static List<Morpheme> Parse(string source)
        {
            List<Morpheme> mlist = new List<Morpheme>();
            string[] elems = source.Split(filters,
                StringSplitOptions.RemoveEmptyEntries); 
            foreach (string elem in elems)
            {
                ParseElem(elem, mlist);
            }
            return mlist;
        } 
        private static void ParseElem(string elem, List<Morpheme> mlist)
        {
            foreach (string pf_filter in pf_filters)
            {
                if (CheckPostfix(pf_filter, elem))
                {
                    MakeElem(pf_filter, elem, mlist);
                    return;
                }
            }

            RecordElem(elem, mlist);
        }
 

        private static void RecordElem(string elem, List<Morpheme> mlist)
        {
            foreach (Morpheme mo in mlist)
            {
                if (mo.Name == elem)
                {
                    mo.Count++;
                    return;
                }
            }

            Morpheme mor = new Morpheme(elem, 1);
            mlist.Add(mor);
        }
 

        private static void MakeElem(string pf_filter, string elem,List<Morpheme> mlist)
        {
            int pos = elem.Length - pf_filter.Length;
            string sub = elem.Substring(0, pos);
            RecordElem(sub, mlist);
        }
 
        private static bool CheckPostfix(string pf_filter, string elem)
        {
            if (elem.Contains(pf_filter))
            {
                int pos = elem.Length - pf_filter.Length;
                string sub = elem.Substring(pos);
                if (sub == pf_filter)
                {
                    return true;
                }
            }
            return false;
        }
    }
}