Skip to main content

Unicode와 Encoding에 대해 몰랐던 사실들

요새 XML parser를 만들어 본다고 encoding에 대해 좀 살펴보고 있는데 제가 그동안 알아왔던 것들과 많이 차이가 나네요. 사실 그동안 십수년 프로그래밍을 하면서 I18n이나 L10n을 고려해 본적은 없었거든요. ((Internationalization (I18n), Localization (L10n) )) 대부분 UI가 없거나 UI가 있더라도 그다지 한글화같은 것을 고려하지 않았도 되었던 것이기도 했고요.

가장 오해하고 있었던 사실은 Unicode는 16bit 크기안에 다 들어갈 수 있다라고 알고 있던 것이었습니다. 그런데 살펴 보니 이것은 옛날 얘기고 현재는 plane-0에만 해당하는 범위네요. 이외에도 plane-1부터 plane-16까지 가질 수 있더군요. 물론 대부분의 현재 사용되는 문자들은 16bit안, 즉 plane-0에 들어간다고 하지만 제대로 Unicode를 fixed length string으로 지원하려고 하면 16bit char로는 불가능하다는 말이 되죠.

시험삼아 간단히 컴파일을 해보니 Windows XP에서 사용한 VC++ 2005 Express에서는 wchar_t가 16bit지만 Ubuntu의 gcc에서는 32bit네요.

원래 계획은 XML parser를 만들면서 기본적으로 wchar_t로 결과를 리턴하고 encoding이 ascii (utf-8의 subset) 인 경우 char로 결과를 리턴하도록 template class로 만들려고 했었는데 wchar_t가 16bit인 환경에서는 어째야 할지 고민이네요. 어차피 현재 사용되는 대부분의 문자들이 16bit안에 들어가므로 plane-0만 지원해도 될것 같기도 한데... 아니면 basic_string 대신 basic_string를 사용하는 방법을 사용해야 할런지... 이렇게 하려면 저장될때 byte ordering도 고려해야 하고 수많은 traits이나 function overloading을 새로 만들어 주어야 하겠죠. string literal 쓰기도 어렵겠네요. :-|

만들려는 XML parser에서는 최소 ascii, utf-8, utf-16의 encoding을 지원하려고 합니다. 이중 utf-8과 utf-16 encoding의 경우에는 wchar_t를 사용해서 결과를 리턴받도록 하고요. 어차피 utf-8, utf-16과 같이 variable length를 가지는 encoding의 열을 리턴하더라도 사용하려면 fixed length string으로 변환해야 할것 같아 미리 wchar_t의 string, 즉 wstring으로 변환해서 리턴해 준다는 목적입니다. 하지만 모든 경우 wchar_t을 사용하면 encoding이 ascii인 경우 공간의 낭비가 많아지므로 이 경우에 특별히 사용할 수 있도록 char 버전을 제공하고요.

제가 이쪽에 기초가 없다보니 아직 모르는게 많네요. 혹시 제가 잘못 알고 있거나 빠진 내용에 대해 알려주시면 고맙겠습니다. :-)

