위키백과의 BMP페이지에서…

Basic Multilingual Plane 페이지를 보다가 나온 대목:

Sometimes, the terms “astral plane” and “astral characters” are used informally to refer to the planes above the Basic Multilingual Plane (planes 1–16) and their characters.

(강조는 내가 넣은 것)

아스트랄 플레인이라니!! 사실 슬레이어즈는 유니코드 문자셋의 앞쪽 코드 포인트들이 뒷쪽 코드 포인트(아스트랄 플레인)를 사용한 암투인 것.

statpress 에 Naver 검색 추가하기

WordPress 용 플러그인 중에 statpress 란 녀석이 있다.

이 플러그인이 블로그에 방문한 사용자가 검색 엔진을 통해서 들어왔는지, 그렇다면 어떤 검색어로 들어왔는지 추적해준다. 그리고 apache log를 직접 해석해서나 가능한 일인, 방문자(정확히는 IP지만) 별, 진입 경로, 계속해서 본 페이지 등도 추적하게 해준다. 여튼 이 작업 때문에 아래와 같은 일을 했다.

이제부터 모든 경로는 플러그인 설치 디렉토리 기준이다.[1]

우선 검색 엔진 정의 파일이 statpress/searchengines.dat 에 있는데 여기에 다음 두 줄을 추가 해준다.

Naver|cafeblog.search.naver.|query|
Naver|search.naver.|query|

그리고 statpress/statpress.php를 다음처럼 수정해준다.

# statpress.php의 1046번째 줄에 추가
function iriIsNaverEncodingUTF8( $vars )
{
    $= count($vars);
    while$i )
    {
        $tab = explode( ‘=’$vars[$i] );
        if$tab[0== ‘ie’ )
            if$tab[1== ‘utf8’ )
                return true;
            else
                return false;
    }
    return false;
}

# statpress.php의 1058번째 줄 수정
if($tab[0== $key){
    if$nome == ‘Naver’ )
     { // naver?
         if( iriIsNaverEncodingUTF8( $variables ) )
             return ($nome.‘|’.urldecode($tab[1]));
         else
             return ($nome.‘|’.iconv( ‘EUC-KR’‘UTF-8’,  urldecode($tab[1]) ) );
     }
    else
        return ($nome.“|”.urldecode($tab[1]));
}

하는 일을 따지자면,

  • referrer url 을 받았을 때, 이를 검색 엔진이름에 해당하는지 분석하는데, (def/searchengines.dat 기준)
  • 여기 추가한 Naver 관련 항목을 이용하고,
  • Naver 일 땐 검색어 인코딩이 UTF-8 인지 혹은 UHC(or EUC-KR 여튼 legacy 한지 확인)인지 검사하고,
  • UTF-8 이면 그냥 원래 statpress 코드를, 그렇지 않으면 EUC-KR을 UTF-8으로 변경한다

정도?

이 수정 작업을 마치고 나면 네이버에서 검색해들어온 결과가 깨지지 않게 된다.

ps. 이 작업을 처음 했을 때 – 밤 중에 애가 배고프다고 울 때, 아내가 젖주는 동안 노트북으로 코딩했다 – 수정이 다 끝났는데도 결과가 그대로라서 삽질했다. 원인은, 위 코드를 잘 보면 알겠지만 $nome 이라는 변수가 있는데, 난 이걸 $name 으로 보고 코딩한 것. 확인해보니 플러그인 제작한 이가 이탈리아 인이었고, 이탈리아 어의 name에 해당하는 단어가 nome 인 것. Orz.

밤 중의 흐릿하게 보이는 화면에서 nome과 name을 어찌 구분하나요 ㅠㅠ.

  1. wordpress_설치_디렉토리/wp-contents/plugins/ 가 됨 []

서버 설치 기록 post-mortem

밑 포스팅에 정리해보면서 생각한거지만 참 문제가 많다.

1. DB 이전할 때 인코딩 문제

Linux 쪽에서 쓸 때는 금방 눈치 챘는데, table 문자집합이 latin1이다. 아니 왜 이게 기본값이야 Orz.

그래서 mysqldump 한 값이 엉망이길래, 구글링을 좀 하고 이런걸 찾았다 – mantis upgrade

이걸 참조해서

mysqldump -u root -p --default-character-set=latin1 -–skip-set-charset bitnami_redmine

