뭘 이런걸..

Posted
Filed under Tech/프로그래밍
문서의 Charset 을 detecting 하는 library 로는 IBM 이 지원하는 International Components for Unicode (ICU Project) 의 ICU library 와 Mozilla Browser 에서 이용하는 Universal Chardet library 가 있습니다.

ICU 의 경우에는 charset detect 가 포함된지 꽤 되었음에도 불구하고, php 5.3 부터 기본 포함되는 intl extension 에는 이 기능이 들어가지 않고 있습니다. 그 외에도 pecl 이나 pear 의 icu library 관련 패키지들에도 이상하게 이 부분만 들어가지 않고 있군요.

그리고 좀 더 안습인 것은, Mozilla 의 Universal Chardet 의 경우에는 C#, java, python, ruby (python chardet 을 porting 했음) 등등이 지원하고 있음에도 불구하고, PHP 나 별도의 c/c++ library 로는 제공되지 않고 있습니다.

Mozilla Universal Charset
Nchardet for C#
jchardet for Java
chardet for python
rchardet for ruby (python clone
Encode-Detect-1.01 > Encode::Detect::Detector for perl

mozilla code 에 c++ 로 지원하고 있으니 이걸 포팅하면 되겠지 하고 쉽게 생각을 했는데, xpcom 구조를 알기 전에는 쉽게 뗄 수 있을 놈이 아니더군요.

이렇듯 저렇듯.. 나오기만 2여년을 기다리다가.. 귀찮아서 mod_chardet 이라는 php extension 으로 하나 만들어 보고 말았습니다. 일단 mod_chardet 은 기본은 ICU library 의 Charset detect 기능을 이용하고, 옵션으로 python chardet 이 설치가 되어 있으면 Python C API를 이용하여 python chardet 을 사용할 수 있도록 설계를 했습니다. Mozilla Universal chardet 이 PHP 만 없다는 것도 좀 그렇다는 생각이 들었고..

일단, ICU 와 Universal Chardet 의 성능 비교도 해 볼겸해서 돌려 보았는데, 역시 ICU 보다는 Universal Chardet 이 detect 능력이 좋더군요. 그리고 ICU 에서 detect 할 수 있는 charset 보다 Universal chardet 이 좀 더 많이 지원하는 까닭도 있었고요.

일단, python chardet homepage 에 있는 자료가 오래된 까닭에 다음의 환경에서 해당 자료를 다시 분석해 보았습니다. 테스트 환경으로는 다음과 같습니다.

Python 2.5
Python Chardet 1.0.1
PHP 5.2.6
ICU library 4.0.1

Python Chardet result:
google.cn {'confidence': 0.98999999999999999, 'encoding': 'GB2312'}
yahoo.jp {'confidence': 0.98999999999999999, 'encoding': 'utf-8'}
amazon.co.jp {'confidence': 1, 'encoding': 'SHIFT_JIS'}
pravda.ru {'confidence': 0.93312187961594417, 'encoding': 'windows-1251'}
auction.co.kr {'confidence': 0.56471064895612277, 'encoding': 'ISO-8859-2'}
haaretz.co.il {'confidence': 0.98999999999999999, 'encoding': 'windows-1255'}
www.nectec.or.th {'confidence': 0.77645629965698426, 'encoding': 'TIS-620'}
feedparser.org {'confidence': 0.98999999999999999, 'encoding': 'utf-8'}


python-chardet 1.0.1 의 경우, 홈페이지 자료와는 약간 다른 결과가 나오더군요. 일단 python chardet homepage 의 정보가 변경이 되었을 수도 있고, chardet 이 업데이트 되면서 결과가 달라질 수도 있겠지만, 일단 이 결과에서 auction 이 예전에는 제대로 EUC-KR로 판단이 되었는데, 지금 환경에서는 다른 결과를 보여 주고 있습니다.

다음은 mod_chardet 의 결과 입니다.

PHP mod_chardet result:

google.cn (7121)
ICU : Encoding -> GB18030 Confidence -> 100
MOZ : Encoding -> GB2312 Confidence -> 98
yahoo.jp (30367)
ICU : Encoding -> UTF-8 Confidence -> 100
MOZ : Encoding -> utf-8 Confidence -> 98
amazon.co.jp (166082)
ICU : Encoding -> Shift_JIS Confidence -> 100
MOZ : Encoding -> SHIFT_JIS Confidence -> 100
pravda.ru (97826)
ICU : Encoding -> ISO-8859-1 Confidence -> 28
MOZ : Encoding -> windows-1251 Confidence -> 93
auction.co.kr (101330)
ICU : Encoding -> EUC-KR Confidence -> 100
MOZ : Encoding -> ISO-8859-2 Confidence -> 56
haaretz.co.il (174179)
ICU : Encoding -> ISO-8859-1 Confidence -> 32
MOZ : Encoding -> windows-1255 Confidence -> 98
www.nectec.or.th (41527)
ICU : Encoding -> ISO-8859-1 Confidence -> 37
MOZ : Encoding -> TIS-620 Confidence -> 77
feedparser.org (28443)
ICU : Encoding -> UTF-8 Confidence -> 100
MOZ : Encoding -> utf-8 Confidence -> 98

18.33 Sec



보시다 시피, CJK / UTF-8 / ASCII 를 제외한 다른 1byte 권의 언어들에 대해서는 Universal chardet 이 ICU chardet 보다 월등히 detecting 을 잘 하고 있습니다. 다만 안습인 것은, mod_charset 의 Universal chardet 이 Python C API 를 이용해서 python 을 호출하다 보니, ICU 보다 성능이 굉장히 많이 떨어집니다. 실제로 위의 결과를 Universal chadet 체크를 하지 않는다면, 대략 0.04초 정도에 결과가 나옵니디만, Universal chardet detecting 을 시키니 거의 20초 가까운 결과치가 나옵니다.

대략 테스트를 해 보니 문자열이 3K 정도가 넘어가면 Python C API 로 호출한 결과가 상당히 늦어지는 결과를 보이더군요. 대략 1K 이내의 경우에 어느정도 비슷한 속도가 나옵니다.

또한, 짧은 문자열에 대해서도 ICU 보다 Universal chardet 이 성능이 조금 더 좋더군요. 그래도 한글 기준으로 테스트를 했을 때, 한글 10글자 정도는 받아야지 왠만한 confidence 가 나오게 됩니다.

다음의 결과는 web page 에서 html code 를 삭제하고 나온 결과 입니다. 다음의 코드가 사용이 되었습니다.

$buf = preg_replace ('/<[^>]*>/', '', $buf);


보통 웹 페이지의 경우, <>로 쌓여져 있는 코드들은 대부분 ASCII 이기 때문에 확률적 판단을 하는 chardet 에 부정적인 영향을 줄것이라 생각을 하고 한번 시도를 해 보았습니다.

PHP mod_chardet result:

google.cn (2781)
ICU : Encoding -> GB18030 Confidence -> 100
MOZ : Encoding -> GB2312 Confidence -> 98
yahoo.jp (3561)
ICU : Encoding -> UTF-8 Confidence -> 100
MOZ : Encoding -> utf-8 Confidence -> 98
amazon.co.jp (30739)
ICU : Encoding -> Shift_JIS Confidence -> 100
MOZ : Encoding -> SHIFT_JIS Confidence -> 100
pravda.ru (15728)
ICU : Encoding -> windows-1251 Confidence -> 28
MOZ : Encoding -> windows-1251 Confidence -> 94
auction.co.kr (36084)
ICU : Encoding -> EUC-KR Confidence -> 100
MOZ : Encoding -> ISO-8859-2 Confidence -> 27
haaretz.co.il (44570)
ICU : Encoding -> ISO-8859-8-I Confidence -> 23
MOZ : Encoding -> windows-1255 Confidence -> 98
www.nectec.or.th (10425)
ICU : Encoding -> EUC-JP Confidence -> 64
MOZ : Encoding -> TIS-620 Confidence -> 76
feedparser.org (5854)
ICU : Encoding -> UTF-8 Confidence -> 100
MOZ : Encoding -> utf-8 Confidence -> 98

4.86 Sec


이렇게 detecting 을 하니 ICU의 결과가 조금 좋아졌으며 (하지만 오판하는 경우도 생겼군요), 텍스트 양이 줄면서 Python C API를 사용하는 Univiersal chardet 의 성능이 대략 5초 정도로 시간이 절약 되었습니다.

뭐 어쨌든 두가지 기능을 다 지원을 하고, PHP 에서도 Universal chardet 을 지원할 수 있다는 점에 일단은 만족을 하고, posting 과 함께 mod_chardet 을 공개합니다.

그리고 혹시나 단순히 euc-kr / utf-8 만 판단을 해야 한다면, chardet 은 overhead 일 경우가 많습니다. 이럴 경우에는 차라리 pear KSC5601에 있는 is_utf8 method 를 사용하시는 것이 훨씬 경제적/성능적 효과가 좋습니다. 물론 제가 만들어 놓은 것입니다 ^^;

코드는 http://cvs.oops.org/index.php?cvsroot=PHP-Module 에서 받으실 수 있으며, 소스 안의 test.php 를 참조 하시면 사용하시는데 별 무리는 없을 겁니다.
2009/02/19 05:12 2009/02/19 05:12
상진군

정균님 안녕하세요.

요 모듈 아직도 서포트 받을 수 있는지 궁금하네요 ^^

libtool 버전이 2.2.6으로 올라가면서 CDPATH관련 에러두 나구요. --tag를 찾네요

그리고
/home/sangjins/mod_chardet/php_chardet.h:104: error: expected specifier-qualifier-list before 'UErrorCode'
/home/sangjins/mod_chardet/php_chardet.c: In function 'zif_chardet_detect':
/home/sangjins/mod_chardet/php_chardet.c:375: error: duplicate case value
/home/sangjins/mod_chardet/php_chardet.c:367: error: previously used here
/home/sangjins/mod_chardet/php_chardet.c:392: error: 'CharDetObj' has no member named 'status'
/home/sangjins/mod_chardet/php_chardet.c: In function 'chardet_obj_init':
/home/sangjins/mod_chardet/php_chardet.c:422: error: 'CharDetObj' has no member named 'status'
/home/sangjins/mod_chardet/php_chardet.c: In function 'moz_chardet':
/home/sangjins/mod_chardet/php_chardet.c:442: error: 'CharDetObj' has no member named 'status'
/home/sangjins/mod_chardet/php_chardet.c:448: error: 'CharDetObj' has no member named 'status'
의 에러도 나네요; 잘 사용하다가 재설치 하려니 이런 문제가 발생했습니다;

바쁘신데 이거 실례가 되지 않을런지.. ㅜㅜ

김정균

libtool 과는 상관이 없습니다. ICU library가 지원되지 않을 경우에 처리 버그가 존재하고 있었네요. 이 버그 수정해서 0.0.3으로 release해 놓았으니, 0.0.3으로 해 보시기 바랍니다.

Ubuntu 9.04 의 기본 패키지들과 libchardet 1.0.1 로 build할 때 문제없이 되었습니다. ^^;

xylosper

그렇군요. 그럼 라이브러리로 빌드하지 말고 그냥 소스를 직접 기존 프로젝트에 넣어버려야겠네요.
이래저래 신경써주셔서 감사합니다.

김정균

제 짧은 소견으로는 배포가 문제라면 귀찮게 소스 관리를 하시느니, 정적으로 link 시키는 것이 더 편하지 않을까 싶습니다. MPL 은 GPL 이나 LGPL 처럼 과격하지 않으니, static link 하시거나 dynamic link 하신 후에, MPL 라이센스 파일 하나 넣어 주시는 것으로 끝이 납니다. 전반적으로 GPL/LGPL로 배포를 하고 싶으시다면, 특정 부분에 call 을 MPL call 을 사용하신다고 명기를 하시면 될 것 같습니다. MPL은 다른 라이센스와 결합하는데 크게 지장이 없는 라이센스이니까요.

xylosper

아...그렇게해도 되는군요. GPL과 MPL이 호환이 안된다고 해서, 문제가 될줄 알았습니다. 감사합니다.

xylosper

그런거였군요. 감사합니다.
방금 다운 받아서 확인해보았는데, 라이센스가 MPL이더군요.
본래 소스는 MPL/GPL/LGPL중에 선택가능하게 되있던거 같은데, GPL로도 이용가능하게 배포하실 생각은 없으신가요...?

김정균

제가 개발하는 코드들은 대부분 필요에 의해서 제작을 하는 것입니다. 그렇기 때문에 순수하게 제가 처음부터 설계/코딩을 하는 경우 보다는 비슷한 것을 찾아서 제가 사용하기 편하도록 수정/개발을 하는 경우가 많습니다.

그러다 보니, 항상 license가 문제가 골치거리가 되는데, 가장 좋은 방법은 원 코드의 license 를 유지하는 것이 가장 편한 방법이더군요. 그리고 libcharset 의 원형이 Mozilla Universal Charset Detector 이기는 하지만, 저는 이 코드를 Perl Moudle 에서 친절하게 잘 분리해 놓은 것을 C wrapper 를 만들어서 c/c++ library 로 둔갑을 시켜 놓은 것입니다. 그리고 해당 Perl Module 이 license를 MPL로 해 놓아서 저 역시 이를 승계한 것이고요.

다만, Universal Chardet code 를 보면 MPL/GPL2/LGPL2.1 중에 하나를 선택할 수 있도록 되어 있습니다. 그리고, 저와 Perl module 개발자가 추가한 코드에서 이 문구는 역시 포함이 되어 있기 때문에 재배포를 하시고 싶고, license 를 변경하고 싶다면 저작권에 위배되지 않도록 결정을 하시면 될 것 같습니다.

저도 나름 살짝 고민했다가 GPL보다 MPL이 더 자유롭지 않을까 생각해서 MPL로 그냥 고민없이 승계를 했습니다.

xylosper

안녕하세요.
libchardet을 쓰고 싶어서 다운 받으려고 했더니 사용자 이름과 암호를 요구하네요... 특정 그룹에게만 공개된 자료인가요...?

김정균

browser 로 접속하지 마시고 ftp client 로 접속 하시면 됩니다.

상진군

감사합니다!! libchardet으로 하니 에러도없이 바로 작동하는군요~

잘 사용하겠습니다 (__) 감사합니다

김정균

mod_chardet 0.0.2 로 업데이트 했습니다. Python C API로 지원하던 mozilla universal charset detect mode 를 제거하고, libchardet 을 만들어서 C++로 지원하도록 변경했습니다. 속도가 캡빵 빨라졌습니다. :-) python-chardet 보다 정확도가 조금 더 좋습니다.

