iconv 用法

iconv 用法

 

場景說明

     UTF-8的編碼轉換成GBK編碼,使用Notepad++創建一個UTF-8(無BOM)的編碼文件,文件內容簡單爲風雨在途,保存文件名稱爲utf8.txt,轉換之後存儲文件名稱爲gbk.txt.

 

1.1iconv命令的使用

iconv -f UTF8  -t GBK <utf8.txt>  gbk.txt

 

1.2iconv函數的使用

      #include <iconv.h>

 

      size_t iconv(iconv_t cd,

                    char **inbuf, size_t*inbytesleft,

                    char **outbuf, size_t*outbytesleft);

參數說明:經過iconv函數之後,*outbuf當前指向轉換之後的字符串的最後位置,*inuf當前指向被轉換字符串的最後位置,所以爲了得到正確的轉換字符串的指針位置,需要進行如下的調整:

*outbuf = *outbuf - iconv函數的返回值.或者定義指針指向當前的字符串緩存區,就不會修改字符串的緩衝區指針了

 

1.3代碼如下

int UTF8fileToGBKfile()

{

int ret;

int read_fd;

int write_fd;

//1打開文件,讀取utf-8文件

read_fd=open("utf8.txt",O_RDONLY|O_CREAT);

write_fd=open("gbk.txt", O_WRONLY|O_CREAT);

char utfBuffer[256] = {0};

size_t inLen = read(read_fd, utfBuffer,256);

 

//2獲取字符集轉換的文件句柄,這裏是從utf-8轉換爲gbk

errno = 0;

iconv_t cd = iconv_open("gbk","utf-8");

char* errMsg = NULL;

if (errno !=0)

{

   errMsg = strerror(errno);

   cout<<errno<<endl;

   cout<<errMsg<<endl;

   return -1;

}

 

//3準備轉換前的字符串,避免修改原字符串

char* szSrc = (char*)malloc(inLen);

memset(szSrc, 0, inLen);

memcpy(szSrc, utfBuffer, inLen);

 

//4準備轉換後的字符串存儲空間

size_t outLen = 256;

char* szDest = (char*)malloc(outLen);

memset(szDest, 0, outLen);

 

 

//4開始轉換

errno = 0;

char* pszDest = szDest;

char* pszSrc = szDest;

errno = 0;

ret = iconv(cd, &pszSrc, &inLen,&pszDest , &outLen);

if (errno !=0)

{

   errMsg = strerror(errno);

   cout<<errno<<endl;

   cout<<errMsg<<endl;

   return -2;

}

iconv_close(cd);

 

//5寫入文件,outlen是剩下多少空間沒有使用

ret = write(write_fd, szDest, 256-outLen);

close(read_fd);

close(write_fd);

free(szDest);

free(szSrc);

return 0;

}

 

2 編譯調試

2.1禁用優化安裝

下載libiconv-1.14.tar.gz,解壓,執行./configure --prefix=/opt/iconv;make;make install

生成文件:

charset.alias  libcharset.so        libiconv.la    libiconv.so.2.5.1

libcharset.a   libcharset.so.1      libiconv.so    preloadable_libiconv.so

libcharset.la  libcharset.so.1.0.0  libiconv.so.2

 

實際鏈接到庫的時候,指定庫目錄,/etc/ld.so.conf文件之後追加一行/opt/iconv/lib.採用ldconfig命令的時候,出錯:

libcharset.so.1 不是符號鏈接

libiconv.so.2 不是符號鏈接

 

原因是:刪除這兩個文件,然後使用ldconfig命令,生成該符號鏈接。

[root@jack lib]# ll

總用量 3560

-rw-r--r--. 1 root root     212 9  13 10:22 charset.alias

-rw-r--r--. 1 root root   27868 9  13 10:22 libcharset.a

-rw-r--r--. 1 root root     936 9  13 10:22 libcharset.la

-rw-r--r--. 1 root root   277609  13 10:22 libcharset.so

lrwxrwxrwx. 1 root root      19 9  13 11:10libcharset.so.1->libcharset.so.1.0.0

