[R] 3판 맛보기) 유니코드 문자열
유니코드(Unicode) 문자열
Sumeun DS
2022년 2월 28일
인코딩
컴퓨터는 전기 신호의 있고 없음 또는 1과 0만으로 모든 데이터를 표현합니다. 1과 0을 여러 개 연결하면 큰 수를 표현할 수 있습니다. 만약 문자를 표현하고 싶다면 수와 문자를 연결하는 방법을 미리 정해놔야 합니다. 이런 약속을 인코딩 방법이라고 합니다.
인코딩 방법은 표현하고자 하는 문자(한글, 알파벳, 한자 등)와 언어, 나라에 따라 달라집니다. 현재 인코딩 방법의 방법은 1000개를 훌쩍 넘는 것으로 알려져 있습니다. 하지만 인테넷 웹에서 사용되는 인코딩은 90% 이상이 UTF-8이라고 합니다.1
유니코드
인터넷의 폭발적 성장 이후 국제적으로 문서를 교환하는 일이 빈번해졌습니다. 하지만 인코딩 방법은 자신 (그리고 주변 국가의) 언어만을 고려하여 만들었기 때문에 한 나라에서 생성한 문서를 다른 나라로 전송하면 내용을 알 수 없게 깨지는 경우가 다반사였습니다. 이런 불편함을 없애기 위해 유니코드가 개발되었습니다. 유니코드란 세상의 (거의) 모든 문자와 기호를 숫자에 대응시키는 방법입니다. 유니코드 콘소시엄(Unicode Consortium)이 개발하고 있으며 하나의 문자에 대응하는 숫자를 코드 포인트(code point)라고 합니다. 구체적인 문자 또는 기호의 대응은 유니코드 코드 차트에서 확인할 수 있습니다. 유니코드에 할당되는 문자는 버전이 증가함에 따라 계속 증가하고 있습니다.2 1991년 버전 1.0.0은 7129개의 문자(24개의 문자체계)를 지원했고, 2021년 9월 발표된 버전 14.0에서는 총 144,697개의 문자(159개의 문자체계)를 지원한다. 한글은 버전 1.0.0에서부터 포함되어 버전 5.2에서는 옛한글까지 포함하여 모든 한글을 표현할 수 있게 되었습니다. 버전 6.0(2010년)에는 이모지(emoji)와 같은 그림 문자도 포함하게 됩니다. 이는 핸드폰 생산자들의 요구를 받아들인 것으로 이 역시 나라마다 각자의 이모지 인코딩을 만들어 서로 호환되지 않는 상황을 예방했다고 볼 수 있습니다. (사실 유니코드에 이모지가 등록되기 전에는 생산자마다 각자의 이모지 인코딩을 사용했습니다.) 유니코드는 이렇게 세상의 모든 문자를 포함하였기 때문에 세상의 다양한 인코딩은 유니코드로 변환될 수 있습니다. 유니코드는 UTF-8, UTF-16, UTF-32와 같은 인코딩 방법으로 구체화됩니다.
R의 문자열
R은 유니코드 기반의 UTF-8을 지원하고 있습니다. 만약 UTF-8이 아니라면 다음과 같은 방법을 통해 UTF-8 문자열로 변환할 수 있습니다.
x = '한글'
y = iconv(x, to='UTF-8')
x
## [1] "한글"
y
## [1] "한글"
Encoding(x)
## [1] "unknown"
Encoding(y)
## [1] "UTF-8"
그런데 UTF-8은 세상의 모든 문자를 표현할 수 있을만큼 강력하지만 컴퓨터가 UTF-8의 모든 문자를 지원하지 못할 수도 있습니다. 예를 들어 윈도우즈에서 사용하는 폰트는 지원하는 글자 갯수에 한계가 있기 때문에 하나의 폰트로 유니코드의 모든 문자를 지원하지 못합니다. 따라서 폰트에서 지원하지 못하는 문자는 화면에서 제대로 확인하기 어렵습니다.
그런 경우를 대비해서 다음과 같은 UTF-8 문자열 함수를 제안합니다.
u_chars
다음의 함수 u_chars()
는 패키지 Unicode
를 사용하여 UTF-8 문자열의 각 문자 하나 하나에 대한 정보를 제공합니다. 특히 유니코드는 문자마다 레이블(label)을 붙어 있어 화면에 제대로 표시되지 않는 문자에 대해서도 어떤 문자인지 확인할 수 있습니다.
u_chars = function(s, encodings) {
stopifnot(class(s) == "character")
stopifnot(length(s)==1)
if (Encoding(s) == "unknown") {
s = iconv(s, to = 'UTF-8')
} else if (Encoding(s) != 'UTF-8') {
s = iconv(s, from = Encoding(s), to='UTF-8') }
dat = data.frame(ch = unlist(strsplit(s, ""))) # 글자 단위로 분리
cps = sapply(dat$ch, utf8ToInt) # unicode codepoint
cps_hex = sprintf("%02x", cps) # 16진수로 변환
# 16진수 표현 방법 " ..ff", " a1ff", "011f3e"
# 처음 두 자리수는 거의 사용되지 않으므로 0일 때 공란으로 표시
# 다음 두 자리수는 ASCII의 경우 사용되지 않으므로 0일 때 .으로 표시
cps_hex =
ifelse(nchar(cps_hex) > 2,
stringi::stri_pad(cps_hex, width = 4, side = 'left', pad = '0'),
stringi::stri_pad(cps_hex, width = 4, side = 'left', pad = '.'))
dat$codepoint =
ifelse(nchar(cps_hex) > 4,
stringi::stri_pad(cps_hex, width=6, side='left', pad='0'),
stringi::stri_pad(cps_hex, width=6, side='left', pad=' '))
# encodings가 주어졌다면, 주어진 인코딩으로 변환 결과
if (!missing(encodings)) {
for (encoding in encodings) {
ch_enc = vector(mode='character', length=nrow(dat))
for (i in 1:nrow(dat)) {
ch = dat$ch[i]
ch_enc[i] =
paste0(sprintf("%02x",
as.integer(unlist(
iconv(ch, from = 'UTF-8',
to=encoding, toRaw=TRUE)))),
collapse = ' ')
}
dat$enc = ch_enc
names(dat)[length(names(dat))] = paste0('enc.', encoding)
}
}
dat$label = Unicode::u_char_label(cps);
dat
}
u_chars("\ufeff\u0041\ub098\u2211\U00010384")
## ch codepoint label
## 1 <U+FEFF> feff ZERO WIDTH NO-BREAK SPACE
## 2 A ..41 LATIN CAPITAL LETTER A
## 3 나 b098 HANGUL SYLLABLE NA
## 4 ∑ 2211 N-ARY SUMMATION
## 5 <U+00010384> 010384 UGARITIC LETTER DELTA
만약 다른 인코딩 방법으로 어떻게 인코딩이 되는지를 확인하려면 encodings =
에 인코딩 방법을 적어 넣습니다.
u_chars("\ufeff\u0041똠\u2211\U00010384", encodings = c("CP949", "latin1"))
## ch codepoint enc.CP949 enc.latin1 label
## 1 <U+FEFF> feff ZERO WIDTH NO-BREAK SPACE
## 2 A ..41 41 41 LATIN CAPITAL LETTER A
## 3 똠 b620 8c 63 HANGUL SYLLABLE DDOM
## 4 ∑ 2211 a2 b2 N-ARY SUMMATION
## 5 <U+00010384> 010384 UGARITIC LETTER DELTA
활용예
library(stringi)
x = '한글'
x = iconv(x, to='UTF-8')
cat(x); cat('\n')
## 한글
y = stri_trans_nfd(x)
cat(y); cat('\n')
## ㅎㅏㄴㄱㅡㄹ
u_chars(x)
## ch codepoint label
## 1 한 d55c HANGUL SYLLABLE HAN
## 2 글 ae00 HANGUL SYLLABLE GEUL
u_chars(y)
## ch codepoint label
## 1 ㅎ 1112 HANGUL CHOSEONG HIEUH
## 2 ㅏ 1161 HANGUL JUNGSEONG A
## 3 ㄴ 11ab HANGUL JONGSEONG NIEUN
## 4 ㄱ 1100 HANGUL CHOSEONG KIYEOK
## 5 ㅡ 1173 HANGUL JUNGSEONG EU
## 6 ㄹ 11af HANGUL JONGSEONG RIEUL
- 출처 : https://w3techs.com/technologies/cross/character_encoding/ranking↩︎
- 유니코드에 포함되는 문자는 여러 가지 이유로 증가하고 있습니다. 예를 들어 옛한글 자음과 모음의 경우에도 새롭게 발견되는 경우가 있다.↩︎
// add bootstrap table styles to pandoc tables function bootstrapStylePandocTables() { $('tr.odd').parent('tbody').parent('table').addClass('table table-condensed'); } $(document).ready(function () { bootstrapStylePandocTables(); });
출처> R로 하는 빅데이터 분석: 데이터 전처리와 시각화
Leave a comment