영어로 잘못 쓴 한글과 한글로 잘못 쓴 영어
/**
* jQuery Plugin: Sticky Tabs
*
* @author Aidan Lister
// Set the correct tab when the page loads showStuffFromHash(context);
// Set the correct tab when a user uses their back/forward button $(window).on('hashchange', function() { showStuffFromHash(context); });
// Change the URL when tabs are clicked $('a', context).on('click', function(e) { history.pushState(null, null, this.href); showStuffFromHash(context); });
return this; }; }(jQuery));
window.buildTabsets = function(tocID) {
// build a tabset from a section div with the .tabset class function buildTabset(tabset) {
// check for fade and pills options var fade = tabset.hasClass("tabset-fade"); var pills = tabset.hasClass("tabset-pills"); var navClass = pills ? "nav-pills" : "nav-tabs";
// determine the heading level of the tabset and tabs var match = tabset.attr('class').match(/level(\d) /); if (match === null) return; var tabsetLevel = Number(match[1]); var tabLevel = tabsetLevel + 1;
// find all subheadings immediately below var tabs = tabset.find("div.section.level" + tabLevel); if (!tabs.length) return;
// create tablist and tab-content elements var tabList = $('
'); $(tabs[0]).before(tabList); var tabContent = $('
'); $(tabs[0]).before(tabContent);
// build the tabset var activeTab = 0; tabs.each(function(i) {
// get the tab div var tab = $(tabs[i]);
// get the id then sanitize it for use with bootstrap tabs var id = tab.attr('id');
// see if this is marked as the active tab if (tab.hasClass('active')) activeTab = i;
// remove any table of contents entries associated with // this ID (since we'll be removing the heading element) $("div#" + tocID + " li a[href='#" + id + "']").parent().remove();
// sanitize the id for use with bootstrap tabs id = id.replace(/[.\/?&!#<>]/g, '').replace(/\s/g, '_'); tab.attr('id', id);
// get the heading element within it, grab it's text, then remove it var heading = tab.find('h' + tabLevel + ':first'); var headingText = heading.html(); heading.remove();
// build and append the tab list item var a = $('' + headingText + ''); a.attr('href', '#' + id); a.attr('aria-controls', id); var li = $('
'); li.append(a); tabList.append(li);
// set it's attributes tab.attr('role', 'tabpanel'); tab.addClass('tab-pane'); tab.addClass('tabbed-pane'); if (fade) tab.addClass('fade');
// move it into the tab content div tab.detach().appendTo(tabContent); });
// set active tab $(tabList.children('li')[activeTab]).addClass('active'); var active = $(tabContent.children('div.section')[activeTab]); active.addClass('active'); if (fade) active.addClass('in');
if (tabset.hasClass("tabset-sticky")) tabset.rmarkdownStickyTabs(); }
// convert section divs with the .tabset class to tabsets var tabsets = $("div.section.tabset"); tabsets.each(function(i) { buildTabset($(tabsets[i])); }); };
keyboard strokes: 한영타 변환 1/2
2022-09-05
문제 개요
한영 키보드를 쓰다보면 한글 입력 상태인 줄 알고 키보드를 쳤는데 영어
입력 상태로 되어 있는 경우가 종종 있다. 예를 들면
안녕?
이라고 치고 싶었는데, 영어 입력 상태에서
dkssud?
이라고 입력되어 있는 상황인 거다.
이렇게 잘못 입력된 영어를 한글로 바꿀 수 있는 방법, 그리고 반대로
영어를 한글로 잘못 입력한 경우에도 올바른 한글로 바꿔주는 함수가
없을까?
검색 결과
위의 사이트를 방문하면 웹 상에서 잘못된 입력을 수정할 수 있다.
하지만 변환을 해야 할 때마다 웹을 방문해야 해서 번거롭다. R에서 바로
바꿀 순 없을까?
한글을 영어로
우선 한타을 영타로 바꿔보자. 왜냐하면 영타->한타보다
한타->영타가 쉽기 때문이다.
영타->한타의 경우 문자별로 1:1 대응 이상이 필요하기 때문이다. 예를
들어 dkssud?
를 한 글자씩 영자를 한글로 바꾸면
ㅇㅏㄴㄴㅕㅇ
이다. 이를 안녕
으로 만드는 것은
단순히 1:1 변환으로는 불가능한데 왜냐하면 안
의
ㄴ
은 종성이고 녕
의 ㄴ
은 초성으로
이에 대한 고려가 필요하기 때문이다. 그리고 UTF-8에서 안
은
한 글자이기 때문에 ㅇㅏㄴ
이라는 세 글자를
안
이라는 한 글자로 변환을 해야 한다.
하지만 한타를 영타를 바꾸는 것도 단점이 있는데, 한글의 경우 SHIFT를
눌러도 같은 문자인 경우가 있기 때문이다. 예를 들어 ㄱ
은
SHIFT를 누른 후라면 ㄲ
이 되지만 ㅏ
는 SHIFT를
눌러도 ㅏ
라서 ㅑ 므 ㅁ ㅠㅐㅛ.
를 영타로 바꾸면
i am a boy.
, I am a boy
,
I Am A boy
등 여러 가능성이 존재한다. 여기서는 간단히
SHIFT를 눌러도 같은 문자라면 SHIFT를 누르지 않은 상태라는 가정을
하겠다.
먼저 영어를 한글로 입력하면 어떻게 입력되는지를 확인해보자.
# Yesterday, New York was dark.
input_han = "ㅛㄷㄴㅅㄷㄱㅇ묘, ㅜㄷㅈ ㅛㅐ가 ㅈㅁㄴ ㅇㅁ가."
u_chars(input_han)
## ch codepoint label
## 1 ㅛ 315b HANGUL LETTER YO
## 2 ㄷ 3137 HANGUL LETTER TIKEUT
## 3 ㄴ 3134 HANGUL LETTER NIEUN
## 4 ㅅ 3145 HANGUL LETTER SIOS
## 5 ㄷ 3137 HANGUL LETTER TIKEUT
## 6 ㄱ 3131 HANGUL LETTER KIYEOK
## 7 ㅇ 3147 HANGUL LETTER IEUNG
## 8 묘 bb18 HANGUL SYLLABLE MYO
## 9 , ..2c COMMA
## 10 ..20 SPACE
## 11 ㅜ 315c HANGUL LETTER U
## 12 ㄷ 3137 HANGUL LETTER TIKEUT
## 13 ㅈ 3148 HANGUL LETTER CIEUC
## 14 ..20 SPACE
## 15 ㅛ 315b HANGUL LETTER YO
## 16 ㅐ 3150 HANGUL LETTER AE
## 17 가 ac00 HANGUL SYLLABLE GA
## 18 ..20 SPACE
## 19 ㅈ 3148 HANGUL LETTER CIEUC
## 20 ㅁ 3141 HANGUL LETTER MIEUM
## 21 ㄴ 3134 HANGUL LETTER NIEUN
## 22 ..20 SPACE
## 23 ㅇ 3147 HANGUL LETTER IEUNG
## 24 ㅁ 3141 HANGUL LETTER MIEUM
## 25 가 ac00 HANGUL SYLLABLE GA
## 26 . ..2e FULL STOP
한글의 경우 ㄱ
(기역, KIYEOK)의 여러 가지 다른 문자로
표시 가능하다는 점을 유의할 필요가 있다.
library(Unicode)
kiyeok = c(u_char_from_name("HANGUL LETTER KIYEOK"),
u_char_from_name("HANGUL CHOSEONG KIYEOK"),
u_char_from_name("HANGUL JONGSEONG KIYEOK"))
print(kiyeok)
## [1] U+3131 U+1100 U+11A8
intToUtf8(kiyeok)
## [1] "ㄱㄱㄱ"
그리고 초성 ᄀ
, 중성 ㅏ
, 종성
ᆨ
이 합쳐저 각
이라는 문자는 또 다른
문자이다.
u_chars('각')
## ch codepoint label
## 1 각 ac01 HANGUL SYLLABLE GAG
첫 번째 방법: 모든 한글에 대해 1:1 변환
아마도 가장 간단한 방법은 모든 한글 입력에 대해 영타 변환을 준비하는
것이다.
input_han='ㅛㄷㄴㅅㄷㄱㅇ묘, ㅜㄷㅈ ㅛㅐ가 ㅈㅁㄴ ㅇㅁ가.'
예를 들어 위의 한글 입력
ㅛㄷㄴㅅㄷㄱㅇ묘, ㅜㄷㅈ ㅛㅐ가 ㅈㅁㄴ ㅇㅁ가.
을 영타로
바꾼다면, ㅛ
는 y
로 묘
는
ay
로 바꾸면 된다. 컴퓨터에 입력 가능한 모든 한글 한 자 한
자에 대해 영타로 대응시키는 방법이다. 간단하지만 모든 한글에 대해 영타를
입력해야 하는 조금은 노가다 방법이다. 참고로 현대 한글로 만들 수 있는
글자는 총 11,172개라고 한다!1 여기엔 ㅏ
와 같은 글자는
포함되지도 않았다!
두 번째 방법: 한글을 초성, 중성, 종성으로 분해한 후 영타로 변환
첫 번째 방법은 구현 가능하지만 대단히 Data intensive한 방법이었다.
10000개라고 한다면, 한 글자에 대한 데이터를 입력하는 10초가 걸린다고
해도, 10*10000/60/60/24==1.1574
하루 이상의 시간이
소요된다. (물론 뭔가 인코딩의 규칙성을 활용하지 않고 무대포로 데이터를
입력할 때의 얘기이다.)
만약 한글을 초성, 중성, 종성 단위로 분해한다면 필요한 데이터는 100개
이하로 줄어든다. 현대
한글의 초,중,종성의 갯수는 초성 19개, 중성 21개, 종성 27개라고
한다.
한글을 초성, 중성, 종성 단위 또는 자음, 모음으로 분해하는 방법은
다음의 두 가지를 생각할 수 있다.
library(stringi)
library(KoNLP)
## Checking user defined dictionary!
stri_trans_nfd('ㅛㄷㄴㅅㄷㄱㅇ묘, ㅜㄷㅈ ㅛㅐ가 ㅈㅁㄴ ㅇㅁ가.')
## [1] "ㅛㄷㄴㅅㄷㄱㅇㅁㅛ, ㅜㄷㅈ ㅛㅐㄱㅏ ㅈㅁㄴ ㅇㅁㄱㅏ."
convertHangulStringToJamos('ㅛㄷㄴㅅㄷㄱㅇ묘, ㅜㄷㅈ ㅛㅐ가 ㅈㅁㄴ ㅇㅁ가.')
## [1] "ㅛ" "ㄷ" "ㄴ" "ㅅ" "ㄷ" "ㄱ" "ㅇ" "ㅁㅛ" "," " "
## [11] "ㅜ" "ㄷ" "ㅈ" " " "ㅛ" "ㅐ" "ㄱㅏ" " " "ㅈ" "ㅁ"
## [21] "ㄴ" " " "ㅇ" "ㅁ" "ㄱㅏ" "."
convertHangulStringToJamos()
는 함수 이름에서 유추할 수
있듯이 자음과 모음으로 분리한다. 묘
는 ㅁㅛ
로
가
는 ㄱㅏ
로 분해되었다. 이에 반해
stri_trans_nfd()
의 결과는 입력값과 달라보이지 않는다.
하지만 다음에서 확인할 수 있듯이 가
는 초성 기억와 중성
아로 분해된다.
u_chars('가')
## ch codepoint label
## 1 가 ac00 HANGUL SYLLABLE GA
u_chars(stri_trans_nfd('가'))
## ch codepoint label
## 1 ㄱ 1100 HANGUL CHOSEONG KIYEOK
## 2 ㅏ 1161 HANGUL JUNGSEONG A
이렇게 초성,중성,종성 또는 자음,모음으로 분해된 텍스트는 손쉽게
영타로 변환된다.
먼저 자음과 모음으로 분리된 텍스트를 영타로 변환하기 위한 1:1 대응
표를 만들어보자.
키보드의 한글 또는 영타에 해당하는 부분을 직접 입력해보면 된다.
ch_han = 'ㅂㅈㄷㄱㅅㅛㅕㅑㅐㅔㅁㄴㅇㄹㅎㅗㅓㅏㅣㅋㅌㅊㅍㅠㅜㅡ' # 한타
ch_han_shifted = 'ㅃㅉㄸㄲㅆㅛㅕㅑㅒㅖㅁㄴㅇㄹㅎㅗㅓㅏㅣㅋㅌㅊㅍㅠㅜㅡ' # 한타를 SHIFT를 누르고 쳤을 때
ch_eng = 'qwertyuiopasdfghjklzxcvbnm' # 영타
ch_eng_shifted = 'QWERTYUIOPASDFGHJKLZXCVBNM' # 영타 + SHIFT
이를 모아 테이블로 구성하면 다음과 같다.
library(data.table)
##
## Attaching package: 'data.table'
## The following object is masked _by_ '.GlobalEnv':
##
## inrange
dt_han_eng =
data.table(key_han = unlist(strsplit(paste0(ch_han,
ch_han_shifted), "")),
key_eng = unlist(strsplit(paste0(ch_eng,
ch_eng_shifted), "")))
# 앞에서 말했듯이 SHIFT를 눌러도 동일한 한글 글자에 해당하는 경우가 있다.
# 그래서 중복되는 경우를 제거한다.
dt_han_eng = dt_han_eng[!duplicated(dt_han_eng$key_han)]
setkey(dt_han_eng, key_han)
head(dt_han_eng)
## key_han key_eng
## 1: ㄱ r
## 2: ㄲ R
## 3: ㄴ s
## 4: ㄷ e
## 5: ㄸ E
## 6: ㄹ f
이제 input_han
을 변환해보자.
input_han = "ㅛㄷㄴㅅㄷㄱㅇ묘, ㅜㄷㅈ ㅛㅐ가 ㅈㅁㄴ ㅇㅁ가."
input_han_dt =
data.table(
key_han = unlist(strsplit(
convertHangulStringToJamos(input_han),
"")))
output_eng = dt_han_eng[input_han_dt, on='key_han']
# 그리고 마지막으로 한글이 아닌 부분은 그대로 보존한다.
output_eng[is.na(key_eng), key_eng:=key_han]
paste0(output_eng$key_eng, collapse='')
## [1] "yesterday, new york was dark."
성공적이다. stri_trans_nfd('...')
의 결과에 대해서도
비슷한 방법으로 변환하는 함수를 만들 수 있다.
그런데! 패키지 KoNLP
에는 비슷한 기능을 하는
convertHangulStringToKeyStrokes()
라는 함수가 있다. 이를
활용하면 다음과 같이 결과를 얻을 수 있다.
paste0(
convertHangulStringToKeyStrokes(input_han, isFullwidth=FALSE),
collapse = '')
## [1] "yesterday, new york was dark."
성능 비교
이 방법의 성능을 비교해보자.
library(microbenchmark)
mbm <- microbenchmark("using_data_table"={
input_han = "ㅛㄷㄴㅅㄷㄱㅇ묘, ㅜㄷㅈ ㅛㅐ가 ㅈㅁㄴ ㅇㅁ가."
input_han_dt =
data.table(
key_han = unlist(strsplit(
convertHangulStringToJamos(input_han),
"")))
output_eng = dt_han_eng[input_han_dt, on='key_han']
# 그리고 마지막으로 한글이 아닌 부분은 그대로 보존한다.
output_eng[is.na(key_eng), key_eng:=key_han]
result1 = paste0(output_eng$key_eng, collapse='')},
"using_KoNLP"={result2 = paste0(
convertHangulStringToKeyStrokes(input_han, isFullwidth=FALSE),
collapse = '')
})
result1 == result2
## [1] TRUE
library(ggplot2)
autoplot(mbm)
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
입력값 등에 따라 달라질 수 있지만 위의 예를 보면
KoNLP::convertHangulStringToKeyStrokes()
를 사용하는 게 훨씬
빠르다.
함수convertHangulStringToKeyStrokes()
를 살펴보자.
convertHangulStringToKeyStrokes
## function (hangul, isFullwidth = TRUE)
## {
## if (!is.character(hangul) | nchar(hangul) == 0) {
## stop("Input must be legitimate character!")
## }
## else {
## keystrokes <- .jcall("kr/pe/freesearch/korean/KoHangul",
## "S", "convertHangulStringToKeyStrokes", hangul, isFullwidth,
## TRUE)
## Encoding(keystrokes) <- "UTF-8"
## return(unlist(strsplit(keystrokes, intToUtf8(65372))))
## }
## }
## <bytecode: 0x000000000e750548>
## <environment: namespace:KoNLP>
.jcall()
을 보니 아마도 java로 작성된 듯 하다.
마무리
이번 포스트에서 한글로 잘못 작성된 영문을 한글로 변환하는 방법에 대해
알아봤다. use
KoNLP::covertHangulStringToKeyStrokes( , isFullwithd=FALSE)
!
이를 함수로 wrap한 후에 rstudio의 addin으로 추가하여 좀더 편리하게
사용할 수도 있을 것이다.
// add bootstrap table styles to pandoc tables function bootstrapStylePandocTables() { $('tr.odd').parent('tbody').parent('table').addClass('table table-condensed'); } $(document).ready(function () { bootstrapStylePandocTables(); });
Leave a comment