Comments

  1. 제가 생각하기에 제일 쉬운(!) 방법은.

    다른 인코딩이 들어오면 iconv같은걸 돌려서 죄다 utf-8혹은 wchat_t(16bit)로 바꿔서 처리해버리세요. ㅎㅎ utf-8이라면 내부 처리단위가 std::string이 될것이고, wchar_t라면 std::wstring이 되겠네요. libxml같은 경우도 죄다 utf-8으로 변환해서 처리하던걸로 기억한다는.. :)

    ReplyDelete
  2. iconv 라는 게 있는지도 몰랐네요. ㅎㅎ 그런데 utf-8로 encoding되었더라도 이 encoding된 char가 어차피 unicode이기 때문에 0x10FFFF 까지 저장할 수 있어야 해서 wstring이 되어야 할 것 같아요. 그리고 만약 encoding이 ascii라고 확신하더라도 ascii를 이용하여 $#10FFFF; 해버리면 결국 wstring이 필요할 것 같고... 그나마 VC++에서 wstring은 0xFFFF까지밖에 안되고... oTL

    결국 지금은 wstring-base로 pull parser를 만들어 보고 있습니다. 일단 pull parser가 완성되면 SAX, DOM은 이것으로 만들면 될 것 같아서요.

    암튼 encoding, decoding 다 구현하려고 맘먹고 있었는데 iconv 라이브러리를 사용하는게 좋겠네요. 이제 문제는 input이 왔을때 이 input이 어떤 encoding인지를 검출하는 방법을 알아내는 것이네요. 좋은 라이브러리 알려주셔서 고맙습니다. :-)

    ReplyDelete
  3. 저도 iconv를 쓸 것 같은데요. utf-8라면 한글 "자르기"와 "글자수 세는 것" 외에는 그냥 아스키 쓰듯 string 쓸 것 같습니다. utf-8을 만든 이유도 기존 프로그램의 변경없이 쓸려고 만든 거니깐요. 작업물에서 한글을 제대로 자르는 것이나 글자수를 정확히 세는 것이 중요한가요?

    ReplyDelete
  4. 꼭 제대로 자르거나 글자수 세는게 문제가 아니더라도 utf-8 string가지고는 출력을 할수 없지 않나요? 결국 unicode를 사용해서 출력하려고 해도 decoding을 해야 할것 같아요. 물론 전체 utf-8 문서가 ascii만 가지고 있다면 모르겠지만요.
    아마 기존 프로그램의 변경 없이라는 얘기는 기존 ascii 문서의 변경없이라는 얘기가 아닐까요?

    게다가 전부 ascii라도 &12345; 같은 unicode 문자를 그냥 저렇게 사용하지는 않을 것 같고 하나의 문자로 나타내야 하는데 이런 것까지 고려한다면 결국은 ascii 포함해서 모든 encoding이 fixed length로 변환되려면 최소 21bit(전체 unicode)가 필요하지 않을까 생각중입니다. 물론 16bit만 있어도 현재 사용되는 모든 문자는 가능하다고 하니 이정도만 있어도 될것 같고요.

    2000 이전의 Windows NT 버전들을 이 16bit범위의 unicode만 지원했다고 하네요. 현재는 utf-16 지원.

    그리고 iconv외에 icu라는 ibm에서 시작한 라이브러리가 있군요. 좀 덩치가 큰것 같긴 하지만 살펴보고 있습니다. :-)

    ReplyDelete
  5. 윈도우즈는 utf-8을 표준 입출력으로 뿌려서 인식이 안되나요? 저는 리눅스나 BSD에서 할때는 utf-8 로케일에서 그대로 utf-8 string을 뿌려버립니다. 웹 프로그래밍할때도 utf-8 로케일이기 때문에 그냥 뿌립니다.

    만약에 윈도우즈가 표준입출력에 대한 로케일을 utf-8로 할 수 없다면 디코딩할 수 밖에 없겠네요.

    ReplyDelete
  6. 네. utf-8 인코딩을 지원하는 환경이라면 그냥 사용하셔도 되겠죠. 그리고 웹 프로그래밍에서도 그냥 utf-8을 사용하면 브라우저가 이를 지원할테니 문제 없을거고요.
    하지만 윈도우즈는 native encoding이 utf-16이니 utf-8을 바로 사용할수는 없을 것 같네요.

    현재로써는 어느 encoding이든지 wchar_t를 사용하는 wstring을 사용해서 구현해 볼 생각입니다. :-)

    ReplyDelete
  7. 결론은 iconv를 써서 후다닥 특정 인코딩으로 강제한 뒤에 파싱 ㄱㄱ 이군요.

    :)

    ReplyDelete
  8. 네. 지금 만들고 있는 parser는 일단 wchar_t로 변환 후 파싱합니다. fixed width character가 traversing 하기에 편해서요. ;-)

    ReplyDelete

Post a Comment

Popular posts from this blog

1의 개수 세기

저도 간단한 알고리즘 문제 하나... :-)

어떤 수 n이 주어졌을때 1~n까지의 수를 쭈욱 썼을때 나오는 1의 개수를 구하는 문제입니다.

예를 들어 13이라는 수가 주어지면 1~13까지의 수 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13에서 1은 1, 10, 11, 12, 13에 나오며 그 개수는 6이 됩니다. 즉, f(13)=6.