-rw-r--r--. 1 root root   27760 9  13 10:22 libcharset.so.1.0.0

-rw-r--r--. 1 root root     912 9  13 10:22 libiconv.la

-rw-r--r--. 1 root root 1186436 9  13 10:22 libiconv.so

lrwxrwxrwx. 1 root root      17 9  13 11:10 libiconv.so.2 ->libiconv.so.2.5.1

-rw-r--r--. 1 root root 1186436 9  13 10:22 libiconv.so.2.5.1

-rw-r--r--. 1 root root 1168119 9  13 10:22 preloadable_libiconv.so

 

調用該動態庫的編譯命令如下:

g++ test.cpp -I/opt/iconv/include//opt/iconv/lib/libcharset.a/opt/iconv/lib/libiconv.so   libsqlite3.a-lpthread -ldl  -g -o main

 

2.2禁用優化

./configure--prefix=/opt/iconvCFLAGS="-g -O0"

生成動態庫鏈接符號錯誤解決

[root@jack lib]# ldconfig

ldconfig: /opt/iconv/lib/libiconv.so.2 不是符號連接

ldconfig: /opt/iconv/lib/libcharset.so.1 不是符號連接

[root@jack lib]# rm -rflibiconv.so.2libcharset.so.1

[root@jack lib]# ldconfig

 

2.3 GDB調試源碼

主要是將UTF-8編碼的字符串在轉換成GBK編碼,詳細的轉換規則UTF-8Unicode

然後Unicode編碼通過查表,映射到GBK編碼上

loop_unicode.h:273

執行函數

incount=cd->ifuncs.xxx_mbtowc(cd,&wc,inptr,inleft);

跳轉到./utf8.h:30

 

核心轉換函數:static size_t unicode_loop_convert (iconv_t icd,                           

  const char* * inbuf, size_t *inbytesleft,char* *outbuf,size_t*outbytesleft)

 

  at./loop_unicode.h:284

284 incount = cd->ifuncs.xxx_mbtowc(cd,&wc,inptr,inleft);

 

  at./loop_unicode.h:362

362   outcount = cd->ofuncs.xxx_wctomb(cd,outptr,wc,outleft);

該函數進行Unicode轉換成GBK

進行的是查表進行轉換,在調試的過程中,可以通過附件,提前知道轉換的字符編碼,然後查看iconv庫是否轉換有問題

 

UTF-8Unicode編碼的函數:

static int

utf8_mbtowc (conv_t conv, ucs4_t*pwc,constunsigned char *s, int n)

 

 iconv函數中傳遞進去的outptr會指向轉換字符串的末尾,所以需要進行指針的前移,outptr-返回值

就是指向最開始轉換的字符串

 

iconv_open函數跳轉到lib/iconv.c 218 行,裏面調用了

#include "iconv_open1.h"

47-60

141-152

  for (cp = fromcode, bp = buf, count = MAX_WORD_LENGTH+10+1; ; cp++,bp++){

    unsigned char c = * (unsigned char *) cp;

    if (c >= 0x80)

      goto invalid;

    if (c >= 'a' && c <= 'z')

      c -= 'a'-'A';

    *bp = c;

    if (c == '\0')

      break;

    if (--count == 0)

      goto invalid;

    }

這裏進行了大小寫轉換,所有的字體編碼類型,轉換成大寫字母,gbk轉換成GBK.小寫字母轉換成大寫字母是減去26

 

目前遇到一個相當大的問題:

   ap= aliases_lookup(buf,bp-buf);

   if(ap == NULL) {

    ap = aliases2_lookup(buf);

    if (ap == NULL)

      goto invalid;

    }

這幾行函數沒能夠單步調試所以並不清楚返回值進行了什麼操作

#include "iconv_open2.h"

設置執行的回調函數,以及初始化標誌位,具體的沒有看出什麼

 

 

2.4手動編寫指令構建動態庫

    使用庫提供的automake,順利生成libiconv.so文件,目前嘗試單獨編譯,出現如下的問題

