作者:龍飛
4.1:為什么iconv不能完全正確的轉換Unicode?
我不是先知,教程里面是整理過的思路和邏輯順序,在我研究這個問題的時候,頭緒遠遠比教程里面亂得多。我完全是從Wesnoth的源代碼去分析問題的,所以,為什么會扯上UTF-8和FriBidi,那也是因為在源代碼中找到了線索。
iconv不能完全正確的獲得Unicode,也就是我們剛才遇到的純漢字轉換沒問題,而有英文就不行了。我并不清楚這是win32下的問題,還是在Linux下也這樣,我也不清楚具體的算法和問題的根本原因,我只是通過試驗得到一個算是表面原因的結論:我們知道,GB2312和Unicode漢字都使用2個字節(在UTF-8中是3個字節),英文和數字等用1個字節。iconv在得到兩個字節(unsigned char即一個字節大小)代碼的時候可以正確的將GB2312轉化為Unicode(或者UTF-8),但是只有1個字節的時候則在轉化Unicode的時候終止了,幸運的是,如果是轉化為UTF-8則可以正確的進行,并且也轉化為1個字節的UTF-8(只限于英文,數字等)。
所以,我們可以先通過iconv將原來的GB2312轉化為UTF-8——漢字用3個字節(3個單位的unsigned char),英文、數字和基本符號用1個字節(1個單位的unsigned char)。然后,我們需要一個函數,將這種形式的UTF-8轉換為SDL所需要的Uint16的Unicode。什么樣的函數可以實現這種轉換呢?
4.2:其它編碼與Unicode之間的雙向轉換,GNU FriBidi
http://fribidi.freedesktop.org/wiki/
FriBidi是一個致力于Unicode編碼與其它編碼相互轉換的開源項目,到目前為止,還是一個尚未完成的項目。我在研究Wesnoth源代碼的時候看到這樣的函數:fribidi_utf8_to_unicode(),所以,我想在這個函數中可能應該包含UTF-8到Unicode的算法——希望不要太復雜。在FriBidi項目中找到這個函數,它在文件fribidi_char_sets_utf8.c下面:
int
fribidi_utf8_to_unicode (char *s, int len, FriBidiChar *us)
/* warning: the length of input string may exceed the length of the output */
{
int length;
char *t = s;
length = 0;
while (s - t < len)
{
if (*(unsigned char *) s <= 0x7f) /* one byte */
{
*us++ = *s++; /* expand with 0s */
}
else if (*(unsigned char *) s <= 0xdf) /* 2 byte */
{
*us++ =
((*(unsigned char *) s & 0x1f) << 6) +
((*(unsigned char *) (s + 1)) & 0x3f);
s += 2;
}
else /* 3 byte */
{
*us++ =
((int) (*(unsigned char *) s & 0x0f) << 12) +
((*(unsigned char *) (s + 1) & 0x3f) << 6) +
(*(unsigned char *) (s + 2) & 0x3f);
s += 3;
}
length++;
}
*us = 0;
return (length);
}
其中,我們找到FriBidiChar的定義,類似Uint32的類型;另外,函數用char表示1字節的單位。根據我的試驗,至少在VC2008下是有錯誤的,我們一直用的是unsigned char表示1字節的單位,所以,我們需要對這個函數做些修改:
int myUTF8_to_UNICODE(Uint16* unicode, unsigned char* utf8, int len)
{
int length;
unsigned char* t = utf8;
length = 0;
while (utf8 - t < len){
//one byte.ASCII as a, b, c, 1, 2, 3
ect
if ( *(unsigned char *) utf8 <= 0x7f ) {
//expand with 0s.
*unicode++ = *utf8++;
}
//2 byte.
else if ( *(unsigned char *) utf8 <= 0xdf ) {
*unicode++ = ((*(unsigned char *) utf8 & 0x1f) << 6) + ((*(unsigned char *) (utf8 + 1)) & 0x3f);
utf8 += 2;
}
//3 byte.Chinese may use 3 byte.
else {
*unicode++ = ((int) (*(unsigned char *) utf8 & 0x0f) << 12) +
((*(unsigned char *) (utf8 + 1) & 0x3f) << 6) +
(*(unsigned char *) (utf8 + 2) & 0x3f);
utf8 += 3;
}
length++;
}
*unicode = 0;
return (length);
}
4.3:將漢字,英文,數字和符號都正確的轉換為16位的Unicode
有了iconv和上面這個函數,我們終于可以將GB2312的編碼正確的轉換為Unicode了。
//FileName: gb2312_to_Unicode.h
#ifndef GB2312_TO_UNICODE_H_
#define GB2312_TO_UNICODE_H_
#include <iostream>
#include <vector>
#include "GNU/iconv.h"
#include "SDL/SDL.h"
std::vector<Uint16> getUnicode(const std::string& str);
#endif
實現文件中包含我們上面寫的從UTF-8到Unicode的函數:
#include "gb2312_to_Unicode.h"
int myUTF8_to_UNICODE(Uint16* unicode, unsigned char* utf8, int len);
std::vector<Uint16> getUnicode(const std::string& str)
{
const int CHAR_SIZE = 256;
//GB2312 src
const unsigned char* src = (const unsigned char*)(str.c_str());
size_t src_len = strlen((char*)src);
//Unicode dst to get
unsigned char dst[CHAR_SIZE] = {0};
size_t dst_len = sizeof(dst);
//iconv arg
const unsigned char* in = src;
unsigned char* out = dst;
iconv_t cd;
//GB2312 to UTF-8
cd = iconv_open("UTF-8", "GB2312");
if ((iconv_t)-1 == cd){
exit (-1);
}
//conversion
iconv(cd, (const char**)&in, &src_len, (char**)&out, &dst_len);
//UTF-8 to Unicode
int utf8Len = strlen((char*)dst);
Uint16 unicodeData[CHAR_SIZE] = {0};
int unicodeLen = myUTF8_to_UNICODE(unicodeData, dst, utf8Len);
std::vector<Uint16> unicodeVectorArray;
for (int i = 0; i < unicodeLen; i++) {
unicodeVectorArray.push_back(unicodeData[i]);
}
iconv_close(cd);
return unicodeVectorArray;
}
函數把一個std::string轉換位Uint16的vector數組并返回,這正是SDL所需要的Unicode格式。
posted on 2008-03-31 11:00
lf426 閱讀(5526)
評論(0) 編輯 收藏 引用 所屬分類:
SDL入門教程 、
跨平臺與GNU