2025년 8월 10일 일요일

C# 한글조합기..

한글조합기를 AI를 통해서 만들었다..

가상키보드를 만든다던가.. 뭐.. 이런..

여튼 잘쓰셈.. ㅋㅋ



public class HangulComposer
{
    // 기본 테이블
    static readonly char[] CHO = new[] { 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' };
    static readonly char[] JUNG = new[] { 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ' };
    static readonly string[] JONG = new[] { "", "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ", "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ" };

    // 겹받침 맵
    static readonly Dictionary<string, string> FinalComposite = new()
    {
        {"ㄱㅅ","ㄳ"}, {"ㄴㅈ","ㄵ"}, {"ㄴㅎ","ㄶ"},
        {"ㄹㄱ","ㄺ"}, {"ㄹㅁ","ㄻ"}, {"ㄹㅂ","ㄼ"},
        {"ㄹㅅ","ㄽ"}, {"ㄹㅌ","ㄾ"}, {"ㄹㅍ","ㄿ"},
        {"ㄹㅎ","ㅀ"}, {"ㅂㅅ","ㅄ"}
    };

    // 겹모음 맵
    static readonly Dictionary<string, char> MedialComposite = new()
    {
        {"ㅗㅏ",'ㅘ'}, {"ㅗㅐ",'ㅙ'}, {"ㅗㅣ",'ㅚ'},
        {"ㅜㅓ",'ㅝ'}, {"ㅜㅔ",'ㅞ'}, {"ㅜㅣ",'ㅟ'},
        {"ㅡㅣ",'ㅢ'}
    };

    // 이중초성(선택)
    static readonly Dictionary<string, char> InitialComposite = new()
    {
        {"ㄱㄱ",'ㄲ'}, {"ㄷㄷ",'ㄸ'}, {"ㅂㅂ",'ㅃ'}, {"ㅅㅅ",'ㅆ'}, {"ㅈㅈ",'ㅉ'}
    };

    // 내부 버퍼
    private List<char> jamoBuffer = new List<char>();
    private StringBuilder committed = new StringBuilder();
    private string preedit = "";

    // 입력
    public void Input(char c)
    {
        if (IsJamo(c))
        {
            jamoBuffer.Add(c);
            RecomposeAndMaybeCommit();
        }
        else
        {
            CommitAll();
            committed.Append(c);
            preedit = "";
        }
    }

    // 백스페이스
    public void Backspace()
    {
        if (jamoBuffer.Count > 0)
        {
            jamoBuffer.RemoveAt(jamoBuffer.Count - 1);
            RecomposeAndMaybeCommit();
            return;
        }

        if (committed.Length > 0)
            committed.Remove(committed.Length - 1, 1);
    }

    // 현재까지 확정된 문자열
    public string Committed => committed.ToString();

    // 현재 미완성(조합 중) 문자열
    public string Preedit => preedit;

    // 확정 문자열 + 조합중 문자열
    public String GuessWord()
    {
        return Committed + Preedit;
    }

    //클리어
    public void Clear()
    {
        jamoBuffer.Clear();
        committed.Clear();
        preedit = "";
    }

    // 강제 커밋
    public void CommitAll()
    {
        var parsed = ParseJamos(jamoBuffer);
        foreach (var p in parsed)
            committed.Append(p.composed);
        jamoBuffer.Clear();
        preedit = "";
    }

    // --- 내부 로직 ---
    private void RecomposeAndMaybeCommit()
    {
        var parsed = ParseJamos(jamoBuffer);
        if (parsed.Count == 0)
        {
            preedit = "";
            return;
        }

        // 마지막 음절 제외하고 모두 확정
        int stableCount = Math.Max(0, parsed.Count - 1);
        int removeJamoCount = 0;
        for (int i = 0; i < stableCount; i++)
        {
            committed.Append(parsed[i].composed);
            removeJamoCount += parsed[i].consumed;
        }

        if (removeJamoCount > 0)
            jamoBuffer.RemoveRange(0, removeJamoCount);

        // 마지막 항목은 미완성 preedit 으로 유지
        preedit = parsed.Last().composed;
    }

    private List<(string composed, int consumed)> ParseJamos(List<char> arr)
    {
        var res = new List<(string composed, int consumed)>();
        int i = 0;
        while (i < arr.Count)
        {
            char cur = arr[i];

            // 모음 시작 처리 (수정: 모음 단독일 경우 초성 없이 모음만 표시)
            if (IsVowel(cur))
            {
                int consumed = 1;
                char medial = cur;

                // 겹모음 체크
                if (i + 1 < arr.Count && IsVowel(arr[i + 1]))
                {
                    string mk = $"{cur}{arr[i + 1]}";
                    if (MedialComposite.ContainsKey(mk))
                    {
                        medial = MedialComposite[mk];
                        consumed = 2;
                    }
                }

                // 종성 처리
                string finalStr = "";

                // 모음 단독인 경우 (뒤에 자음 없거나, 모음 다음에 자음이 초성일 가능성 있는 경우)
                // 여기서는 “모음만 단독” 표시 위해 초성 없이 모음 문자 그대로 출력
                // (예: ㅜ, ㅏ 같은 단독 모음)
                if (consumed == arr.Count - i)
                {
                    res.Add((new string(medial, 1), consumed));
                    i += consumed;
                    continue;
                }

                // 기존 종성 판단 유지
                if (i + consumed < arr.Count && IsConsonant(arr[i + consumed]))
                {
                    if (i + consumed + 1 < arr.Count && IsConsonant(arr[i + consumed + 1]))
                    {
                        string pk = $"{arr[i + consumed]}{arr[i + consumed + 1]}";
                        if (FinalComposite.ContainsKey(pk))
                        {
                            if (i + consumed + 2 < arr.Count && IsVowel(arr[i + consumed + 2]))
                            {
                                finalStr = arr[i + consumed].ToString();
                                consumed += 1;
                            }
                            else
                            {
                                finalStr = FinalComposite[pk];
                                consumed += 2;
                            }
                        }
                        else
                        {
                            if (i + consumed + 2 < arr.Count && IsVowel(arr[i + consumed + 2]))
                            {
                                // 다음 초성으로 넘어감
                            }
                            else
                            {
                                finalStr = arr[i + consumed].ToString();
                                consumed += 1;
                            }
                        }
                    }
                    else
                    {
                        if (i + consumed + 1 < arr.Count && IsVowel(arr[i + consumed + 1]))
                        {
                            // 다음 초성으로 넘어감
                        }
                        else
                        {
                            finalStr = arr[i + consumed].ToString();
                            consumed += 1;
                        }
                    }
                }

                // 초성 없이 조합
                if (finalStr == "")
                {
                    res.Add((new string(medial, 1), consumed));
                }
                else
                {
                    res.Add((Combine('ㅇ', medial, finalStr), consumed));
                }
                i += consumed;
                continue;
            }

            // 자음 시작 처리
            if (IsConsonant(cur))
            {
                if (i + 1 < arr.Count && IsVowel(arr[i + 1]))
                {
                    char onset = cur;
                    int usedOnset = 1;

                    if (i + 1 < arr.Count && IsConsonant(arr[i + 1]) && i + 2 < arr.Count && IsVowel(arr[i + 2]))
                    {
                        string ik = $"{arr[i]}{arr[i + 1]}";
                        if (InitialComposite.ContainsKey(ik))
                        {
                            onset = InitialComposite[ik];
                            usedOnset = 2;
                        }
                    }

                    char medial = arr[i + usedOnset];
                    int consumed = usedOnset + 1;

                    if (i + consumed < arr.Count && IsVowel(arr[i + consumed]))
                    {
                        string mk = $"{medial}{arr[i + consumed]}";
                        if (MedialComposite.ContainsKey(mk))
                        {
                            medial = MedialComposite[mk];
                            consumed += 1;
                        }
                    }

                    string finalStr = "";
                    if (i + consumed < arr.Count && IsConsonant(arr[i + consumed]))
                    {
                        if (i + consumed + 1 < arr.Count && IsConsonant(arr[i + consumed + 1]))
                        {
                            string pk = $"{arr[i + consumed]}{arr[i + consumed + 1]}";
                            if (FinalComposite.ContainsKey(pk))
                            {
                                if (i + consumed + 2 < arr.Count && IsVowel(arr[i + consumed + 2]))
                                {
                                    finalStr = arr[i + consumed].ToString();
                                    consumed += 1;
                                }
                                else
                                {
                                    finalStr = FinalComposite[pk];
                                    consumed += 2;
                                }
                            }
                            else
                            {
                                if (i + consumed + 2 < arr.Count && IsVowel(arr[i + consumed + 2]))
                                {
                                    // 다음 초성으로 넘어감
                                }
                                else
                                {
                                    finalStr = arr[i + consumed].ToString();
                                    consumed += 1;
                                }
                            }
                        }
                        else
                        {
                            if (i + consumed + 1 < arr.Count && IsVowel(arr[i + consumed + 1]))
                            {
                                // 다음 초성으로 넘어감
                            }
                            else
                            {
                                finalStr = arr[i + consumed].ToString();
                                consumed += 1;
                            }
                        }
                    }

                    res.Add((Combine(onset, medial, finalStr), consumed));
                    i += consumed;
                    continue;
                }

                // 자음 단독
                res.Add((cur.ToString(), 1));
                i += 1;
                continue;
            }

            // 그 외 문자 (에러 방지용)
            res.Add((cur.ToString(), 1));
            i += 1;
        }

        return res;
    }

    private string Combine(char cho, char jung, string jong)
    {
        int choIndex = Array.IndexOf(CHO, cho);
        if (choIndex < 0) choIndex = Array.IndexOf(CHO, 'ㅇ');
        int jungIndex = Array.IndexOf(JUNG, jung);
        if (jungIndex < 0) jungIndex = 0;
        int jongIndex = Array.IndexOf(JONG, jong);
        if (jongIndex < 0) jongIndex = 0;

        int code = 0xAC00 + (choIndex * 21 + jungIndex) * 28 + jongIndex;
        return ((char)code).ToString();
    }

    private bool IsVowel(char c) => Array.IndexOf(JUNG, c) >= 0 || MedialComposite.Values.Contains(c);
    private bool IsConsonant(char c) => Array.IndexOf(CHO, c) >= 0 || InitialComposite.Values.Contains(c);
    private bool IsJamo(char c) => IsVowel(c) || IsConsonant(c);

}




활용 예시..

HangulComposer hc = new HangulComposer();
hc.Input('ㅈ');
Console.WriteLine(hc.GuessWord());
hc.Input('ㅗ');
Console.WriteLine(hc.GuessWord());
hc.Input('ㅈ');
Console.WriteLine(hc.GuessWord());
hc.Input('ㄲ');
Console.WriteLine(hc.GuessWord());
hc.Input('ㅏ');
Console.WriteLine(hc.GuessWord());
hc.Input(' ');
Console.WriteLine(hc.GuessWord());
hc.Input('ㅅ');
Console.WriteLine(hc.GuessWord());
hc.Input('ㅡ');
Console.WriteLine(hc.GuessWord());
hc.Input('B');
Console.WriteLine(hc.GuessWord());
hc.Input('u');
Console.WriteLine(hc.GuessWord());
hc.Input('l');
Console.WriteLine(hc.GuessWord());

//
string strr = hc.GuessWord();

댓글 없음:

댓글 쓰기