使用指令如下:

g++libiconv-1.14/libcharset/lib/localcharset.clibiconv-1.14/lib/iconv.clibiconv-1.14/lib/relocatable.c  libsqlite3.a -lpthread -ldl -Ilibiconv-1.14/-I libiconv-1.14/include/-I libiconv-1.14/lib-Ilibiconv-1.14/libcharset/include/ -fPIC -shared -o libiconv.so

libiconv-1.14/libcharset/lib/localcharset.c:77:25:錯誤:configmake.h:沒有那個文件或目錄

libiconv-1.14/libcharset/lib/localcharset.c:Infunction‘const char* get_charset_aliases()’:

libiconv-1.14/libcharset/lib/localcharset.c:135:錯誤:‘LIBDIR’在此作用域中尚未聲明

 

查找資料顯示在windows下無需該文件,因此註釋該頭文件的包含

問題剩下LIBDIR的定義問題

 

查找所有的引用LIBDIR ,localcharset.c函數

static const char*get_charset_aliases(void)會通過dir =relocate (LIBDIR);

獲取該值,查看該函數的內容:

 const char *cp;

 

 cp=charset_aliases;

 

/* Pointer to the contentsofthecharset.alias file, if it has already been

 read, else NULL.  Its format is:

 ALIAS_1 '\0' CANONICAL_1 '\0' ... ALIAS_n '\0'CANONICAL_n '\0''\0'  */

static const char *volatilecharset_aliases;

 

說明指向這個charset.alias文件,查看該文件:

該文件的目錄如下:

libiconv-1.14/libcharset/lib

內容如下:

# This file contains a table ofcharacterencodingaliases,

# suitable for operating system'linux-gnu'.

# It was automatically generatedfromconfig.charset.

# Packages using this file:

ISO_646.IRV:1983 ASCII

 

說明該文件是由同一級目錄下的config.charset腳本生成的

 

跟蹤:

 linux* | *-gnu*)

  #With glibc-2.1 or newer, we don't need any canonicalization,

  #because glibc has iconv and both glibc and libiconv support all

  #GNU canonical names directly. Therefore, the Makefile does not

  #need to install the alias file at all.

  #The following applies only to glibc-2.0.x and older libcs.

 echo "ISO_646.IRV:1983 ASCII"

翻譯:glibc-2.1以後的版本都不需要任何的標準化文件,因爲自帶的緣故,該文件只是被之前的版本引用

因此在localcharset.c 定義LIBDIR指向的目錄:

#defineLIBDIR"/work/libiconv-1.14/libcharset/lib"

 

[root@jack work]# g++libiconv-1.14/libcharset/lib/localcharset.clibiconv-1.14/lib/relocatable.c    libiconv-1.14/lib/iconv.c test.cpp  -I libiconv-1.14/ -Ilibiconv-1.14/lib/-Ilibiconv-1.14/include  -Ilibiconv-1.14/libcharset/include/ -I libiconv-1.14/srclib/   libsqlite3.a -lpthread -ldl  -o testmain

/tmp/cc1ENLex.o: Infunction`libiconv_open':

iconv.c:(.text+0x18206):undefinedreferenceto `aliases_lookup(char const*, unsigned int)'

iconv.c:(.text+0x18391):undefinedreferenceto `aliases_lookup(char const*, unsigned int)'

/tmp/cc1ENLex.o:Infunction`libiconv_open_into':

iconv.c:(.text+0x187d0): undefinedreferenceto`aliases_lookup(char const*, unsigned int)'

iconv.c:(.text+0x1895b):undefinedreferenceto `aliases_lookup(char const*, unsigned int)'

/tmp/cc1ENLex.o: Infunction`iconv_canonicalize':

iconv.c:(.text+0x190a9):undefinedreferenceto `aliases_lookup(char const*, unsigned int)'

collect2: ld 返回 1

 

該函數的定義就在lib/aliases.h文件當中定義的,不清楚爲什麼無法找到,

然後手動將該定義拷貝到lib/iconv.c文件,還是出現同樣子的問題


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章