From 240e6c3b45a7cb8ec1384f0d2706f075dce53af8 Mon Sep 17 00:00:00 2001 From: chodadoo Date: Tue, 3 Aug 2021 19:32:27 +0900 Subject: [PATCH] =?UTF-8?q?1.=20=ED=83=80=EC=9D=B4=ED=95=91=20=EB=B6=84?= =?UTF-8?q?=ED=95=B4=20=EC=9E=91=EC=84=B1=20(C++=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20->=20C#=20=EC=BD=94=EB=93=9C)=202?= =?UTF-8?q?.=20=EC=82=AC=EC=9D=BC=EB=9F=B0=ED=8A=B8=20=EB=AA=A8=EB=93=9C?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=ED=83=AD=20=EC=A7=84=ED=96=89=20=EB=B6=88?= =?UTF-8?q?=EA=B0=80=20(=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=99=84=EB=A3=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NaverSearcher.sln | 14 +- NaverSearcher/Form1.cs | 120 ++-- NaverSearcher/HangulChar.cs | 4 +- NaverSearcher/HangulString.cs | 1022 +++++++++++++++++++++------------ NaverSearcher/Program.cs | 3 - 5 files changed, 770 insertions(+), 393 deletions(-) diff --git a/NaverSearcher.sln b/NaverSearcher.sln index e5239ce..331ff39 100644 --- a/NaverSearcher.sln +++ b/NaverSearcher.sln @@ -3,18 +3,30 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31424.327 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NaverSearcher", "NaverSearcher\NaverSearcher.csproj", "{54262938-66CB-4A5F-926B-48C3ACDCE9CF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NaverSearcher", "NaverSearcher\NaverSearcher.csproj", "{54262938-66CB-4A5F-926B-48C3ACDCE9CF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Debug|x64.ActiveCfg = Debug|Any CPU + {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Debug|x64.Build.0 = Debug|Any CPU + {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Debug|x86.ActiveCfg = Debug|Any CPU + {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Debug|x86.Build.0 = Debug|Any CPU {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Release|Any CPU.Build.0 = Release|Any CPU + {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Release|x64.ActiveCfg = Release|Any CPU + {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Release|x64.Build.0 = Release|Any CPU + {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Release|x86.ActiveCfg = Release|Any CPU + {54262938-66CB-4A5F-926B-48C3ACDCE9CF}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/NaverSearcher/Form1.cs b/NaverSearcher/Form1.cs index 0cc220f..ccdb06e 100644 --- a/NaverSearcher/Form1.cs +++ b/NaverSearcher/Form1.cs @@ -3,14 +3,8 @@ using OpenQA.Selenium.Chrome; using OpenQA.Selenium.Support.UI; using SeleniumExtras.WaitHelpers; using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Diagnostics; -using System.Drawing; +using System.Collections.ObjectModel; using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace NaverSearcher @@ -44,8 +38,8 @@ namespace NaverSearcher ChromeDriverService _ChromeDriverService = ChromeDriverService.CreateDefaultService(); _ChromeDriverService.HideCommandPromptWindow = true; - _ChromeDriver = new ChromeDriver(_ChromeDriverService, _ChromeOptions); - //_ChromeDriver = new ChromeDriver(); + //_ChromeDriver = new ChromeDriver(_ChromeDriverService, _ChromeOptions); + _ChromeDriver = new ChromeDriver(); //_ChromeOptions.add_argument("user-agent=Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko") @@ -54,11 +48,7 @@ namespace NaverSearcher // 검색어 1페어 - // 로그인 여부 확인 - // 로그아웃 실행 - // 네이버 메인 - // 스크롤 내린다. - // 20 ~ 60초 대기 + // 검색어 입력 (검색어 1) // 검색어 확인 @@ -152,9 +142,64 @@ namespace NaverSearcher _ChromeDriver.get('http://www.google.com/') */ - _ChromeDriver.Navigate().GoToUrl(@"https://naver.com"); WebDriverWait _WebDriverWait = new WebDriverWait(_ChromeDriver, TimeSpan.FromSeconds(3)); + var rand = new Random(); + string[][] key_word = + { + new string[] {"바나나우유 라떼", "초코우유 라떼" }, + new string[] { "민트초코바", "민트색" } + }; + + foreach (string[] key_pair in key_word) + { + // 로그인 여부 확인 + // 로그아웃 실행 + // 네이버 메인 + // 스크롤 내린다. + // 20 ~ 60초 대기 + + foreach (string key in key_pair) + { + // 검색어 입력 (검색어 1) + // 검색어 확인 + // 스크롤 내린다. + // 20 ~ 60초 대기 + // 아무 게시글 클릭 + // 이동된 페이지 작업 + // 스크롤 내리기 + // 20 ~ 60초 대기 + // 새창 닫기 + + _ChromeDriver.Navigate().GoToUrl(@"https://naver.com"); + + string _search_xpath = "//*[@id=\"query\"]"; + _WebDriverWait.Until(ExpectedConditions.ElementIsVisible(By.XPath(_search_xpath))); + + var _elment = _ChromeDriver.FindElement(By.XPath(_search_xpath)); + + foreach (var item in HangulString.CharacterMakeHistory(key)) + { + _elment.Clear(); + _elment.SendKeys(item); + Delay(rand.Next(500, 1000)); + } + + _elment.SendKeys(OpenQA.Selenium.Keys.Enter); + + ((IJavaScriptExecutor)_ChromeDriver).ExecuteScript("window.scrollBy(0, document.body.scrollHeight);"); + Delay(rand.Next(2000, 6000)); + + ReadOnlyCollection links = _ChromeDriver.FindElements(By.TagName("a")); + + links[rand.Next(0, links.Count)].Click(); + + ((IJavaScriptExecutor)_ChromeDriver).ExecuteScript("window.scrollBy(0, document.body.scrollHeight);"); + Delay(rand.Next(2000, 6000)); + } + } + + return; var elem = _ChromeDriver.FindElementByXPath("//*"); var source_code = elem.GetAttribute("outerHTML"); @@ -246,11 +291,11 @@ namespace NaverSearcher { } - string _search_xpath = "//*[@id=\"react-root\"]/section/nav/div[2]/div/div/div[2]/input"; + //_search_xpath = "//*[@id=\"react-root\"]/section/nav/div[2]/div/div/div[2]/input"; - _WebDriverWait.Until(ExpectedConditions.ElementIsVisible(By.XPath(_search_xpath))); + //_WebDriverWait.Until(ExpectedConditions.ElementIsVisible(By.XPath(_search_xpath))); - _ChromeDriver.FindElement(By.XPath(_search_xpath)).SendKeys(@"인플 검색"); + //_ChromeDriver.FindElement(By.XPath(_search_xpath)).SendKeys(@"인플 검색"); } catch (Exception ex) @@ -261,25 +306,36 @@ namespace NaverSearcher private void Form1_Load(object sender, EventArgs e) { - #region 인스턴스 생성 및 엑세스 - - string kknd = HangulString.SplitToPhonemes("ㅄ"); - - MessageBox.Show(kknd); - - char[] arr = kknd.ToCharArray(); - - foreach (var item in arr) - { - Trace.WriteLine(item); - } - - #endregion + } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { _ChromeDriver.Quit(); } + + private static DateTime Delay(int MS) + + { + + DateTime ThisMoment = DateTime.Now; + + TimeSpan duration = new TimeSpan(0, 0, 0, 0, MS); + + DateTime AfterWards = ThisMoment.Add(duration); + + while (AfterWards >= ThisMoment) + + { + + System.Windows.Forms.Application.DoEvents(); + + ThisMoment = DateTime.Now; + + } + + return DateTime.Now; + + } } } diff --git a/NaverSearcher/HangulChar.cs b/NaverSearcher/HangulChar.cs index 29e26e6..c5150e9 100644 --- a/NaverSearcher/HangulChar.cs +++ b/NaverSearcher/HangulChar.cs @@ -18,9 +18,9 @@ namespace NaverSearcher private static readonly char[] Onset = { 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' }; private static readonly char[] Nucleus = { 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', - 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ' }; + 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ' }; private static readonly char[] Coda = { (char)0x00, 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', - 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' }; + 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' }; // 문자 획수: 초성, 중성, 종성 private static readonly int[] OnsetStrokes = { 1, 2, 1, 2, 4, 3, 3, 4, 8, 2, 4, 1, 2, 4, 3, 2, 3, 4, 3 }; diff --git a/NaverSearcher/HangulString.cs b/NaverSearcher/HangulString.cs index 642d93c..f875644 100644 --- a/NaverSearcher/HangulString.cs +++ b/NaverSearcher/HangulString.cs @@ -5,386 +5,698 @@ using System.Text; namespace NaverSearcher { - /// - /// 한글 문자열 클래스: (한글을 포함한) 문자열에 대해 한글 처리에 관련된 다양한 속성과 메서드를 제공하는 클래스입니다. - /// - public class HangulString - { - /// - /// 문자열로부터 한글 문자열 클래스의 인스턴스를 생성합니다. - /// - /// 인스턴스를 생성할 문자열 - public HangulString(string aString) => CurrentString = aString; + /// + /// 한글 문자열 클래스: (한글을 포함한) 문자열에 대해 한글 처리에 관련된 다양한 속성과 메서드를 제공하는 클래스입니다. + /// + public class HangulString + { + private static readonly Dictionary Nucleus_Sub = new Dictionary() + { + {'ㅘ', "ㅗㅏ"}, {'ㅙ', "ㅗㅐ"}, {'ㅚ', "ㅗㅣ"}, {'ㅝ', "ㅜㅓ"}, {'ㅟ', "ㅜㅣ"}, {'ㅞ', "ㅜㅔ"} + }; - /// - /// 현재 인스턴스의 문자열입니다. - /// - public string CurrentString { get; private set; } + private static readonly Dictionary Coda_Sub = new Dictionary() + { + {'ㄳ', "ㄱㅅ"}, {'ㄵ', "ㄴㅈ"}, {'ㄶ', "ㄴㅎ"}, {'ㄺ', "ㄹㄱ"}, {'ㄻ', "ㄹㅁ"}, {'ㄼ', "ㄹㅂ"}, {'ㄽ', "ㄹㅅ"}, + {'ㄾ', "ㄹㅌ"}, {'ㄿ', "ㄹㅍ"}, {'ㅀ', "ㄹㅎ"}, {'ㅄ', "ㅂㅅ"} + }; - /// - /// 문자열을 HangulChar 클래스 인스턴스의 배열로 변환하여 반환합니다. - /// - /// - /// - /// 입력 인자가 null인 경우 - public static HangulChar[] ToHangulCharArray(string aString) - { - if (aString is null) - { - throw new ArgumentNullException(); - } - return aString.ToCharArray().Select(c => new HangulChar(c)).ToArray(); - } + ///< 초성 테이블 + private static readonly char[] wcHead = + { + 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', + 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', + 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', + 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', + 'ㅌ', 'ㅍ', 'ㅎ' + }; - /// - /// 문자열을 한글 문자열 부분과 나머지 문자열 부분으로 구분하여 반환합니다. - /// - /// - /// - /// 입력 인자가 null인 경우 - public static string[] SeparateString(string aString) - { - if (aString is null) - { - throw new ArgumentNullException(); - } + ///< 중성 테이블 + private static readonly char[] wcMid = + { + 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', + 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', + 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', + 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', + 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ' + }; - string[] result = new string[2]; - StringBuilder sbKorean = new StringBuilder(); - StringBuilder sbOthers = new StringBuilder(); - foreach (char c in aString) - { - HangulChar hc = new HangulChar(c); - if (hc.IsKoreanCharacter()) - { - sbKorean.Append(c); - } - else - { - sbOthers.Append(c); - } - } - result[0] = sbKorean.ToString(); - result[1] = sbOthers.ToString(); - return result; - } + ///< 중성 합성 테이블 + private static readonly char[,] wcMidMix = + { + {'ㅗ', 'ㅏ', 'ㅘ'}, + {'ㅗ', 'ㅐ', 'ㅙ'}, + { 'ㅗ', 'ㅑ', 'ㅚ'}, + { 'ㅜ', 'ㅓ', 'ㅝ'}, + { 'ㅜ', 'ㅔ', 'ㅞ'}, + { 'ㅜ', 'ㅣ', 'ㅟ'}, + { 'ㅡ', 'ㅣ', 'ㅢ'}, + }; - /// - /// 문자열이 모두 한글로만 이루어져 있는지의 여부를 반환합니다. - /// - /// - /// - public static bool IsAllHangul(string aString) - { - string checkPoint = HangulString.SeparateString(aString)[1]; - if (checkPoint.Length > 0) - { - return false; - } - else - { - return true; - } - } + ///< 종성 테이블 + private static readonly char[] wcTail = + { + ' ', 'ㄱ', 'ㄲ', 'ㄳ', + 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', + 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', + 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', + 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', + 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', + 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' + }; - /// - /// 문자열에 대해 한글 음절을 초성, 중성, 종성으로 분리하여 반환합니다. - /// - /// - /// - /// 이 null인 경우 - public static string SplitToPhonemes(string aString) - { - if (aString == null) - { - throw new ArgumentNullException(); - } + ///< 중성 합성 테이블 + private static readonly char[,] wcTailMix = + { + { 'ㄱ', 'ㅅ', 'ㄳ'}, + { 'ㄴ', 'ㅈ', 'ㄵ'}, + { 'ㄴ', 'ㅎ', 'ㄶ'}, + { 'ㄹ', 'ㄱ', 'ㄺ'}, + { 'ㄹ', 'ㅁ', 'ㄻ'}, + { 'ㄹ', 'ㅂ', 'ㄼ'}, + { 'ㄹ', 'ㅅ', 'ㄽ'}, + { 'ㄹ', 'ㅌ', 'ㄾ'}, + { 'ㄹ', 'ㅍ', 'ㄿ'}, + { 'ㄹ', 'ㅎ', 'ㅀ'}, + { 'ㅂ', 'ㅅ', 'ㅄ'}, + }; - StringBuilder sb = new StringBuilder(); - foreach (char c in aString) - { - HangulChar hc = new HangulChar(c); - // 한글 음절로 판명되어 분리가 가능한 경우 - if (hc.TrySplitSyllable(out char[] phonemes)) - { - foreach (char pc in phonemes) - { - // 초성-중성으로만 이루어져 있는 경우 종성은 반환 문자열에 포함하지 않음 - if (pc != (char)0x00) - { - sb.Append(pc); - } - } - } - // 한글 음절이 아닌 경우 - else - { - sb.Append(c); - } - } - return sb.ToString(); - } + /// + /// 문자열로부터 한글 문자열 클래스의 인스턴스를 생성합니다. + /// + /// 인스턴스를 생성할 문자열 + public HangulString(string aString) => CurrentString = aString; - /// - /// 문자열에 대해 한글 음절을 초성, 중성, 종성으로 분리하여 반환합니다. - /// - /// - /// - /// 이 null인 경우 - public static string SplitToPhonemes2(string aString) - { - if (aString == null) - { - throw new ArgumentNullException(); - } + /// + /// 현재 인스턴스의 문자열입니다. + /// + public string CurrentString { get; private set; } - StringBuilder sb = new StringBuilder(); - foreach (char c in aString) - { - HangulChar hc = new HangulChar(c); - // 한글 음절로 판명되어 분리가 가능한 경우 - if (hc.TrySplitSyllable(out char[] phonemes)) - { - foreach (char pc in phonemes) - { - // 초성-중성으로만 이루어져 있는 경우 종성은 반환 문자열에 포함하지 않음 - if (pc != (char)0x00) - { - sb.Append(pc); - } - } - } - // 한글 음절이 아닌 경우 - else - { - sb.Append(c); - } - } - - // 여기에서 한번더 수행한다. - StringBuilder sb2 = new StringBuilder(); - foreach (char c in sb.ToString()) - { - HangulChar hc = new HangulChar(c); - // 한글 음절로 판명되어 분리가 가능한 경우 - if (hc.TrySplitSyllable(out char[] phonemes)) - { - foreach (char pc in phonemes) - { - // 초성-중성으로만 이루어져 있는 경우 종성은 반환 문자열에 포함하지 않음 - if (pc != (char)0x00) - { - sb.Append(pc); - } - } - } - // 한글 음절이 아닌 경우 - else - { - sb.Append(c); - } - } + /// + /// 문자열을 HangulChar 클래스 인스턴스의 배열로 변환하여 반환합니다. + /// + /// + /// + /// 입력 인자가 null인 경우 + public static HangulChar[] ToHangulCharArray(string aString) + { + if (aString is null) + { + throw new ArgumentNullException(); + } + return aString.ToCharArray().Select(c => new HangulChar(c)).ToArray(); + } - return sb2.ToString(); - } + /// + /// 문자열을 한글 문자열 부분과 나머지 문자열 부분으로 구분하여 반환합니다. + /// + /// + /// + /// 입력 인자가 null인 경우 + public static string[] SeparateString(string aString) + { + if (aString is null) + { + throw new ArgumentNullException(); + } - /// - /// 문자열 내의 초성, 중성, 종성 음소를 합성하여 반환합니다. - /// 의도하지 않은 반환 결과를 피하기 위해 일반적으로 사용하는 방식의 - /// (완전한 한글 음절이 분리된) 자음/모음으로 구성된 문자열을 인수로 사용할 것을 권장합니다. - /// - /// - /// - /// 이 null인 경우 - public static string JoinPhonemes(string aString) - { - if (aString == null) { throw new ArgumentNullException(); } - StringBuilder sb = new StringBuilder(); - int curIdx = 0; - int lastIdx = aString.Length - 1; - // 마지막 인덱스 이후의 값 판단에 대한 예외를 피하기 위해 대상 문자열에 10개의 공백 임시 추가 - string newString = aString + new string(' ', 10); + string[] result = new string[2]; + StringBuilder sbKorean = new StringBuilder(); + StringBuilder sbOthers = new StringBuilder(); + foreach (char c in aString) + { + HangulChar hc = new HangulChar(c); + if (hc.IsKoreanCharacter()) + { + sbKorean.Append(c); + } + else + { + sbOthers.Append(c); + } + } + result[0] = sbKorean.ToString(); + result[1] = sbOthers.ToString(); + return result; + } - while (curIdx <= lastIdx) - { - // 현재 인덱스의 문자 - HangulChar hc = new HangulChar(newString[curIdx]); - // 현재 인덱스의 문자가 음소가 아닌 경우 현재 문자를 더하고 다음 인덱스로 이동 - if (!hc.IsPhoneme()) - { - sb.Append(hc.CurrentCharacter); - curIdx++; - continue; - } + /// + /// 문자열이 모두 한글로만 이루어져 있는지의 여부를 반환합니다. + /// + /// + /// + public static bool IsAllHangul(string aString) + { + string checkPoint = HangulString.SeparateString(aString)[1]; + if (checkPoint.Length > 0) + { + return false; + } + else + { + return true; + } + } - // 현재 인덱스의 문자가 음소인 경우에 대해 - // (1) 현재 인덱스로부터 2글자 합성이 가능하고 - // (2)-A 이후 첫 글자가 한글이 아니거나 - // (2)-B 이후 두 글자(초성-중성)가 합성이 가능한 경우 - // 현재 인덱스로부터 2글자를 합성하고, 인덱스를 2칸 전진 - bool isCur2SpanOk = HangulChar.TryJoinToSyllable( - new char[] { newString[curIdx], newString[curIdx + 1] }, - out char cur2SpanSyllable); - bool isCur2Next1Ok = !(new HangulChar(newString[curIdx + 2])).IsHangul(); - bool isCur2Next2Ok = HangulChar.TryJoinToSyllable( - new char[] { newString[curIdx + 2], newString[curIdx + 3] }, - out char _); - // (3) 현재 인덱스로부터 3글자 합성이 가능하고 - // (4)-A 이후 첫 글자가 한글이 아니거나 - // (4)-B 이후 두 글자(초성-중성)가 합성이 가능한 경우 - // 현재 인덱스로부터 3글자를 합성하고, 인덱스를 3칸 전진 - bool isCur3SpanOk = HangulChar.TryJoinToSyllable( - new char[] { newString[curIdx], newString[curIdx + 1], newString[curIdx + 2] }, - out char cur3SpanSyllable); - bool isCur3Next1Ok = !(new HangulChar(newString[curIdx + 3])).IsHangul(); - bool isCur3Next2Ok = HangulChar.TryJoinToSyllable( - new char[] { newString[curIdx + 3], newString[curIdx + 4] }, - out char _); + /// + /// 문자열에 대해 한글 음절을 초성, 중성, 종성으로 분리하여 반환합니다. + /// + /// + /// + /// 이 null인 경우 + public static string SplitToPhonemes(string aString) + { + if (aString == null) + { + throw new ArgumentNullException(); + } - // 상기 논리에 의한 판단부 - // 2글자 기준 판단 - if (isCur2SpanOk && (isCur2Next1Ok || isCur2Next2Ok)) - { - sb.Append(cur2SpanSyllable); - curIdx += 2; - } - // 3글자 기준 판단 - else if (isCur3SpanOk && (isCur3Next1Ok || isCur3Next2Ok)) - { - sb.Append(cur3SpanSyllable); - curIdx += 3; - } - else - { - sb.Append(hc.CurrentCharacter); - curIdx++; - } - } + StringBuilder sb = new StringBuilder(); + foreach (char c in aString) + { + HangulChar hc = new HangulChar(c); + // 한글 음절로 판명되어 분리가 가능한 경우 + if (hc.TrySplitSyllable(out char[] phonemes)) + { + foreach (char pc in phonemes) + { + // 초성-중성으로만 이루어져 있는 경우 종성은 반환 문자열에 포함하지 않음 + if (pc != (char)0x00) + { + sb.Append(pc); + } + } + } + // 한글 음절이 아닌 경우 + else + { + sb.Append(c); + } + } + return sb.ToString(); + } - return sb.ToString(); - } + /// + /// 문자열에 대해 한글 음절을 초성, 중성, 종성으로 분리하여 반환합니다. + /// + /// + /// + /// 이 null인 경우 + public static string SplitToPhonemes2(string aString) + { + if (aString == null) + { + throw new ArgumentNullException(); + } - /// - /// 문자열에 대해 초성 검색을 실시합니다. 반환값은 초성 검색에 대한 결과의 존재여부입니다. - /// 검색 문자열에 초성이 주어질 경우 초성 일치, 그렇지 않은 경우 문자 완전 일치 여부를 반환합니다. - /// - /// 검색할 문자열 - /// 대상 문자열 - /// (초성)일치가 발견된 대상 문자열 내 인덱스 배열 - /// - /// 또는 이 null인 경우 - public static bool GetOnsetMatches(string searchString, string targetString, out int[] indices) - { - if (searchString == null || targetString == null) - { - throw new ArgumentNullException(); - } + StringBuilder sb = new StringBuilder(); + foreach (char c in aString) + { + HangulChar hc = new HangulChar(c); + // 한글 음절로 판명되어 분리가 가능한 경우 + if (hc.TrySplitSyllable(out char[] phonemes)) + { + foreach (char pc in phonemes) + { + // 초성-중성으로만 이루어져 있는 경우 종성은 반환 문자열에 포함하지 않음 + if (pc != (char)0x00) + { + // 추가 분리 가능할때 + if (Nucleus_Sub.ContainsKey(pc)) + { + foreach (char item in Nucleus_Sub[pc].ToCharArray()) + { + sb.Append(item); + } + } + else if (Coda_Sub.ContainsKey(pc)) + { + foreach (char item in Coda_Sub[pc].ToCharArray()) + { + sb.Append(item); + } + } + else + { + sb.Append(pc); + } + } + } + } + // 한글 음절이 아닌 경우 + else + { + // 추가 분리 가능할때 + if (Nucleus_Sub.ContainsKey(c)) + { + foreach (char item in Nucleus_Sub[c].ToCharArray()) + { + sb.Append(item); + } + } + else if (Coda_Sub.ContainsKey(c)) + { + foreach (char item in Coda_Sub[c].ToCharArray()) + { + sb.Append(item); + } + } + else + { + sb.Append(c); + } + } + } - List idxs = new List(); + return sb.ToString(); + } - // 검색 문자열의 길이가 대상 문자열의 길이보다 긴 경우 - if (searchString.Length > targetString.Length) - { - indices = idxs.ToArray(); - return false; - } + /// + /// 문자열 내의 초성, 중성, 종성 음소를 합성하여 반환합니다. + /// 의도하지 않은 반환 결과를 피하기 위해 일반적으로 사용하는 방식의 + /// (완전한 한글 음절이 분리된) 자음/모음으로 구성된 문자열을 인수로 사용할 것을 권장합니다. + /// + /// + /// + /// 이 null인 경우 + public static string JoinPhonemes(string aString) + { + if (aString == null) { throw new ArgumentNullException(); } + StringBuilder sb = new StringBuilder(); + int curIdx = 0; + int lastIdx = aString.Length - 1; + // 마지막 인덱스 이후의 값 판단에 대한 예외를 피하기 위해 대상 문자열에 10개의 공백 임시 추가 + string newString = aString + new string(' ', 10); - // 대상 문자열 내의 인덱스를 증가시켜 가며 - for (int curIdx = 0; curIdx < targetString.Length - searchString.Length + 1; curIdx++) - { - // 해당 인덱스로부터 검색 문자열의 길이만큼 초성일치 검색 수행 - // 초성 일치가 될 경우 해당 인덱스 저장 - bool isMatch = true; - for (int chkIdx = curIdx; chkIdx < curIdx + searchString.Length; chkIdx++) - { - if (!HangulChar.IsOnsetMatch(searchString[chkIdx - curIdx], targetString[chkIdx])) - { - isMatch = false; - break; - } - } - if (isMatch) - { - idxs.Add(curIdx); - } - } - indices = idxs.ToArray(); - if (indices.Length > 0) - { - return true; - } - else - { - return false; - } - } + while (curIdx <= lastIdx) + { + // 현재 인덱스의 문자 + HangulChar hc = new HangulChar(newString[curIdx]); + // 현재 인덱스의 문자가 음소가 아닌 경우 현재 문자를 더하고 다음 인덱스로 이동 + if (!hc.IsPhoneme()) + { + sb.Append(hc.CurrentCharacter); + curIdx++; + continue; + } - /// - /// 현재 인스턴스의 문자열을 HangulChar 클래스 인스턴스의 배열로 변환하여 반환합니다. - /// - /// - public HangulChar[] ToHangulCharArray() => HangulString.ToHangulCharArray(CurrentString); + // 현재 인덱스의 문자가 음소인 경우에 대해 + // (1) 현재 인덱스로부터 2글자 합성이 가능하고 + // (2)-A 이후 첫 글자가 한글이 아니거나 + // (2)-B 이후 두 글자(초성-중성)가 합성이 가능한 경우 + // 현재 인덱스로부터 2글자를 합성하고, 인덱스를 2칸 전진 + bool isCur2SpanOk = HangulChar.TryJoinToSyllable( + new char[] { newString[curIdx], newString[curIdx + 1] }, + out char cur2SpanSyllable); + bool isCur2Next1Ok = !(new HangulChar(newString[curIdx + 2])).IsHangul(); + bool isCur2Next2Ok = HangulChar.TryJoinToSyllable( + new char[] { newString[curIdx + 2], newString[curIdx + 3] }, + out char _); + // (3) 현재 인덱스로부터 3글자 합성이 가능하고 + // (4)-A 이후 첫 글자가 한글이 아니거나 + // (4)-B 이후 두 글자(초성-중성)가 합성이 가능한 경우 + // 현재 인덱스로부터 3글자를 합성하고, 인덱스를 3칸 전진 + bool isCur3SpanOk = HangulChar.TryJoinToSyllable( + new char[] { newString[curIdx], newString[curIdx + 1], newString[curIdx + 2] }, + out char cur3SpanSyllable); + bool isCur3Next1Ok = !(new HangulChar(newString[curIdx + 3])).IsHangul(); + bool isCur3Next2Ok = HangulChar.TryJoinToSyllable( + new char[] { newString[curIdx + 3], newString[curIdx + 4] }, + out char _); - /// - /// 현재 인스턴스의 문자열을 한글 문자열 부분과 나머지 문자열 부분으로 구분하여 반환합니다. - /// - /// - public string[] SeparateString() => HangulString.SeparateString(CurrentString); + // 상기 논리에 의한 판단부 + // 2글자 기준 판단 + if (isCur2SpanOk && (isCur2Next1Ok || isCur2Next2Ok)) + { + sb.Append(cur2SpanSyllable); + curIdx += 2; + } + // 3글자 기준 판단 + else if (isCur3SpanOk && (isCur3Next1Ok || isCur3Next2Ok)) + { + sb.Append(cur3SpanSyllable); + curIdx += 3; + } + else + { + sb.Append(hc.CurrentCharacter); + curIdx++; + } + } - /// - /// 현재 인스턴스의 문자열이 모두 한글로만 이루어져 있는지의 여부를 반환합니다. - /// - /// - public bool IsAllHangul() => HangulString.IsAllHangul(CurrentString); + return sb.ToString(); + } - /// - /// 현재 인스턴스의 문자열에 대해 한글 음절을 초성, 중성, 종성으로 분리하여 반환합니다. - /// - /// - public string SplitToPhonemes() => HangulString.SplitToPhonemes(CurrentString); + /// + /// 문자열에 대해 초성 검색을 실시합니다. 반환값은 초성 검색에 대한 결과의 존재여부입니다. + /// 검색 문자열에 초성이 주어질 경우 초성 일치, 그렇지 않은 경우 문자 완전 일치 여부를 반환합니다. + /// + /// 검색할 문자열 + /// 대상 문자열 + /// (초성)일치가 발견된 대상 문자열 내 인덱스 배열 + /// + /// 또는 이 null인 경우 + public static bool GetOnsetMatches(string searchString, string targetString, out int[] indices) + { + if (searchString == null || targetString == null) + { + throw new ArgumentNullException(); + } - /// - /// 인스턴스 문자열 내의 초성, 중성, 종성 음소를 합성하여 반환합니다. - /// 의도하지 않은 반환 결과를 피하기 위해 일반적으로 사용하는 방식의 - /// (완전한 한글 음절이 분리된) 자음/모음으로 구성된 문자열을 인수로 사용할 것을 권장합니다. - /// - /// - public string JoinPhonemes() => HangulString.JoinPhonemes(CurrentString); + List idxs = new List(); - /// - /// 인스턴스 문자열의 길이(글자수)를 반환합니다. - /// - /// - /// 윈도우 시스템에서는 개행문자가 2글자(2바이트)로 취급됨에 유의해야 합니다. - /// - /// 공백문자(whitespace) 무시 여부 - /// - public int GetStringLength(bool ignoreWhiteSpcaes = false) - { - string inputString = ignoreWhiteSpcaes ? RemoveWhiteSpaces(CurrentString) : CurrentString; - return inputString.Length; - } + // 검색 문자열의 길이가 대상 문자열의 길이보다 긴 경우 + if (searchString.Length > targetString.Length) + { + indices = idxs.ToArray(); + return false; + } - /// - /// 현재 인코딩에 대해 인스턴스 문자열의 길이(바이트)를 반환합니다. - /// - /// - /// 윈도우 시스템에서는 개행문자가 2글자(2바이트)로 취급됨에 유의해야 합니다. - /// - /// 공백문자(whitespace) 무시 여부 - /// - public int GetStringByteLength(bool ignoreWhiteSpcaes = false) - { - string inputString = ignoreWhiteSpcaes ? RemoveWhiteSpaces(CurrentString) : CurrentString; - return Encoding.Default.GetByteCount(inputString); - } + // 대상 문자열 내의 인덱스를 증가시켜 가며 + for (int curIdx = 0; curIdx < targetString.Length - searchString.Length + 1; curIdx++) + { + // 해당 인덱스로부터 검색 문자열의 길이만큼 초성일치 검색 수행 + // 초성 일치가 될 경우 해당 인덱스 저장 + bool isMatch = true; + for (int chkIdx = curIdx; chkIdx < curIdx + searchString.Length; chkIdx++) + { + if (!HangulChar.IsOnsetMatch(searchString[chkIdx - curIdx], targetString[chkIdx])) + { + isMatch = false; + break; + } + } + if (isMatch) + { + idxs.Add(curIdx); + } + } + indices = idxs.ToArray(); + if (indices.Length > 0) + { + return true; + } + else + { + return false; + } + } - /// - /// 인수의 문자열에서 공백문자(whitespace)를 제거한 문자열을 반환합니다. - /// - /// - /// - private static string RemoveWhiteSpaces(string aString) => - new string(aString.Where(c => !char.IsWhiteSpace(c)).Select(c => c).ToArray()); - } + /// + /// 현재 인스턴스의 문자열을 HangulChar 클래스 인스턴스의 배열로 변환하여 반환합니다. + /// + /// + public HangulChar[] ToHangulCharArray() => HangulString.ToHangulCharArray(CurrentString); + + /// + /// 현재 인스턴스의 문자열을 한글 문자열 부분과 나머지 문자열 부분으로 구분하여 반환합니다. + /// + /// + public string[] SeparateString() => HangulString.SeparateString(CurrentString); + + /// + /// 현재 인스턴스의 문자열이 모두 한글로만 이루어져 있는지의 여부를 반환합니다. + /// + /// + public bool IsAllHangul() => HangulString.IsAllHangul(CurrentString); + + /// + /// 현재 인스턴스의 문자열에 대해 한글 음절을 초성, 중성, 종성으로 분리하여 반환합니다. + /// + /// + public string SplitToPhonemes() => HangulString.SplitToPhonemes(CurrentString); + + /// + /// 인스턴스 문자열 내의 초성, 중성, 종성 음소를 합성하여 반환합니다. + /// 의도하지 않은 반환 결과를 피하기 위해 일반적으로 사용하는 방식의 + /// (완전한 한글 음절이 분리된) 자음/모음으로 구성된 문자열을 인수로 사용할 것을 권장합니다. + /// + /// + public string JoinPhonemes() => HangulString.JoinPhonemes(CurrentString); + + /// + /// 인스턴스 문자열의 길이(글자수)를 반환합니다. + /// + /// + /// 윈도우 시스템에서는 개행문자가 2글자(2바이트)로 취급됨에 유의해야 합니다. + /// + /// 공백문자(whitespace) 무시 여부 + /// + public int GetStringLength(bool ignoreWhiteSpcaes = false) + { + string inputString = ignoreWhiteSpcaes ? RemoveWhiteSpaces(CurrentString) : CurrentString; + return inputString.Length; + } + + /// + /// 현재 인코딩에 대해 인스턴스 문자열의 길이(바이트)를 반환합니다. + /// + /// + /// 윈도우 시스템에서는 개행문자가 2글자(2바이트)로 취급됨에 유의해야 합니다. + /// + /// 공백문자(whitespace) 무시 여부 + /// + public int GetStringByteLength(bool ignoreWhiteSpcaes = false) + { + string inputString = ignoreWhiteSpcaes ? RemoveWhiteSpaces(CurrentString) : CurrentString; + return Encoding.Default.GetByteCount(inputString); + } + + /// + /// 인수의 문자열에서 공백문자(whitespace)를 제거한 문자열을 반환합니다. + /// + /// + /// + private static string RemoveWhiteSpaces(string aString) => + new string(aString.Where(c => !char.IsWhiteSpace(c)).Select(c => c).ToArray()); + + // c++ -> C# + private static bool IsMidMix(char wc, ref char pFirst, ref char pSeconde) + { + for (int i = 0; i < wcMidMix.GetLength(0); i++) + { + if (wcMidMix[i, 2] == wc) + { + pFirst = wcMidMix[i, 0]; + pSeconde = wcMidMix[i, 1]; + return true; + } + } + + return false; + } + + // C++ -> C# + private static bool IsTailMix(char wc, ref char pFirst, ref char pSeconde) + { + for (int i = 0; i < wcTailMix.GetLength(0); i++) + { + if (wcTailMix[i, 2] == wc) + { + pFirst = wcTailMix[i, 0]; + pSeconde = wcTailMix[i, 1]; + return true; + } + } + + return false; + } + + // C++ -> C# + private static bool IsFusionTailMix(char wcFirst, char wcSeconde, ref char pResult) + { + int dIndex = -1; + + for (int i = 0; i < wcTailMix.GetLength(0); i++) + { + if (wcTailMix[i, 0] == wcFirst && wcTailMix[i, 1] == wcSeconde) + { + dIndex = i; + break; + } + } + + if (dIndex == -1) + return false; + + pResult = wcTailMix[dIndex, 2]; + return true; + } + + // C++ -> C# + private static int Get_T_Index(char ch) + { + for (int i = 0; i < wcTail.GetLength(0); i++) + { + if (wcTail[i] == ch) + return i; + } + + return -1; + } + + // C++ -> C# + private static int Get_M_Index(char ch) + { + for (int i = 0; i < wcMid.GetLength(0); i++) + { + if (wcMid[i] == ch) + return i; + } + + return -1; + } + + // C++ -> C# + public static List CharacterMakeHistory(string wstr) + { + List m_buffer = new List(); + + string strMakeString = string.Empty; + string strIngInput = string.Empty; + + char wcFirst = '\0'; + char wcSeconde = '\0'; + + int dPos = 0; + char[] str = wstr.ToCharArray(); + + while (dPos < str.Length) + { + // KR 조합형 + if (str[dPos] >= 'ㄱ' && str[dPos] <= 'ㅣ') + { + if (IsMidMix(str[dPos], ref wcFirst, ref wcSeconde)) + { + strIngInput = strMakeString; + strIngInput += String.Format("{0}", wcFirst); + + m_buffer.Add(strIngInput); + } + else if (IsTailMix(str[dPos], ref wcFirst, ref wcSeconde)) + { + + strIngInput = strMakeString; + strIngInput += String.Format("{0}", wcFirst); + + m_buffer.Add(strIngInput); + } + + strIngInput = strMakeString; + strIngInput += String.Format("{0}", wcFirst); + + m_buffer.Add(strIngInput); + } + // KR 완성형 + else if (str[dPos] >= '가' && str[dPos] <= '힣') + { + // 초성 + int dHeadPos = Math.Abs(str[dPos] - 0xAC00) / (21 * 28); + + char wcTemp = '\0'; + + // 앞 글자에 초성 합성이 가능할 경우 체크하여 합성 + if ((strIngInput.Length >= 1)) + { + wcTemp = strIngInput[strIngInput.Length - 1]; + + // 이전 글자 종성과 현재 글자 초성으로 혼합된 종성을 얻는다. + char wcMixTail = ' '; + + // 합성에 성공한 경우 + if (IsFusionTailMix(wcTail[Math.Abs(wcTemp - 0xAC00) % 28], wcHead[dHeadPos], ref wcMixTail)) + { + // 이전 글자 종성 제거 + wcTemp = (char)(wcTemp - Get_T_Index(wcTail[(Math.Abs(wcTemp - 0xAC00)) % 28])); + + // 이전 글자 새로운 종성 삽입 + wcTemp = (char)(wcTemp + Get_T_Index(wcMixTail)); + } + // 이전 글자에 종성이 없고 현재 글자의 초성이 정상일 경우 + else if (Get_T_Index(wcTail[Math.Abs(wcTemp - 0xAC00) % 28]) == 0 && Get_T_Index(wcHead[dHeadPos]) != -1) + { + wcTemp = (char)(wcTemp + Get_T_Index(wcHead[dHeadPos])); + } + else + { + wcTemp = '\0'; + } + } + + strIngInput = strMakeString; + + if (wcTemp != '\0') + { + strIngInput = strIngInput.Substring(0, strIngInput.Length - 1); + strIngInput += String.Format("{0}", wcTemp); + } + else + { + strIngInput += String.Format("{0}", wcHead[dHeadPos]); + } + + m_buffer.Add(strIngInput); + + // 중성 + int dMidPos = Math.Abs(str[dPos] - 0xAC00) % (21 * 28) / 28; + + if (IsMidMix(wcMid[dMidPos], ref wcFirst, ref wcSeconde)) + { + int szMindex2 = Get_M_Index(wcFirst); + char TszM_0 = (char)(0xAC00 + (dHeadPos * 588) + (szMindex2 * 28)); + + strIngInput = strMakeString; + strIngInput += String.Format("{0}", TszM_0); + + m_buffer.Add(strIngInput); + } + + char TszM = (char)(0xAC00 + (dHeadPos * 588) + (dMidPos * 28)); + + strIngInput = strMakeString; + strIngInput += String.Format("{0}", TszM); + + m_buffer.Add(strIngInput); + + // 종성 + int dTailPos = Math.Abs(str[dPos] - 0xAC00) % 28; + + if (wcTail[dTailPos] != ' ') + { + if (IsTailMix(wcTail[dTailPos], ref wcFirst, ref wcSeconde)) + { + int szTindex2 = Get_T_Index(wcFirst); + + char TszT_0 = (char)(0xAC00 + (dHeadPos * 588) + (dMidPos * 28) + szTindex2); + + strIngInput = strMakeString; + strIngInput += String.Format("{0}", TszT_0); + + m_buffer.Add(strIngInput); + } + + char TszT = (char)(0xAC00 + (dHeadPos * 588) + (dMidPos * 28) + dTailPos); + + strIngInput = strMakeString; + strIngInput += String.Format("{0}", TszT); + + m_buffer.Add(strIngInput); + } + } + else + { + strIngInput = strMakeString; + strIngInput += String.Format("{0}", str[dPos]); + + m_buffer.Add(strIngInput); + } + + strMakeString = strIngInput; + + dPos++; + } + + return m_buffer; + } + } } diff --git a/NaverSearcher/Program.cs b/NaverSearcher/Program.cs index f1dafbc..8ae8931 100644 --- a/NaverSearcher/Program.cs +++ b/NaverSearcher/Program.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using System.Windows.Forms; namespace NaverSearcher