From a55addb24e7aee43e6b39da7b47b93c4f126976a Mon Sep 17 00:00:00 2001 From: chodadoo Date: Thu, 5 Aug 2021 10:03:57 +0900 Subject: [PATCH] =?UTF-8?q?1.=20=ED=82=A4=EC=9B=8C=EB=93=9C=20=EC=9B=B9=20?= =?UTF-8?q?=EC=88=98=EC=8B=A0=20=EB=A1=9C=EC=A7=81=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?2.=20=EC=93=B0=EB=A0=88=EB=93=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NaverSearcher/.editorconfig | 78 ++ NaverSearcher/Form1.Designer.cs | 1 - NaverSearcher/Form1.cs | 346 +++++++-- NaverSearcher/HangulChar.cs | 292 -------- NaverSearcher/HangulString.cs | 702 ------------------ NaverSearcher/NaverSearcher.csproj | 20 +- NaverSearcher/Program.cs | 10 +- NaverSearcher/Properties/Settings.Designer.cs | 38 + NaverSearcher/Properties/Settings.settings | 9 + NaverSearcher/StringParser.cs | 288 +++++++ 10 files changed, 721 insertions(+), 1063 deletions(-) create mode 100644 NaverSearcher/.editorconfig delete mode 100644 NaverSearcher/HangulChar.cs delete mode 100644 NaverSearcher/HangulString.cs create mode 100644 NaverSearcher/Properties/Settings.Designer.cs create mode 100644 NaverSearcher/Properties/Settings.settings diff --git a/NaverSearcher/.editorconfig b/NaverSearcher/.editorconfig new file mode 100644 index 0000000..298e151 --- /dev/null +++ b/NaverSearcher/.editorconfig @@ -0,0 +1,78 @@ +# Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\Users\choda\Desktop\TEST\NaverSearcher\NaverSearcher\ codebase based on best match to current usage at 2021-08-04 +# You can modify the rules from these initially generated values to suit your own policies +# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference +[*.cs] + + +#Core editorconfig formatting - indentation + +#use soft tabs (spaces) for indentation +indent_style = space + +#Formatting - new line options + +#place else statements on a new line +csharp_new_line_before_else = true +#require members of object initializers to be on the same line +csharp_new_line_before_members_in_object_initializers = false +#require braces to be on a new line for methods, control_blocks, and types (also known as "Allman" style) +csharp_new_line_before_open_brace = methods, control_blocks, types + +#Formatting - organize using options + +#sort System.* using directives alphabetically, and place them before other usings +dotnet_sort_system_directives_first = true + +#Formatting - spacing options + +#require NO space between a cast and the value +csharp_space_after_cast = false +#require a space after a keyword in a control flow statement such as a for loop +csharp_space_after_keywords_in_control_flow_statements = true +#remove space within empty argument list parentheses +csharp_space_between_method_call_empty_parameter_list_parentheses = false +#remove space between method call name and opening parenthesis +csharp_space_between_method_call_name_and_opening_parenthesis = false +#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call +csharp_space_between_method_call_parameter_list_parentheses = false +#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. +csharp_space_between_method_declaration_parameter_list_parentheses = false + +#Style - Code block preferences + +#prefer curly braces even for one line of code +csharp_prefer_braces = true:suggestion + +#Style - expression level options + +#prefer the type name for member access expressions, instead of the language keyword +dotnet_style_predefined_type_for_member_access = false:suggestion + +#Style - implicit and explicit types + +#prefer explicit type over var in all cases, unless overridden by another code style rule +csharp_style_var_elsewhere = false:suggestion +#prefer explicit type over var to declare variables with built-in system types such as int +csharp_style_var_for_built_in_types = false:suggestion +#prefer explicit type over var when the type is already mentioned on the right-hand side of a declaration +csharp_style_var_when_type_is_apparent = false:suggestion + +#Style - language keyword and framework type options + +#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion + +#Style - modifier options + +#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +#Style - Modifier preferences + +#when this rule is set to a list of modifiers, prefer the specified ordering. +csharp_preferred_modifier_order = static,readonly,override,private,public,protected:suggestion + +#Style - qualification options + +#prefer fields not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_field = false:suggestion diff --git a/NaverSearcher/Form1.Designer.cs b/NaverSearcher/Form1.Designer.cs index 47a0ad2..676e094 100644 --- a/NaverSearcher/Form1.Designer.cs +++ b/NaverSearcher/Form1.Designer.cs @@ -51,7 +51,6 @@ namespace NaverSearcher this.Name = "Form1"; this.Text = "Form1"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1_FormClosing); - this.Load += new System.EventHandler(this.Form1_Load); this.ResumeLayout(false); } diff --git a/NaverSearcher/Form1.cs b/NaverSearcher/Form1.cs index 27f625b..0f612a5 100644 --- a/NaverSearcher/Form1.cs +++ b/NaverSearcher/Form1.cs @@ -1,51 +1,120 @@ -using OpenQA.Selenium; +using Newtonsoft.Json.Linq; +using OpenQA.Selenium; using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Interactions; using OpenQA.Selenium.Support.UI; using SeleniumExtras.WaitHelpers; using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Threading; using System.Windows.Forms; +using WebSocketSharp; namespace NaverSearcher { public partial class Form1 : Form { - ChromeDriver _ChromeDriver; + private static Random m_Random = new Random(); - Random m_Random = new Random(); + WebSocket m_WebSocket = new WebSocket("ws://api.inrose.com"); + + Thread m_Thread = new Thread(NaverSearcherExcute); public Form1() { InitializeComponent(); - ChromeOptions _ChromeOptions = new ChromeOptions(); - _ChromeOptions.AddArguments("disable-infobars"); - _ChromeOptions.AddArguments("--js-flags=--expose-gc"); - _ChromeOptions.AddArguments("--enable-precise-memory-info"); - _ChromeOptions.AddArguments("--disable-popup-blocking"); - _ChromeOptions.AddArguments("--disable-default-apps"); - _ChromeOptions.AddArguments("--headless"); - _ChromeOptions.AddArguments("user - agent = Mozilla / 5.0(Macintosh; Intel Mac OS X 10_12_6) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 61.0.3163.100 Safari / 537.36"); + m_WebSocket.OnOpen += (sender, e) => + { + m_WebSocket.Send("{\"GUID\":\"" + Properties.Settings.Default.GUID + "\"}"); + }; - // 프록시 설정 - //Proxy proxy = new Proxy(); - //proxy.Kind = ProxyKind.Manual; - //proxy.IsAutoDetect = false; - //proxy.HttpProxy = - //proxy.SslProxy = ip; - //_ChromeOptions.Proxy = proxy; - //_ChromeOptions.AddArgument("ignore-certificate-errors"); + m_WebSocket.OnMessage += (sender, e) => + { + JObject _JObject = JObject.Parse(e.Data); - ChromeDriverService _ChromeDriverService = ChromeDriverService.CreateDefaultService(); - _ChromeDriverService.HideCommandPromptWindow = true; + if (_JObject.ContainsKey("keepalive")) + { + } - //_ChromeDriver = new ChromeDriver(_ChromeDriverService, _ChromeOptions); - _ChromeDriver = new ChromeDriver(); + if (_JObject.ContainsKey("key_pairs")) + { + if (m_Thread.ThreadState != ThreadState.Unstarted) + m_Thread.Join(); - //_ChromeOptions.add_argument("user-agent=Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko") + if (m_Thread.ThreadState == ThreadState.Stopped) + m_Thread = new Thread(NaverSearcherExcute); + + m_Thread.Start(_JObject); + } + }; + + m_WebSocket.OnError += (sender, e) => + { + System.Diagnostics.Trace.WriteLine(e.Message); + }; + + m_WebSocket.OnClose += (sender, e) => + { + System.Diagnostics.Trace.WriteLine(e.Reason); + + if (m_Thread.ThreadState != ThreadState.Unstarted) + m_Thread.Join(); + + Thread.Sleep(2000); + + m_WebSocket.Connect(); + }; + +#if DEBUG + // To change the logging level. + m_WebSocket.Log.Level = WebSocketSharp.LogLevel.Trace; + + // To change the wait time for the response to the Ping or Close. + //ws.WaitTime = TimeSpan.FromSeconds (10); + + // To emit a WebSocket.OnMessage event when receives a ping. + //ws.EmitOnPing = true; +#endif + // To enable the Per-message Compression extension. + //ws.Compression = CompressionMethod.Deflate; + + // To validate the server certificate. + /* + ws.SslConfiguration.ServerCertificateValidationCallback = + (sender, certificate, chain, sslPolicyErrors) => { + ws.Log.Debug ( + String.Format ( + "Certificate:\n- Issuer: {0}\n- Subject: {1}", + certificate.Issuer, + certificate.Subject + ) + ); + return true; // If the server certificate is valid. + }; + */ + + // To send the credentials for the HTTP Authentication (Basic/Digest). + //ws.SetCredentials ("nobita", "password", false); + + // To send the Origin header. + //ws.Origin = "http://localhost:4649"; + + // To send the cookies. + //ws.SetCookie (new Cookie ("name", "nobita")); + //ws.SetCookie (new Cookie ("roles", "\"idiot, gunfighter\"")); + + // To connect through the HTTP Proxy server. + //ws.SetProxy ("http://localhost:3128", "nobita", "password"); + + // To enable the redirection. + //ws.EnableRedirection = true; + + // Connect to the server. + m_WebSocket.Connect(); + + // Connect to the server asynchronously. + //m_WebSocket.ConnectAsync(); // 하루에 두번 검색 // {{바나나우유 라떼, 초코우유 라떼}, {민트초코바, 민트색}} @@ -126,26 +195,47 @@ namespace NaverSearcher // 새창 닫기 } - private void button1_Click(object sender, EventArgs e) + private static void NaverSearcherExcute(Object _Object) { + JObject _JObject = (JObject)_Object; + + ChromeOptions _ChromeOptions = new ChromeOptions(); + _ChromeOptions.AddArguments("disable-infobars"); + _ChromeOptions.AddArguments("--js-flags=--expose-gc"); + _ChromeOptions.AddArguments("--enable-precise-memory-info"); + _ChromeOptions.AddArguments("--disable-popup-blocking"); + _ChromeOptions.AddArguments("--disable-default-apps"); + _ChromeOptions.AddArguments("--headless"); + _ChromeOptions.AddArguments("user - agent = Mozilla / 5.0(Macintosh; Intel Mac OS X 10_12_6) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 61.0.3163.100 Safari / 537.36"); + + // 프록시 설정 + //Proxy proxy = new Proxy(); + //proxy.Kind = ProxyKind.Manual; + //proxy.IsAutoDetect = false; + //proxy.HttpProxy = + //proxy.SslProxy = ip; + //_ChromeOptions.Proxy = proxy; + //_ChromeOptions.AddArgument("ignore-certificate-errors"); + + ChromeDriverService _ChromeDriverService = ChromeDriverService.CreateDefaultService(); + _ChromeDriverService.HideCommandPromptWindow = true; + + //_ChromeDriver = new ChromeDriver(_ChromeDriverService, _ChromeOptions); + ChromeDriver _ChromeDriver = new ChromeDriver(); + + //_ChromeOptions.add_argument("user-agent=Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko") + + WebDriverWait _WebDriverWait = new WebDriverWait(_ChromeDriver, TimeSpan.FromSeconds(3)); + try { - WebDriverWait _WebDriverWait = new WebDriverWait(_ChromeDriver, TimeSpan.FromSeconds(3)); - - string[][] key_word = - { - new string[] {"바나나우유 라떼", "초코우유 라떼" }, - new string[] { "민트초코바", "민트색" } - }; - - foreach (string[] key_pair in key_word) + foreach (var key_words in _JObject["key_pairs"]) { // 로그인 여부 확인 // 로그아웃 실행 // 네이버 메인 // 스크롤 내린다. // 20 ~ 60초 대기 - // try { @@ -157,9 +247,9 @@ namespace NaverSearcher if (IsElementPresent(_ChromeDriver, By.XPath(_logout))) { _ChromeDriver.FindElement(By.XPath(_logout)).Click(); - } + } - foreach (string key in key_pair) + foreach (var key_word in key_words["key_words"]) { // 검색어 입력 (검색어 1) // 검색어 확인 @@ -170,6 +260,7 @@ namespace NaverSearcher // 스크롤 내리기 // 20 ~ 60초 대기 // 새창 닫기 + try { _ChromeDriver.Navigate().GoToUrl(@"https://naver.com"); @@ -180,17 +271,17 @@ namespace NaverSearcher IWebElement _query_elment = _ChromeDriver.FindElement(By.XPath(_query_xpath)); - foreach (var item in HangulString.CharacterMakeHistory(key)) + foreach (var item in StringParser.CharacterMakeHistory(key_word["key_word"].ToString())) { _query_elment.Clear(); _query_elment.SendKeys(item); - Delay(m_Random.Next(500, 1000)); + Thread.Sleep(m_Random.Next(500, 1000)); } _query_elment.SendKeys(OpenQA.Selenium.Keys.Enter); ((IJavaScriptExecutor)_ChromeDriver).ExecuteScript("window.scrollBy(0, document.body.scrollHeight);"); - Delay(m_Random.Next(2000, 6000)); + Thread.Sleep(m_Random.Next(2000, 6000)); ReadOnlyCollection links = _ChromeDriver.FindElements(By.CssSelector("a[href*='https://']")); @@ -208,12 +299,151 @@ namespace NaverSearcher } catch (Exception ex) { - + Console.WriteLine("=== 무작위 링크 접속 ==="); + Console.WriteLine(ex.Message); } - } while (--_try > 0); + } while (--_try > 0); ((IJavaScriptExecutor)_ChromeDriver).ExecuteScript("window.scrollBy(0, document.body.scrollHeight);"); - Delay(m_Random.Next(2000, 6000)); + Thread.Sleep(m_Random.Next(2000, 6000)); + + if (_ChromeDriver.WindowHandles.First() != _ChromeDriver.WindowHandles.Last()) + { + _ChromeDriver.SwitchTo().Window(_ChromeDriver.WindowHandles.Last()); + _ChromeDriver.Close(); + _ChromeDriver.SwitchTo().Window(_ChromeDriver.WindowHandles.First()); + } + } + catch (Exception ex) + { + Console.WriteLine("=== 검색어 ==="); + Console.WriteLine(ex.Message); + } + } + } + catch (Exception ex) + { + Console.WriteLine("=== 검색어 페어 ==="); + Console.WriteLine(ex.Message); + } + } + + // 결과 던지기 + using (WebSocket _WebSocket = new WebSocket("ws://api.inrose.com")) + { + _WebSocket.OnOpen += (sender, e) => { + _WebSocket.Send("{\"GUID\":\"" + Properties.Settings.Default.GUID + "\", \"keyword\":" + _JObject + ", \"done\":true}"); + }; + + _WebSocket.OnMessage += (sender, e) => { + }; + + _WebSocket.OnError += (sender, e) => { + }; + + _WebSocket.OnClose += (sender, e) => { + }; + + _WebSocket.Connect(); + + Thread.Sleep(5000); + } + } + catch (Exception ex) + { + Console.WriteLine("=== 실행 ==="); + Console.WriteLine(ex.Message); + } + finally + { + _ChromeDriver.Quit(); + } + } + + private void Excute(JObject _JObject) + { + try + { + ChromeDriver _ChromeDriver = new ChromeDriver(); + WebDriverWait _WebDriverWait = new WebDriverWait(_ChromeDriver, TimeSpan.FromSeconds(3)); + + foreach (var key_words in _JObject["key_pairs"]) + { + // 로그인 여부 확인 + // 로그아웃 실행 + // 네이버 메인 + // 스크롤 내린다. + // 20 ~ 60초 대기 + + try + { + _ChromeDriver.Navigate().GoToUrl(@"https://naver.com"); + _WebDriverWait.Until(ExpectedConditions.ElementIsVisible(By.XPath("//*[@id=\"query\"]"))); + + string _logout = @"/html/body/div/div/div[1]/div[1]/a[1]"; + + if (IsElementPresent(_ChromeDriver, By.XPath(_logout))) + { + _ChromeDriver.FindElement(By.XPath(_logout)).Click(); + } + + foreach (var key_word in key_words["key_words"]) + { + // 검색어 입력 (검색어 1) + // 검색어 확인 + // 스크롤 내린다. + // 20 ~ 60초 대기 + // 아무 게시글 클릭 + // 이동된 페이지 작업 + // 스크롤 내리기 + // 20 ~ 60초 대기 + // 새창 닫기 + + try + { + _ChromeDriver.Navigate().GoToUrl(@"https://naver.com"); + + string _query_xpath = "//*[@id=\"query\"]"; + + _WebDriverWait.Until(ExpectedConditions.ElementIsVisible(By.XPath(_query_xpath))); + + IWebElement _query_elment = _ChromeDriver.FindElement(By.XPath(_query_xpath)); + + foreach (var item in StringParser.CharacterMakeHistory(key_word["key_word"].ToString())) + { + _query_elment.Clear(); + _query_elment.SendKeys(item); + Thread.Sleep(m_Random.Next(500, 1000)); + } + + _query_elment.SendKeys(OpenQA.Selenium.Keys.Enter); + + ((IJavaScriptExecutor)_ChromeDriver).ExecuteScript("window.scrollBy(0, document.body.scrollHeight);"); + Thread.Sleep(m_Random.Next(2000, 6000)); + + ReadOnlyCollection links = _ChromeDriver.FindElements(By.CssSelector("a[href*='https://']")); + + int _try = links.Count; + + do + { + try + { + int _sel = m_Random.Next(0, links.Count); + string kknd = links[_sel].GetAttribute("href"); + links[_sel].Click(); + + break; + } + catch (Exception ex) + { + Console.WriteLine("=== 무작위 링크 접속 ==="); + Console.WriteLine(ex.Message); + } + } while (--_try > 0); + + ((IJavaScriptExecutor)_ChromeDriver).ExecuteScript("window.scrollBy(0, document.body.scrollHeight);"); + Thread.Sleep(m_Random.Next(2000, 6000)); if (_ChromeDriver.WindowHandles.First() != _ChromeDriver.WindowHandles.Last()) { @@ -343,33 +573,19 @@ namespace NaverSearcher } } - private void Form1_Load(object sender, EventArgs e) + private void button1_Click(object sender, EventArgs e) { - + if (m_WebSocket.ReadyState == WebSocketState.Closed) + m_WebSocket.Connect(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { - _ChromeDriver.Quit(); + if (m_Thread.ThreadState != ThreadState.Unstarted) + m_Thread.Join(); } - 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; - } - - private bool IsElementPresent(IWebDriver _IWebDriver, By by) + private static bool IsElementPresent(IWebDriver _IWebDriver, By by) { try { diff --git a/NaverSearcher/HangulChar.cs b/NaverSearcher/HangulChar.cs deleted file mode 100644 index c5150e9..0000000 --- a/NaverSearcher/HangulChar.cs +++ /dev/null @@ -1,292 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace NaverSearcher -{ - /// - /// 한글 문자 클래스: 현대 한글 문자에 대해 다양한 속성과 메서드를 제공하는 클래스입니다. - /// - public class HangulChar - { - // 한글에 대한 유니코드 범위 [44032, 55215] - private const int BeginCode = 0xAC00; - private const int EndCode = 0xD7AF; - - // 문자 구성: 초성, 중성, 종성 - 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 }; - private static readonly int[] NucleusStrokes = - { 2, 3, 3, 4, 2, 3, 3, 4, 2, 4, 5, 3, 3, 2, 4, 5, 3, 3, 1, 2, 1 }; - private static readonly int[] CodaStrokes = - { 0, 1, 2, 3, 1, 3, 4, 2, 3, 4, 6, 7, 5, 6, 7, 6, 3, 4, 6, 2, 4, 1, 2, 3, 2, 3, 4, 3 }; - - /// - /// 문자로부터 한글 문자 클래스의 인스턴스를 생성합니다. - /// - /// 한글 문자(char) - public HangulChar(char character) - { - this.CurrentCharacter = character; - } - - /// - /// 유니코드로부터 한글 문자 클래스의 인스턴스를 생성합니다. - /// - /// 한글 문자(char)에 해당하는 유니코드 - public HangulChar(int unicode) - { - this.CurrentCharacter = Convert.ToChar(unicode); - } - - /// - /// 현재 인스턴스의 문자입니다. - /// - public char CurrentCharacter { get; private set; } - - /// - /// 현재 인스턴스 문자의 유니코드입니다. - /// - public int CurrentUnicode => (int)CurrentCharacter; - - /// - /// 인자의 음소 배열을 한글 음절로 합성합니다. - /// 반환값은 합성의 성공(가능) 여부를 나타냅니다. - /// - /// 초성, 중성(, 종성) 순의 한글 음소 배열 - /// 합성된 한글 음절 - /// - /// 이 null로 주어질 경우 - public static bool TryJoinToSyllable(char[] phonemes, out char syllable) - { - if (phonemes == null) { throw new ArgumentNullException(); } - - bool isSuccess = false; - // 초성, 중성(, 종성)으로 이루어진 문자 배열인지 확인 - if (phonemes.Length >= 2 && phonemes.Length <= 3) - { - bool check = Onset.Contains(phonemes[0]) && Nucleus.Contains(phonemes[1]); - isSuccess = phonemes.Length == 3 ? check && Coda.Contains(phonemes[2]) : check; - } - - syllable = (char)0x00; - // 한글 음절로의 합성이 불가능한 경우 - if (!isSuccess) - { - return false; - } - // 한글 음절로의 합성이 가능한 경우 - int onsetIndex = Array.IndexOf(Onset, phonemes[0]); - int nucleusIndex = Array.IndexOf(Nucleus, phonemes[1]); - int codaIndex = phonemes.Length == 3 ? Array.IndexOf(Coda, phonemes[2]) : 0; - int newCode = BeginCode + (onsetIndex * 588) + (nucleusIndex * 28) + codaIndex; - if (newCode < BeginCode || newCode > EndCode) - { - isSuccess = false; - } - else - { - syllable = Convert.ToChar(newCode); - } - - return isSuccess; - } - - /// - /// 인자의 음소 배열을 한글 음절로 합성합니다. - /// 합성이 불가능할 경우 예외를 발생시킵니다. - /// - /// 초성, 중성(, 종성) 순의 한글 음소 배열 - /// 인수의 배열이 합성 가능한 초성, 중성(, 종성)이 아닐 경우 - /// - public static char JoinToSyllable(char[] phonemes) - { - bool isSuccess = TryJoinToSyllable(phonemes, out char syllable); - - if (!isSuccess) - { - throw new InvalidOperationException("인수의 배열은 합성 가능한 한글 초성, 중성(, 종성)이 아닙니다."); - } - - return syllable; - } - - /// - /// 검색문자를 대상문자에 대해 (초성) 비교 후 일치 여부를 반환합니다. - /// 검색문자에 초성이 주어질 경우 초성 일치, 그렇지 않은 경우 문자 완전 일치 여부를 반환합니다. - /// - /// (초성) 비교할 문자 - /// 비교 대상 문자 - /// - public static bool IsOnsetMatch(char searchChar, char targetChar) - { - // 1. 검색문자가 초성인 경우 대응하는 대상문자도 초성을 비교 - // 2. 그렇지 않은 경우 대응하는 대상문자와 완전 일치 여부 비교 - HangulChar shc = new HangulChar(searchChar); - HangulChar thc = new HangulChar(targetChar); - if (shc.IsOnset() && thc.TrySplitSyllable(out char[] phonemes)) - { - targetChar = phonemes[0]; - } - - return searchChar == targetChar ? true : false; - } - - /// - /// 검색문자를 현재 인스턴스의 문자에 대해 (초성) 비교 후 일치 여부를 반환합니다. - /// 검색문자에 초성이 주어질 경우 초성 일치, 그렇지 않은 경우 문자 완전 일치 여부를 반환합니다. - /// - /// (초성) 비교할 문자 - /// - public bool IsOnsetMatch(char searchChar) - { - return HangulChar.IsOnsetMatch(searchChar, this.CurrentCharacter); - } - - /// - /// 초성으로 사용될 수 있는지 판단합니다. - /// - /// - public bool IsOnset() => Onset.Contains(CurrentCharacter); - - /// - /// 중성으로 사용될 수 있는지 판단합니다. - /// - /// - public bool IsNucleus() => Nucleus.Contains(CurrentCharacter); - - /// - /// 종성으로 사용될 수 있는지 판단합니다. - /// - /// - public bool IsCoda() => Coda.Contains(CurrentCharacter); - - /// - /// 자음인지 판단합니다. - /// - /// - public bool IsConsonant() => Onset.Contains(CurrentCharacter) || Coda.Contains(CurrentCharacter); - - /// - /// 모음인지 판단합니다. - /// - /// - public bool IsVowel() => Nucleus.Contains(CurrentCharacter); - - /// - /// 음소(낱소리)인지 판단합니다. - /// - /// - public bool IsPhoneme() => IsConsonant() || IsVowel(); - - /// - /// 음소(낱소리)가 아닌 완전한 한글 음절인지 판단합니다. - /// - /// - public bool IsSyllable() => BeginCode <= (int)CurrentCharacter && (int)CurrentCharacter <= EndCode; - - /// - /// 한글 문자인지 판단합니다. - /// - /// - public bool IsKoreanCharacter() => IsPhoneme() || IsSyllable(); - - /// - /// 한글 문자인지 판단합니다. (== IsKoreanCharacter()) - /// - /// - public bool IsHangul() => IsKoreanCharacter(); - - /// - /// 한글 음절을 초성, 중성, 종성 순으로 분리합니다. - /// 반환 결과는 분리의 성공(가능)여부를 나타냅니다. - /// - /// 초성, 중성, 종성 순으로 분리된 배열 - /// - public bool TrySplitSyllable(out char[] phonemes) - { - // 분리 가능한 한글이 아닌 경우 - if (!IsSyllable()) - { - phonemes = new char[] { (char)0x00, (char)0x00, (char)0x00 }; - return false; - } - - int foo = (int)CurrentCharacter - BeginCode; - int onsetIndex = (int)foo / 588; - int nucleusIndex = (int)(foo - onsetIndex * 588) / 28; - int codaIndex = foo - onsetIndex * 588 - 28 * nucleusIndex; - - phonemes = new char[] { Onset[onsetIndex], Nucleus[nucleusIndex], Coda[codaIndex] }; - return true; - } - - /// - /// 한글 음절을 초성, 중성, 종성 순으로 분리합니다. - /// 분리가 불가능한 경우 예외를 발생시킵니다. - /// - /// 인스턴스의 문자가 분리 가능한 한글이 아닌 경우 - /// - public char[] SplitSyllable() - { - bool isSuccess = TrySplitSyllable(out char[] phonemes); - - if (!isSuccess) - { - throw new InvalidOperationException("인스턴스의 문자가 분리 가능한 한글이 아닙니다."); - } - - return phonemes; - } - - /// - /// 한글 획수를 반환합니다. 한글이 아닌 문자의 경우 0을 반환합니다. - /// - /// - public int CountStrokes() - { - // 한글이 아닌 경우 0을 반환 - if (!IsHangul()) - { - return 0; - } - - int count; - // 한글 - 음소인 경우 - if (IsPhoneme()) - { - if (IsOnset()) - { - count = OnsetStrokes[Array.IndexOf(Onset, CurrentCharacter)]; - } - else if (IsNucleus()) - { - count = NucleusStrokes[Array.IndexOf(Nucleus, CurrentCharacter)]; - } - else - { - count = CodaStrokes[Array.IndexOf(Coda, CurrentCharacter)]; - } - } - // 한글 - 분리 가능한 음절인 경우 - else - { - _ = TrySplitSyllable(out char[] phonemes); - count = OnsetStrokes[Array.IndexOf(Onset, phonemes[0])]; - count += NucleusStrokes[Array.IndexOf(Nucleus, phonemes[1])]; - count += CodaStrokes[Array.IndexOf(Coda, phonemes[2])]; - } - - return count; - - } - } -} diff --git a/NaverSearcher/HangulString.cs b/NaverSearcher/HangulString.cs deleted file mode 100644 index f875644..0000000 --- a/NaverSearcher/HangulString.cs +++ /dev/null @@ -1,702 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace NaverSearcher -{ - /// - /// 한글 문자열 클래스: (한글을 포함한) 문자열에 대해 한글 처리에 관련된 다양한 속성과 메서드를 제공하는 클래스입니다. - /// - public class HangulString - { - private static readonly Dictionary Nucleus_Sub = new Dictionary() - { - {'ㅘ', "ㅗㅏ"}, {'ㅙ', "ㅗㅐ"}, {'ㅚ', "ㅗㅣ"}, {'ㅝ', "ㅜㅓ"}, {'ㅟ', "ㅜㅣ"}, {'ㅞ', "ㅜㅔ"} - }; - - private static readonly Dictionary Coda_Sub = new Dictionary() - { - {'ㄳ', "ㄱㅅ"}, {'ㄵ', "ㄴㅈ"}, {'ㄶ', "ㄴㅎ"}, {'ㄺ', "ㄹㄱ"}, {'ㄻ', "ㄹㅁ"}, {'ㄼ', "ㄹㅂ"}, {'ㄽ', "ㄹㅅ"}, - {'ㄾ', "ㄹㅌ"}, {'ㄿ', "ㄹㅍ"}, {'ㅀ', "ㄹㅎ"}, {'ㅄ', "ㅂㅅ"} - }; - - ///< 초성 테이블 - private static readonly char[] wcHead = - { - 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', - 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', - 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', - 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', - 'ㅌ', 'ㅍ', 'ㅎ' - }; - - ///< 중성 테이블 - private static readonly char[] wcMid = - { - 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', - 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', - 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', - 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', - 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ' - }; - - ///< 중성 합성 테이블 - private static readonly char[,] wcMidMix = - { - {'ㅗ', 'ㅏ', 'ㅘ'}, - {'ㅗ', 'ㅐ', 'ㅙ'}, - { 'ㅗ', 'ㅑ', 'ㅚ'}, - { 'ㅜ', 'ㅓ', 'ㅝ'}, - { 'ㅜ', 'ㅔ', 'ㅞ'}, - { 'ㅜ', 'ㅣ', 'ㅟ'}, - { 'ㅡ', 'ㅣ', 'ㅢ'}, - }; - - ///< 종성 테이블 - private static readonly char[] wcTail = - { - ' ', 'ㄱ', 'ㄲ', 'ㄳ', - 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', - 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', - 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', - 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', - 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', - 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' - }; - - ///< 중성 합성 테이블 - private static readonly char[,] wcTailMix = - { - { 'ㄱ', 'ㅅ', 'ㄳ'}, - { 'ㄴ', 'ㅈ', 'ㄵ'}, - { 'ㄴ', 'ㅎ', 'ㄶ'}, - { 'ㄹ', 'ㄱ', 'ㄺ'}, - { 'ㄹ', 'ㅁ', 'ㄻ'}, - { 'ㄹ', 'ㅂ', 'ㄼ'}, - { 'ㄹ', 'ㅅ', 'ㄽ'}, - { 'ㄹ', 'ㅌ', 'ㄾ'}, - { 'ㄹ', 'ㅍ', 'ㄿ'}, - { 'ㄹ', 'ㅎ', 'ㅀ'}, - { 'ㅂ', 'ㅅ', 'ㅄ'}, - }; - - /// - /// 문자열로부터 한글 문자열 클래스의 인스턴스를 생성합니다. - /// - /// 인스턴스를 생성할 문자열 - public HangulString(string aString) => CurrentString = aString; - - /// - /// 현재 인스턴스의 문자열입니다. - /// - public string CurrentString { get; private set; } - - /// - /// 문자열을 HangulChar 클래스 인스턴스의 배열로 변환하여 반환합니다. - /// - /// - /// - /// 입력 인자가 null인 경우 - public static HangulChar[] ToHangulCharArray(string aString) - { - if (aString is null) - { - throw new ArgumentNullException(); - } - return aString.ToCharArray().Select(c => new HangulChar(c)).ToArray(); - } - - /// - /// 문자열을 한글 문자열 부분과 나머지 문자열 부분으로 구분하여 반환합니다. - /// - /// - /// - /// 입력 인자가 null인 경우 - public static string[] SeparateString(string aString) - { - if (aString is null) - { - throw new ArgumentNullException(); - } - - 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; - } - - /// - /// 문자열이 모두 한글로만 이루어져 있는지의 여부를 반환합니다. - /// - /// - /// - public static bool IsAllHangul(string aString) - { - string checkPoint = HangulString.SeparateString(aString)[1]; - if (checkPoint.Length > 0) - { - return false; - } - else - { - return true; - } - } - - /// - /// 문자열에 대해 한글 음절을 초성, 중성, 종성으로 분리하여 반환합니다. - /// - /// - /// - /// 이 null인 경우 - public static string SplitToPhonemes(string aString) - { - if (aString == 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) - { - sb.Append(pc); - } - } - } - // 한글 음절이 아닌 경우 - else - { - sb.Append(c); - } - } - return sb.ToString(); - } - - /// - /// 문자열에 대해 한글 음절을 초성, 중성, 종성으로 분리하여 반환합니다. - /// - /// - /// - /// 이 null인 경우 - public static string SplitToPhonemes2(string aString) - { - if (aString == 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); - } - } - } - - return sb.ToString(); - } - - /// - /// 문자열 내의 초성, 중성, 종성 음소를 합성하여 반환합니다. - /// 의도하지 않은 반환 결과를 피하기 위해 일반적으로 사용하는 방식의 - /// (완전한 한글 음절이 분리된) 자음/모음으로 구성된 문자열을 인수로 사용할 것을 권장합니다. - /// - /// - /// - /// 이 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); - - while (curIdx <= lastIdx) - { - // 현재 인덱스의 문자 - HangulChar hc = new HangulChar(newString[curIdx]); - // 현재 인덱스의 문자가 음소가 아닌 경우 현재 문자를 더하고 다음 인덱스로 이동 - if (!hc.IsPhoneme()) - { - sb.Append(hc.CurrentCharacter); - curIdx++; - continue; - } - - // 현재 인덱스의 문자가 음소인 경우에 대해 - // (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 _); - - // 상기 논리에 의한 판단부 - // 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++; - } - } - - return sb.ToString(); - } - - /// - /// 문자열에 대해 초성 검색을 실시합니다. 반환값은 초성 검색에 대한 결과의 존재여부입니다. - /// 검색 문자열에 초성이 주어질 경우 초성 일치, 그렇지 않은 경우 문자 완전 일치 여부를 반환합니다. - /// - /// 검색할 문자열 - /// 대상 문자열 - /// (초성)일치가 발견된 대상 문자열 내 인덱스 배열 - /// - /// 또는 이 null인 경우 - public static bool GetOnsetMatches(string searchString, string targetString, out int[] indices) - { - if (searchString == null || targetString == null) - { - throw new ArgumentNullException(); - } - - List idxs = new List(); - - // 검색 문자열의 길이가 대상 문자열의 길이보다 긴 경우 - if (searchString.Length > targetString.Length) - { - indices = idxs.ToArray(); - return false; - } - - // 대상 문자열 내의 인덱스를 증가시켜 가며 - 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; - } - } - - /// - /// 현재 인스턴스의 문자열을 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/NaverSearcher.csproj b/NaverSearcher/NaverSearcher.csproj index 1bac427..3bef28c 100644 --- a/NaverSearcher/NaverSearcher.csproj +++ b/NaverSearcher/NaverSearcher.csproj @@ -2,14 +2,30 @@ WinExe - netcoreapp3.1 + net45 true - + + + + + + + True + True + Settings.settings + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + \ No newline at end of file diff --git a/NaverSearcher/Program.cs b/NaverSearcher/Program.cs index 8ae8931..1752519 100644 --- a/NaverSearcher/Program.cs +++ b/NaverSearcher/Program.cs @@ -11,7 +11,15 @@ namespace NaverSearcher [STAThread] static void Main() { - Application.SetHighDpiMode(HighDpiMode.SystemAware); + //SetEnvironmentVariable + string _guid = Properties.Settings.Default.GUID; + + if (_guid == string.Empty || _guid == null) + { + Properties.Settings.Default.GUID = Guid.NewGuid().ToString(); + Properties.Settings.Default.Save(); + } + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); diff --git a/NaverSearcher/Properties/Settings.Designer.cs b/NaverSearcher/Properties/Settings.Designer.cs new file mode 100644 index 0000000..3f6bf64 --- /dev/null +++ b/NaverSearcher/Properties/Settings.Designer.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// 이 코드는 도구를 사용하여 생성되었습니다. +// 런타임 버전:4.0.30319.42000 +// +// 파일 내용을 변경하면 잘못된 동작이 발생할 수 있으며, 코드를 다시 생성하면 +// 이러한 변경 내용이 손실됩니다. +// +//------------------------------------------------------------------------------ + +namespace NaverSearcher.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string GUID { + get { + return ((string)(this["GUID"])); + } + set { + this["GUID"] = value; + } + } + } +} diff --git a/NaverSearcher/Properties/Settings.settings b/NaverSearcher/Properties/Settings.settings new file mode 100644 index 0000000..688ed4e --- /dev/null +++ b/NaverSearcher/Properties/Settings.settings @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/NaverSearcher/StringParser.cs b/NaverSearcher/StringParser.cs index c2cfb81..c8d4229 100644 --- a/NaverSearcher/StringParser.cs +++ b/NaverSearcher/StringParser.cs @@ -6,5 +6,293 @@ namespace NaverSearcher { class StringParser { + ///< 초성 테이블 + private static readonly char[] wcHead = + { + 'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', + 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', + 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', + 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', + 'ㅌ', 'ㅍ', 'ㅎ' + }; + + ///< 중성 테이블 + private static readonly char[] wcMid = + { + 'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', + 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', + 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', + 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', + 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ' + }; + + ///< 중성 합성 테이블 + private static readonly char[,] wcMidMix = + { + {'ㅗ', 'ㅏ', 'ㅘ'}, + {'ㅗ', 'ㅐ', 'ㅙ'}, + { 'ㅗ', 'ㅑ', 'ㅚ'}, + { 'ㅜ', 'ㅓ', 'ㅝ'}, + { 'ㅜ', 'ㅔ', 'ㅞ'}, + { 'ㅜ', 'ㅣ', 'ㅟ'}, + { 'ㅡ', 'ㅣ', 'ㅢ'}, + }; + + ///< 종성 테이블 + private static readonly char[] wcTail = + { + ' ', 'ㄱ', 'ㄲ', 'ㄳ', + 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', + 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', + 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', + 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', + 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', + 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ' + }; + + ///< 중성 합성 테이블 + private static readonly char[,] wcTailMix = + { + { 'ㄱ', 'ㅅ', 'ㄳ'}, + { 'ㄴ', 'ㅈ', 'ㄵ'}, + { 'ㄴ', 'ㅎ', 'ㄶ'}, + { 'ㄹ', 'ㄱ', 'ㄺ'}, + { 'ㄹ', 'ㅁ', 'ㄻ'}, + { 'ㄹ', 'ㅂ', 'ㄼ'}, + { 'ㄹ', 'ㅅ', 'ㄽ'}, + { 'ㄹ', 'ㅌ', 'ㄾ'}, + { 'ㄹ', 'ㅍ', 'ㄿ'}, + { 'ㄹ', 'ㅎ', 'ㅀ'}, + { 'ㅂ', 'ㅅ', 'ㅄ'}, + }; + + // 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; + } } }