원래 문제는 f(n)=n이 되는 1이 아닌 가장 작은 수를 구하는 문제인데 이 문제의 경우에는 처음부터 쭈욱 세어나가면 되기 때문에 간단히 다음과 같이 구현을 하면 됩니다. ((한가지 주의할 점은 이전에 찾았던 n-1값을 사용하지 않고 다시 처음부터 n까지 값을 계산하면 시간이 너무 많이 걸린다는 점입니다. 위의 코드에서는 static 변수를 사용하여 이전 값에 계속 더해나가는 방법을 사용했습니다.))


#include

int count1(int n)
{
static int cnt = 1; // not 0 because n starts from 2. see main.

while (n > 0) {
if ((n % 10) == 1) ++cnt;
n /= 10;
}

return cnt;
}

int main()
{
using namespace std;

int n = 2;

while (count1(n) != n) ++n;
cout << n << endl;
}


좀 재미가 없죠? 그래서 이번 문제는 어떤 수 n에 대해서 f(n)을 O(1)시간에 구하는 알고리즘을 만드는 것입니다. 관심있으신 분들은 한번 풀어보세요. 제가 만든 코드는 내일 올려보겠습니다.

C++ of the Day #9 - Boost.Python 사용하기 #1

Python은 가장 인기있는 interpret 언어중의 하나입니다. Python의 장점 중 하나는 C/C++ 모듈과 쉽게 연동할 수 있다는 점입니다. 물론 손으로 일일히 wrapper를 만드는 것은 손이 많이 가고 에러를 만들수 있는 작업이나 SWIG등과 같은 도구를 사용하면 쉽게 python 모듈을 만들 수 있습니다.

Boost.Python은 이런 SWIG와 같이 python 모듈을 쉽게 만들 수 있도록 도와주는 라이브러리로 순수 C++만을 사용한다는 점이 SWIG와 다른 점입니다. 그리고 개인적으로는 Boost 라이브러리에 포함되어 있는 것들이 왠지 좀 더 믿음직스러워서... :-)

이번 글에서는 Boost.Python 문서에 나와 있는 예제를 가지고 간단하게 python 모듈을 만드는 방법에 대해서 알아보겠습니다.

Requirements리눅스
이 글에서는 리눅스 환경에서의 사용 방법을 설명한다.Boost.Python 라이브러리 (1.33.1)
Boost 라이브러리를 다운로드받아 아래와 유사한 명령으로 라이브러리를 빌드한다.
bjam -sTOOLS=gcc -with-python install

bjam의 --prefix 옵션으로 라이브러리가 설치될 위치를 변경할 수 있다.Python 라이브러리 (2.4.3)
Python을 다운로드 받아 빌드하여 설치한다.
위의 경우와 유사하게 configure의 --prefix 옵션으로 설치될 위치를 변경할 수 있다.

Write C++ Code다음과 같이 코드를 작성한다.

// greet.cpp #include <stdexcept> char const* greet(unsigned x) { static char const* const msgs[] = { "hello", "Boost.Python", "world!" }; if (x > 2) throw std::range_error("greet: index out of range"…

Hello Wordpress, again.

한 두주일 정도 Textpattern을 사용해봤는데 다시 Wordpress로 돌아오기로 결정했습니다. 무엇보다 스킨 변경이 너무 복잡하고 사용자층이 Wordpress에 비해 너무 앏네요. 원하는 plugin도 찾기 어렵고... :-|

그동안 Textpattern에 썼던 글들은 모두 Wordpress로 옮겼습니다. 2개 있던 댓글도 옮겼는데 그중의 하난 제가 쓴... ;-)

애초에 wp-dokuwiki plugin이 무거워서 옮겼던 것이라 이 plugin은 설치를 안할 예정인데 몇가지 아쉬운 점이 있네요.

첫째는 code highlighting 기능인데 이 기능은 예전에 만들어 놨던 것을 조금 수정해서 쓰려고 준비중입니다. 두번째는 Footnote 기능인데 찾아보니 Footnotes 0.9 Plugin for WordPress 2.0.x라는게 있네요.

이정도면 비록 wiki syntax에 비할바는 아니지만 쓸만할 것 같습니다. :-)