로 백업했는데, skip-set-charset 이 MySQL이 기본적으로 삽입하는 SET NAMES 문자집합 구문을 빼버리기 때문에 문자열을 해석안하고 그대로 덤프해서 latin1인양 들어가있던 UTF-8 문자열들이 살아서(?) 나온 듯 싶다.

2. 빌드 서버 설정 문제

CC.net 띄우는것 까진 문제가 없었는데, 이게 빌드하고 테스트 케이스 실행하는 과정에서 발생한 문제가 한 두 개가 아니었다.

일단 바로 해결할 수 있는건 그다지 없어서 문서화 잘 해놓는걸로 끝내긴했는데, 이거 참 귀찮은 일이로세.

빌드하는 수준에서는 어떻게 될 것 같은데, 테스트 케이스를 실행하는 단계가 되면 문제가 좀 복잡한듯 Orz

3. 가상화 서버 성능 문제

intel xeon x5560이라는 상당히 훌륭한(…) CPU가 할당되서 좋아했는데 성능은 생각만큼이 아니다. 가상화 오버헤드도 있긴하겠지만, 그 보단 충분한 수의 코어가 할당이 안된듯? 아마 코어가 하나 할당되고, 이게 HT라서 두 개의 CPU 가 잡히는 것 같지만 이걸로는 부족하다(?).

이전 빌드 서버가 intel core2duo (conroe) 6600이었는데 (메모리는 이쪽이 4G이긴 했지만), 여기서 대략 20분 정도 걸리던 빌드가 21 분 수준으로 걸린다. 반면에 실행 속도는 거의 1.5~2.2배 정도씩 빨라졌더라.

뭐가 문젠가 하고 /MP 옵션이 없는 것만 빌드해봤는데[1] 1.7배씩 빠른 정도로 나오더라. 아무래도 물리 코어가 2개가 아니라 HT 코어 2개로 빌드해서 생기는 문제인듯? 밑단에서 어떤 가상화를 쓰는지 모르지만 코어 2개가 할당이 안되면(xeon x5560은 물리코어가 4, 각각에서 HT해서 총 8개 코어다) 좀 괴로울듯. 아니 기왕 딴 서버들 성능도 그닥 안 중요한데(???) 물리 코어 3개 할당 좀 해주시면 (도망간다).

일단 요청은 했는데 금요일 밤에야 한거라 월요일에나 응답을 받을듯. 잘 되었으면 좋겠는데;

아마 CPU 부하 최대로 걸릴 녀석은 이 놈인듯하니(빌드서버가 좀…) 얘만 성능 잘 나와주면 다른 부하 적게 걸리는 공용 써버들도 다 가상화 서버 위로 옮겨도 될 것 같은데.

  1. VS에서 빌드할 때 모든 코어를 최대한 써서 빌드하는 옵션 []

Redmine 설정 중의 뻘짓

Windows 에서 BitNami 스택으로 설치하는데 DB 설정 실패

이 녀석이 설치 후에(Apache/MySQL/Rails/Ruby/Redmine을 설치한다), MySQL에 테이블을 만드는데, 설치 디렉토리에 공백(…)이 있으면 테이블을 만들지 못한다. 

덕분에 지우고 재설치 ㄳ.

상대적으로 나머지 설정은 금방 끝났다.

 

*nix 에서 한글이 깨지다

한글이 뭔가 제대로 안나와서 원인을 분석해보니 — 마일스톤(=Redmine에선 버젼이라고 부른다)을 만들다 알게되었다 — MySQL 테이블의 collation이 latin1이더군 -_-;; MySQL 에서 테이블을 생성할 때 collation 을 강제할 수 있는데 이걸 쓰지 않은 모양? 이거 수정하고나니 제대로 쓸 수 있게 되었다.

ps. MySQL에서 DB 테이블 만들 때 다음과 같이 하면 특정 collation이 강제된다. 여기선 UTF-8으로 강제한 WordPress 사용자 테이블 생성 코드다.