ftp://ftp.oops.org/pub/oops/php/extensions/mod_chardet 에서 받으실 수 있습니다.

http://cvs.oops.org/?cvsroot=PHP-Module&module=mod_chardet&file=README,v&rev=1.3 를 참조해서 빌드 하실 수 있습니다.

이 버전은 libchardet 이 기본 모드로 지원되며, CHARDET_ICU mode 로 ICU chardet mode 를 사용할 수 있으며, 개발시의 debug mode로 CHARDET_PY (default option 아님) 를 이용할 수 있습니다.

상진군

안녕하세요 ^^

개발해주신 모듈 설치에 성공하였습니다.

다만 온라인 용으로 사용시의 예제가 필요한데 혹시 제작해주실 수 있으신지요..

죄송합니다;

김정균

source 안에 있는 test.php 와 http://cvs.oops.org/?cvsroot=PHP-Module&module=mod_chardet&file=Reference,v&rev=1.1 를 참조하시면 될 것 같습니다. test.php 역시 제일 첫라인을 제외하면 web에서 그대로 사용하실 수 있습니다.

김정균

"mozilla code 에 c++ 로 지원하고 있으니 이걸 포팅하면 되겠지 하고 쉽게 생각을 했는데, xpcom 구조를 알기 전에는 쉽게 뗄 수 있을 놈이 아니더군요." 라고 적어 놓았는데, Perl 모듈을 보니 가져다 사용하고 있더군요. 그래도 libchardet 을 c/c++ 용 library 로 만들어 보았습니다. mod_chardet 의 python c api 를 굳이 이용할 필요가 없어 졌네요. :-)

조만간 업데이트를 하겠습니다.