Android NDK 下的寬字符編碼轉換及icu庫的使用 – Android移動開發技術文章_手機開發 Android移動開發教學課程

如果是在java層,有String類可以很好的轉換各種編碼,在ndk下面就沒有現成的公開的工具,不過可以用icu4c。

    ICU4C 是IBM的國際化開發組件ICU的C語言實現版本。在android系統裡也有實現。ndk裡面並沒有公開可用的api,需要自己加載動態庫來調用轉換函數。

    android下icu庫路徑為"/system/lib/libicuuc.so",主要用到的轉換函數為ucnv_convert_?_?。這裡的問號是根據版本的不同函數名也不一樣。在2.2的模擬器中的libicuuc.so中此函數名為ucnv_convert_4_2,在2.1模擬器中為ucnv_convert_3_8,貌似要根據版本不同來分開對待,還沒發現可以統一的辦法。

    函數原型:

    void ucnv_convert(const char *, const char *, char * , int32_t , const char *, int32_t,int32_t*);

 

    用法:

    [cpp] //聲明函數指針  
void (*ucnv_convert)(const char *, const char *, char * , int32_t , const char *, int32_t,int32_t*)=0; 
//加載動態庫  
void* pDL = dlopen("/system/lib/libicuuc.so", RTLD_LAZY); 
//這裡以android2.2為例,函數名就是ucnv_convert_4_2  
ucnv_convert = (void (*)(const char *, const char *, char * , int32_t , const char *, int32_t,int32_t*))dlsym(pDL, "ucnv_convert_4_2"); 
//加載成功就可以用瞭  
if(ucnv_convert){ 
    char* cbuf = "…"; 
    char buffer[100]; 
    int errcode = 0; 
    //utf8是目標編碼,ucs2是原字符編碼  
     //buffer是存放轉換出來的字符的緩沖,給瞭100字節  
     //cbuf是要轉換的字符串指針  
     //errcode是錯誤編碼,具體可網上搜索  
    ucnv_convert("utf8","ucs2", buffer, 100, cbuf, strlen(cbuf),&errcode); 

//聲明函數指針
void (*ucnv_convert)(const char *, const char *, char * , int32_t , const char *, int32_t,int32_t*)=0;
//加載動態庫
void* pDL = dlopen("/system/lib/libicuuc.so", RTLD_LAZY);
//這裡以android2.2為例,函數名就是ucnv_convert_4_2
ucnv_convert = (void (*)(const char *, const char *, char * , int32_t , const char *, int32_t,int32_t*))dlsym(pDL, "ucnv_convert_4_2");
//加載成功就可以用瞭
if(ucnv_convert){
    char* cbuf = "…";
    char buffer[100];
    int errcode = 0;
    //utf8是目標編碼,ucs2是原字符編碼
     //buffer是存放轉換出來的字符的緩沖,給瞭100字節
     //cbuf是要轉換的字符串指針
     //errcode是錯誤編碼,具體可網上搜索
    ucnv_convert("utf8","ucs2", buffer, 100, cbuf, strlen(cbuf),&errcode);
}

 

    轉換成功後的字符串就放在buffer裡面,如果出錯瞭就會在errcode裡面放錯誤代碼。

 

    如標題所示,ndk下還有一個寬字符,也就是wchar_t的問題,跟其他平臺移植也是個麻煩的事。

    linux下wchar_t默認是4個字節,而windows下(包括CE,MOBILE)和symbian下都是2個字節。解決的辦法是在android.mk文件中,找到LOCAL_CFLAGS 為其加上編譯開關 -fshort-wchar(如果沒有此項就手動寫上),如 LOCAL_CFLAGS :=  -fshort-wchar  。這樣強制編譯器用2個字節處理wchar_t,不過編譯時會有warning,可以不管。

    這樣雖然編譯器處理成2個字節,但是預編譯的庫libc等依然是4個字節,會導致wcslen等函數無法使用(其實ndk下wcslen本來就是廢的),解決的辦法可以重新編譯libc,不過最簡單的還是自己實現wcslen就行瞭。

    下面的代碼是copy網上的,具體哪裡的忘瞭,可以把wchar_t轉換成char字符串,這樣就可以用icu庫隨意轉換瞭。

 

    [cpp] //取得wchar_t字符串長度  
int wlen(const wchar_t* strString){ 
    int i=0; 
    while (1) { 
        if (strString[i] == 0) { 
            break; 
        }else{ 
            i++; 
        } 
    } 
    return i; 

 
char *W2C(const wchar_t *pw , char *pc) 

      *pc++ = *pw >> 8 ; 
      *pc = *pw ; 
      return 0 ; 

//轉換字符串  
char *wstr2cstr(const wchar_t *pwstr, char *pcstr, size_t len) { 
    char *ptemp = pcstr; 
    if (pwstr != NULL && pcstr != NULL) { 
        size_t wstr_len = wlen(pwstr); 
        len = (len > wstr_len) ? wstr_len : len; 
        while (len– > 0) { 
            W2C(pwstr, pcstr); 
            pwstr++; 
            pcstr += 2;  
        } 
        *pcstr = '/0'; 
        return ptemp; 
    } 
    return 0; 

//取得wchar_t字符串長度
int wlen(const wchar_t* strString){
 int i=0;
 while (1) {
  if (strString[i] == 0) {
   break;
  }else{
   i++;
  }
 }
 return i;
}

char *W2C(const wchar_t *pw , char *pc)
{
      *pc++ = *pw >> 8 ;
      *pc = *pw ;
      return 0 ;
}
//轉換字符串
char *wstr2cstr(const wchar_t *pwstr, char *pcstr, size_t len) {
 char *ptemp = pcstr;
 if (pwstr != NULL && pcstr != NULL) {
  size_t wstr_len = wlen(pwstr);
  len = (len > wstr_len) ? wstr_len : len;
  while (len– > 0) {
   W2C(pwstr, pcstr);
   pwstr++;
   pcstr += 2;
  }
  *pcstr = '/0';
  return ptemp;
 }
 return 0;
}

 

    使用wstr2cstr就可以轉換出來。這裡還有個字節序的問題,在W2C函數裡面,一個wchar_t轉到char究竟是低位在前還是高位在前恐怕還是要看轉換前後的編碼具體對待。

 作者 liujian885
 

發佈留言