CREATE TABLE `wp_users` (
`ID` bigint(20) unsigned NOT NULL auto_increment,
`user_login` varchar(60) character set latin1 NOT NULL default '',
`user_pass` varchar(64) character set latin1 NOT NULL default '',
`user_nicename` varchar(50) character set latin1 NOT NULL default '',
`user_email` varchar(100) character set latin1 NOT NULL default '',
`user_url` varchar(100) character set latin1 NOT NULL default '',
`user_registered` datetime NOT NULL default '0000-00-00 00:00:00',
`user_activation_key` varchar(60) character set latin1 NOT NULL default '',
`user_status` int(11) NOT NULL default '0',
`display_name` varchar(250) character set latin1 NOT NULL default '',
PRIMARY KEY (`ID`),
KEY `user_login_key` (`user_login`),
KEY `user_nicename` (`user_nicename`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

마지막 부분의 DEFAULT CHARSET=??? COLLATE=??? 에 대해서 원하는 설정을 쓰면된다.

텍스트 필터링용 trie 구현하기

자료구조 시간에 배우게 되는 자료 구조 중 가장 특이한 애를 꼽자면 – 적어도 내 경우엔 – 해쉬 테이블과 트라이(trie) 였다. n-ary tree 처럼 “비교”라는 직관적인 방식을 쓰는게 아니라서 그렇게 느꼈던듯 싶다.

지난 주 후반~오늘까지 텍스트 필터링을 만들면서 이 trie의 일종을 구현하고 테스트했다. 그 와중의 기억을 정리해보는 의미로 여기에 덤프.

필요한 스펙은 대충,

  • 가능한 범위 내에서 속도가 빠를 것
  • 메모리를 적게 쓸 것
  • longest-prefix matching; LPM 이 가능할 것
  • Unicode (UCS2 or UTF-16LE)를 지원할 것
  • 가능하면 MBCS/UTF-8[1] 를 지원하거나 지원하기 쉬울 것

정도. 덤으로 상황(환경?)은 대충 다음과 같다.

  • 전처리 시간은 따로 뺄 수 있다
  • 전체 데이터는 메모리에 올릴 수 있는 수준(disk-access 이런거 없음)

그리고 후보였던 자료 구조는 3가진데,

  1. (기존에 있던) map/binary-tree 기반의 자료구조
  2. trie 기반의 자료구조
  3. hash-table 기반의 자료구조

이 중에 기존에 있던 녀석은 메모리 사용량은 그렇다치고(데이터 중복이 많은 편이라 낮지는 않음), 동작 속도의 worst-case가 눈에 띄게 높아서 –_- 일단 보류하고 나머지를 좀 생각했다. hash-table의 경우 imperfect-hashing(=key collison이 있는 경우) 은 trie 보다 느릴 수 있는데, 이번엔 전처리 해도 무방해서 perfect-hashing을 만들어낼 수 있다. 하지만 LPM엔 부적합해서 일단 trie를 쓰기로 했다.

Trie 는 비교를 이용하지 않는다 는 면에서 효율성이 나오는데, 대신 공간은 매우 비효율적으로 쓴다. 노드 자체에 어떤 데이터가 있는지 기록하는 형태가 아니라, 노드 간의 연결이 있고/없음에 데이터의 일부가 기록되는 형태라서, 없는 데이터를 표현하는 공간의 낭비가 심하다. 예를 들어 char 기반의 문자열들을 저장한다고 했을 때, 데이터가 a로 시작하는 단어 100개와 b로 시작하는 단어 100개가 있는 집합이라면, 최상위 노드(root)에서 a, b 항목을 뺀 나머지 공간(적어도 254개 정도)은 쓸모가 없어진다.

 

Continue reading “텍스트 필터링용 trie 구현하기”

  1. UTF-8 도 MBCS의 일종이라 해도 되지만, Win32에서 MBCS라고 하면 보통 한 글자에 최대 2바이트를 쓰는 DBCS 류의 문자집합을 의미해서 구분 []

trac wiki 변환 삽질기

모종의 번역 작업을 소스코드 형상 관리도구(SCM)인 trac을 사용해서 진행했다.

오늘 기나긴[1] 회사 업무의 막바지에 틈이 생긴지라, trac을 전달 가능하다고 생각하는 형태 — 독립된 HTML 페이지라거나, RTF 형태라거나, PDF라거나, MS Word 형식이라거나 — 로 바꾸는 작업을 진행했다.

 

처음으로 trac을 썼었던 0.9 버젼 때만 하더라도 text/rtf 로 변환하는 옵션이 있었던 것 같은데! 막상 오늘 하려고보니 (현재 trac 0.11stable 사용 중) 없-_-더-_-라.

뭐 별 수 있나하고 trac-hacks를 뒤지기 시작.  export 태그 밑에 적당한게 몇 개 있길래 시도해보기 시작.
우선 MSWord 포맷으로 바꿔준다는 PageToDocIntegration 을 써봤다. 빌드 -> site-package로 설치.[2] 
페이지 밑에 text 말고도 MSWord 다운로드가 추가되어 .zip 으로 묶인 MSWord doc 파일…로 보이는 것으로 변환했다. 하지만 한글로 된 부분이,

??? ??? ?? ??? ???? ??? ??

처럼 깨져서 나온다. orz 

 

이건 버리고 다음으로 PDF 포맷으로 만들어주는 TrackWikiToPdfPluginPageToPdfPlugin을 시도. 이 두 플러그인은 htmldoc 이 설치되어 있어야 한다. 하지만 이 플러그인으로 만들어낸  PDF 페이지도 한글로 된 부분이,

??? ??? ?? ??? ???? ??? ??

로 깨진다 ㅠㅠ htmldoc 자체가 multi-byte 인코딩된 문자집합을 제대로 지원하지 않는 것 -_-;

 

마지막으로 docbook을 시도. Page2DocbookPlugin 이란 녀석을 설치. 출력이 xml[3] 이고, UTF-8으로 인코딩된 유니코드를 사용하기 때문에 이 녀석은 아무 문제가 없더군. 다만 특정 페이지에서 가끔 500 internal error를 내면서 죽어버리는데(…), 열고 공백문자 하나쯤 집어넣고 다시 저장한 후 변환하면 잘 되더라…

이걸활용해서docbook 변환 -> docbook2rtf 로 RTF 문서로 바꾸는 스크립트를 짜고, 자동변환이 이루어지게 한 뒤 작업 끝. 이제 손으로 수정할만한 부분만 수정해서 보내면 될듯하다?

 

근데 생각할 점. Chapter 제목이 wiki 안에서의 URI에 근거한다. 덕분에 chapter 제목이 두 번 나온다거나 하는 일도 좀 생기고 그렇다.[4]

+ 번역 작업 자체를 아예 docbook으로 하는게 편했을지도 모른다는 생각이 든다. orz
여튼 오늘의 교훈. 다국어를 잘 지원할 것 같은 포맷 — 여기선 UTF-8 인코딩을 지원하는 xml — 을 사용한 툴들은 다국어/문자집합/인코딩 문제를 잘 안일으킨다. 이런 툴을 우선적으로 고려하는 습관?을 들여야 할 듯.

  1. 근 3주간 하루 쉬었음 orz 지난 주말의 연휴는 3연짱 야근 ㄳ []
  2. 덤으로 Gnome XSLT 라이브러리, HTML Tidy 가 있어야 한다 []
  3. 물론 docbook이니 SGML일 수도 있지만 적어도 이 경우엔 … []
  4. 문단 시작이 == 제목 == 하는 식의 챕터 제목인 경우 []

WinZip 의 Unicode 지원 시작

미카엘 카플란[1] 의 블로그에서 WinZip이 Unicode를 지원하기 시작–할 예정(?) — 한다는 포스팅을 읽었다.

흔한 현존하는 압축 파일 포맷 중 가장 흔한 것은 .zip, .rar, .gz, .bz2, .7z 정도이다. 이 중에서 .bz2와 .gz 는 단일 파일을 압축하기 위한 것이라 다국어 지원을 좀 빼고 생각하면[2] , archiving 기능이 있는 .zip, .rar, .7z 중에 공식적으로 Unicode 를 지원하는 것은 .7z 와 .rar 뿐이었다.

예전에 .zip 과 .rar 을 비교하면서, .zip 도 RAR 흉내를 내면 상대적으로 손쉽게 유니코드를 지원할 수 있을 것 같다고 말했었는데 완전히 같은 방식은 아니겠지만[3] , 유니코드 지원이 없어서 다른 코드 페이지의 MS Windows 혹은 다른 인코딩의 *nix-like OS 에서 묶어온 파일들의 압축을 풀 때마다 괴로워했던 일이 이젠 없어지게 될지도 모른다.그리고 미카엘 카플란이 저런 포스팅을 하는 걸로 봐서 MS Windows 의 내장 ZIP 폴더 기능에 Unicode 지원이 추가될지도 모르고.

여튼 이런 식으로 각 언어가 얽혀 돌아가는 세상에서 — 특히 Web — 사실상 마지막 남은 주요 압축 파일 포맷인 ZIP에도 Unicode 지원이 추가되기 시작했으니,[4] 컴퓨터 쓰기가 조금은 편해지지 않을까 한다.

ps. Berryz WebShare 팀에서는 이런 문제 때문에 다른 인코딩 — 특히 동아시아 권의 인코딩 — 해석을 위해서 UniZip 이라는 압축했을 때의 인코딩을 지정해서 압축을 해제하게 해주는 툴을 제공한다. 또한 Berryz WebShare 에서 파일 여러 개를 묶어서 받을 때에는 Unicode 지원이 내장된 RAR 포맷을 사용하게 권고(?)하기도 하고…

  1. Microsoft의 엔지니어로 International Fundamentals 팀을 이끌고 있다; Unicode 작업의 초기부터 참여해온 사람이기도 하다 []
  2. *nix 문화에서 중복 기능을 새로 구현하는 일은 지양된다. 그래서 tar로 묶고 gzip or bzip2 로 압축한다! 하는 식의 방식을 사용한다 []
  3. 혹 비슷할지도 모르지만 그건 실제로 공개되면 압축파일의 바이너리를 까보고(…) 생각하겠다 []
  4. 물론 WinZip 자체가 Unicode 지원을 시작한다고 *nix 계열의 InfoZip 같은 녀석에 바로 추가되지는 않겠지만, 이런 경우 사용자가 일정 수를 넘어서면 서로서로 해당 기능을 포팅하게 될 것이다. 그리곤 모두(?)가 사용할 수 있게 될 것이라고 생각한다. []

Windows Character Encoding: UCS2? UTF-16?

예전에 문자집합;character-set 과 인코딩에 관해서 일련의 포스팅을 했다(#1, #2, #3). 그런데 어제부터 Windows via C/C++ 이란 책을 읽고있는데, 내가 잘못 알고 있던 부분을 발견해서 이전 포스팅에 대한 정정 포스팅.

Windows에서는 UCS-2 인코딩을 사용한다

라고 했었는데 사실이 아닙니다.

Windows는 2000과 그 이후의 버젼들에서 모두 UTF-16 기반으로 동작하며, 그 이전의 NT 커널들만 UCS-2 기반.

여튼 그런 연유로 저 둘을 다시 부연 설명하자면,

  • 둘 다 기본적으론 글자당 2bytes 를 할당하는 형태
  • UCS-2는 글자당 무조건 2bytes만 쓴다. 그보다 길게는 쓸 수 없는 고정길이 인코딩. BMP[1] 만 표현.
  • UTF-16은 그 이상[2] 을 표현할 수 있는 방법 — surrogate pair라는 것을 쓴다. 자세한 것은 다음 기회에 포스팅 거리로(…) — 을 제공한다. 그래서 한 글자의 길이가 4 bytes가 되는 경우가 존재한다.

정도다. Windows 에서는 UTF-16에 기반한 Unicode를 지원하며, SQL 서버들과는 좀 다르다. MS SQL서버는 내가 아는 범위에선 아직 UCS-2 위주로 동작한다. (정확히 아시는 분이 가르쳐주시면 좋겠다)

ps. 제목과는 좀 연관없는 얘기지만, Windows via C/C++ 에서 앍게된 사실 하나 더.

Windows Vista 이후의 OS는 완전히 UTF-16 모드로 동작하게 되었고, 소위 ANSI 함수들은 래퍼로만 남게 되었다. CreateWindowA, 처럼 끝에 A가 붙는 함수들[3] 의 경우에는 ANSI문자열(=MBCS 문자열)을 받아들이는 함수였는데, 이젠 정말로 래퍼 수준으로 전락해서 단순히 문자열 인자를 UTF-16 인코딩으로 변환해서 Windows System Call이나 API 함수를 호출하는 역할을 하게 되었다.

Windows Vista / Server 2008 성능을 원한다면 UTF-16 (=wide character API) 를 쓰라는 것.

  1. U+0000~U+FFFF 까지의 영역만 표현할 수 있다. 가장 기본적인 문자집합에 해당하며, 이런 부분만 표현하기 때문에 UCS-2는 고정길이가 가능해진다. 그리고 고정길이이기 때문에 Database 처럼 문자열 처리 속도가 아주 빨라야하는 응용에서는 꽤 널리 사용된다. []
  2. plane 0인 BMP말고 그 이상의 것(plane 1~15)들 까지 []
  3. 특히 CreateWindowW처럼 W로 끝나는 소위 Wide 함수들과 대응해서 []