Linux(程序設計):27---字符編碼iconv開發庫(iconv.h)

一、iconv庫介紹

二、iconv_open()

#include <iconv.h>
iconv_t iconv_open (const char* tocode, const char* fromcode);
  • 功能:爲字符集轉換分配描述符,該描述符適用於從字符編碼fromcode轉換爲tocode
  • 返回值:
    • 成功:返回一個新分配的轉換描述符
    • 失敗:設置errno並返回(iconv_t)-1
  • 支持的errono有:
    • EINVAL:不支持從fromcode到tocode的轉換
  • 參數的注意事項:
    • 空的編碼名稱""等價於“char”,它表示與語言環境有關的字符編碼
    • 當tocode以字符串“//TRANSLTI”結尾時,音譯被激活,意味着當一個字符不能在目標字符集中表示時,可以通過一個或幾個看起來與原始字符相似的字符來近似表示
    • 當tocode以字符串“//IGNORE”結尾時,目標字符集中無法表示的字符將被默認丟棄
  • 返回值說明:
    • 返回生成的轉換描述符可以與iconv一起使用多次,一直保持有效,直到使用iconv_close()關閉爲止
    • 轉換描述符包含轉換狀態,使用iconv_open()創建的爲初始狀態,使用iconv()函數可以修改描述符的狀態
    • 要將描述符返回到初始狀態,請傳遞NULL給iconv()的inbuf參數

支持的編碼

  • fromcode和tocode以及允許的組合所允許的值取決於系統。對於libiconv庫,所有組合均支持以下編碼:
    • European languages:ASCII, ISO−8859−{1,2,3,4,5,7,9,10,13,14,15,16}, KOI8−R, KOI8−U, KOI8−RU, CP{1250,1251,1252,1253,1254,1257}, CP{850,866,1131}, Mac{Roman,CentralEurope,Iceland,Croatian,Romania}, Mac{Cyrillic,Ukraine,Greek,Turkish}, Macintosh
    • Semitic languages:ISO−8859−{6,8}, CP{1255,1256}, CP862, Mac{Hebrew,Arabic}
    • Japanese:EUC−JP, SHIFT_JIS, CP932, ISO−2022−JP, ISO−2022−JP−2, ISO−2022−JP−1ISO−2022−CN−EXT
    • Chinese:EUC−CN, HZ, GBK, CP936, GB18030, EUC−TW, BIG5, CP950, BIG5−HKSCS, BIG5−HKSCS:2001, BIG5−HKSCS:1999, ISO−2022−CN, ISO−2022−CN,-EXT
    • Korean:EUC−KR, CP949, ISO−2022−KR, JOHAB
    • Armenian:ARMSCII−8
    • Georgian:Georgian−Academy, Georgian−PS
    • Tajik:KOI8−T
    • Kazakh:PT154, RK1048
    • Thai:TIS−620, CP874, MacThai
    • Laotian:MuleLao−1, CP1133
    • Vietnamese:VISCII, TCVN, CP1258
    • Platform specifics:HP−ROMAN8, NEXTSTEP
    • Full Unicode:
      • C99, JAVA
      • UTF−7
      • UTF−8
      • UCS−2, UCS−2BE, UCS−2LE
      • UCS−4, UCS−4BE, UCS−4LE
      • UTF−16, UTF−16BE, UTF−16LE
      • UTF−32, UTF−32BE, UTF−32LE
  • Full Unicode,以uint16_t或 uint32_t表示(具有與機器相關的字節序和對齊方式
  • 取決於語言環境,以char或 wchar_t表示(具有與機器有關的字節序和對齊方式,並且其語義取決於操作系統和當前的LC_CTYPE語言環境方面)
  • 當使用選項 -enable-extra-encodings配置時,它還提供了對一些額外編碼的支持:
    • European languages:CP{437,737,775,852,853,855,857,858,860,861,863,865,869,1125}
    • Semitic languages:CP864
    • Japanese:EUC−JISX0213, Shift_JISX0213, ISO−2022−JP−3
    • Chinese:BIG5−2003 (實驗性的)
    • Turkmen:TDS565
    • Platform specifics:ATARIST, RISCOS−LATIN1

三、iconv_open_into()

#include <iconv.h>
int iconv_open_into(const char *tocode, const char *fromcode
                    iconv_allocation_t *resultp);
  • 功能:爲該函數與iconv_open()函數的功能一樣,也是將字符編碼fromcode轉換爲tocode,但是其將轉換描述符保存到參數3所指的內存中,iconv_allocation_t與iconv_t一樣可以適用於其它函數的使用
  • 兼容性:
    • 此功能僅在GNU libiconv中實現,而在其他iconv實現中不實現。它沒有標準支持
    • 您可以通過(_LIBICONV_VERSION> = 0x010D)測試其存在 
  • 返回值:
    • 成功:填充*resultp並返回0
    • 失敗:設置errno並返回(iconv_t)-1
  • 支持的errono有:
    • EINVAL:不支持從fromcode到tocode的轉換

四、iconv_close()

#include <iconv.h>
int iconv_close (iconv_t cd);
  • 功能:關閉參數所指定的轉換描述符
  • 返回值:
    • 成功:返回0
    • 失敗:設置errno並返回-1
  • 支持的errono有:
    • EINVAL:不支持從fromcode到tocode的轉換

五、iconvctl()

#include <iconv.h>
int iconvctl(iconv_t cd, int request, void *argument);
  • 功能:在指定的轉換描述符上進行查詢,或者調整iconv()函數的行爲,具體取決於請求值
  • 兼容性:
    • 此功能僅在GNU libiconv中實現,而在其他iconv實現中不實現。它沒有標準支持
    • 您可以通過(_LIBICONV_VERSION> = 0x0108)測試其存在 
  • 下面是request參數的允許值:
    • ICONV_TRIVIALP:argument參數應該是一個int*,如果轉換是微不足道的,argument參數將被設置爲1,否則爲0
    • ICONV_GET_TRANSLITERATE:argument參數應該是一個int*,如果在轉換中啓用了音譯功能(見上面iconv_open()函數介紹),argument參數將被設置爲1,否則爲0
    • ICONV_SET_TRANSLITERATE:argument參數應該是一個const int*,如果argument非0表示啓用轉換中的音譯功能,0值表示禁用音譯功能
    • ICONV_GET_DISCARD_ILSEQ:argument參數應該是一個int*,如果在轉換中啓用了“非法序列丟棄並繼續”功能,argument參數將被設置爲1,否則爲0
    • ICONV_SET_DISCARD_ILSEQ:argument參數應該是一個const int*,如果argument非0表示啓用轉換中的“非法序列丟棄並繼續”功能,0值表示禁用該功能
  • 返回值:
    • 成功:返回0
    • 失敗:設置errno並返回-1
  • 支持的errono有:
    • EINVAL:該請求無效

六、iconv()

#include <iconv.h>
size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf,
            size_t *outbytesleft); 
  • 功能:執行字符集轉換,從inbuf中讀取inbytesleft字節然後轉換保存到outbuf指向的內存中,最多寫入outbytesleft字節
  • 轉換可能因爲下面四個原因停止:
    • ①在輸入中遇到無效的多字節序列。在這種情況下,它將errno設置爲EILSEQ並返回(size_t)-1。* inbuf指向無效的多字節序列的開頭
    • ②輸入的字節序列已完全轉換,即*inbytesleft已降至0。在這種情況下, iconv返回此調用期間執行的不可逆轉換的次數
    • ③輸入中遇到不完整的多字節序列,輸入字節序列在此之後終止。在這種情況下,它將errno設置爲EINVAL並返回(size_t)-1。* inbuf指向不完整的多字節序列的開頭。
    • ④輸出緩衝區不再有空間容納下一個轉換的字符。在這種情況下,它將errno設置爲E2BIG並返回(size_t)-1
  • (重點)在轉換的過程中,inbuf和outbuf的指針會逐漸的向後偏移,因此inbuf和outbuf指針會改變,因此在操作iconv()之前最後定義兩個臨時指針分別指向inbuf和outbuf,然後使用這兩個臨時指針來調用iconv()函數(見下面的演示案例①)
  • 當inbuf或outbuf爲NULL時函數的表現
    • 如果inbuf爲NULL或*inbuf爲NULL,但是outbuf不爲NULL和*outbuf不爲NULL。在這種情況下,iconv函數會嘗試將cd的轉換狀態設置爲初始狀態,並將相應的移位序列存儲在*outbuf處。 從*outbuf開始,最多將寫入*outbytesleft個字節。如果輸出緩衝區沒有更多空間可用於此復位序列,則會將errno設置爲E2BIG並返回(size_t)-1。否則,它將增加*outbuf並減少*outbytesleft 通過寫入的字節
    • 如果inbuf爲NULL或*inbuf爲NULL,且outbuf爲NULL或*outbuf爲NULL。在這種情況下,iconv函數會將cd的轉換狀態設置爲初始狀態
  • 返回值:
    • 成功:返回轉換的所有字符中不可逆轉換的字符數,可逆的不計算在內
    • 失敗:設置errno並返回(size_t)-1
  • 支持的errono有:
    • E2BIG:*outbuf 沒有足夠的空間
    • EILSEQ:輸入中遇到無效的多字節序列
    • EINVAL:輸入中遇到了不完整的多字節序列

七、演示案例①

  • 演示功能:調用iconv_open()、iconv()、iconv_close()三個基本的接口,實現將一個字符串由utf-8編碼格式轉換爲utf-16的格式

代碼實現

//utf8_to_utf16.c
//https://github.com/dongyusheng/csdn-code/blob/master/iconv/utf8_to_utf16.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iconv.h>

int main()
{
    unsigned char *encTo = "UTF-16";  //要轉換的編碼格式
    unsigned char *encFrom = "UTF-8"; //原編碼格式

    //新建一個轉換分配描述符
    iconv_t cd = iconv_open(encTo, encFrom);
    if(cd == (iconv_t)-1)
    {
        perror("iconv_open");
        return -1;
    }

    //轉換的字符串
    unsigned char inbuf[1024] = "CSDN 江南、董少";
    size_t inbufLen = strlen(inbuf);

    //用來保存轉換後的字符串
    size_t outbufLen = 1024;
    unsigned char outbuf[outbufLen];
    memset(outbuf, 0, outbufLen);

    //因爲iconv()函數會改變inbuf和outbuf指針,因此實現定義兩個臨時指針指向於這兩,然後用這兩個指針去操作
    unsigned char *inbufSrc = inbuf;
    unsigned char *outbufSrc = outbuf;

    //轉換之前打印一下原字符串和每個字符串的二進制值
    printf("inbuf: %s, len: %ld\n", inbuf, strlen(inbuf));
    printf("utf8:\n");
    for(int i = 0; i < inbufLen; ++i)
    {
        printf("%02x ", inbuf[i]);
    }
    printf("\n\n\n");


    //轉換
    size_t ret = iconv(cd, (char**)&inbufSrc, &inbufLen, (char**)&outbufSrc, &outbufLen);
    if(ret == -1)
    {
        perror("iconv");
        iconv_close(cd);
        return -1;
    }


    //打印轉換之後的字符串和每個字符串的二進制值(因爲outbuf中間會有0,所以不能簡單的使用strlen來計算長度)
    printf("outbuf: %s len: %ld\n", outbuf, outbufSrc - outbuf);
    printf("utf16:\n");
    //不能以strlen(outbuf)作爲參數2,因爲outbuf中間有0,會導致for循環只打印部分的內容
    for(int i = 0; i < (int)(outbufSrc - outbuf); ++i)
    {
        printf("%02x ", outbuf[i]);
    }
    printf("\n");

    //關閉句柄
    iconv_close(cd);
    
    return 0;
}
  •  演示效果如下:
    • 上面是轉換之前的信息,下面是轉換之後的信息
    • 下面箭頭處是程序打印的一處錯誤,因爲將inbuf轉換爲utf-16存儲之後,通過二進制可以看到其字符串中間有0值,但是printf()打印的時候遇到0就會終止打印,因此轉換後的字符串不能通過printf()打印出來

八、演示案例②

  • 演示功能:下面的功能與上面一樣,不過這一次我們將iconv的各種接口進行封裝

代碼實現

//charset_converter.c
//​​​​​​​https://github.com/dongyusheng/csdn-code/blob/master/iconv/charset_converter.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <iconv.h>

typedef struct charset_converter
{
    iconv_t cd; //字符轉換描述符
    int error;  //錯誤碼
} charset_converter_t;


/*****************************************************************************
 函 數 名: charset_convert_init
 功能描述  : 創建字符轉換描述符,並封裝charset_converter_t結構返回
 輸入參數  : const char*tocode      ---> 目標編碼格式 
           const char *fromcode ---> 源編碼格式
 輸出參數  : 無
 返 回 值: charset_converter_t * ---> 成功返回charset_converter_t*對象,失敗返回NULL
 調用函數  : 
 被調函數  : 
 
 修改歷史      :
  1.日    期   : 2020年6月10日
    作    者   : 董雨生
    修改內容   : 新生成函數

*****************************************************************************/
charset_converter_t *charset_convert_init(const char*tocode, const char *fromcode)
{
    charset_converter_t *converter = (charset_converter_t*)malloc(sizeof(charset_converter_t));
    if(converter == NULL)
    {
        perror("malloc");
        return NULL;
    }
    converter->cd = (iconv_t)-1;
    converter->error = 0;

    converter->cd = iconv_open(tocode, fromcode);
    if(converter->cd == (iconv_t)-1)
    {
        printf("iconv_open failed, errno = %d, %s\n",errno, strerror(errno));
        free(converter);
        return NULL;
    }

    return converter;    
}


/*****************************************************************************
 函 數 名: charset_convert_iconv
 功能描述  : 執行字符集轉換
 輸入參數  : charset_converter_t *converter --->charset_converter_t*對象
           char **inbuf                   --->源編碼緩衝區
           size_t *inbytesleft            --->源編碼緩衝區中字符串的大小
           char **outbuf                  --->目標編碼緩衝區
           size_t *outbytesleft           --->目標編碼緩衝區接收的最大大小
 輸出參數  : 無
 返 回 值: int ---> 成功返回0,失敗返回-1
 調用函數  : 
 被調函數  : 
 
 修改歷史      :
  1.日    期   : 2020年6月10日
    作    者   : 董雨生
    修改內容   : 新生成函數

*****************************************************************************/
int charset_convert_iconv(charset_converter_t *converter, char **inbuf, size_t *inbytesleft,
                        char **outbuf, size_t *outbytesleft)
{
    if(converter != NULL)
    {
        size_t ret = iconv(converter->cd, inbuf, inbytesleft, outbuf, outbytesleft);
        if(ret == (size_t)-1)
        {
            converter->error = errno;
            printf("iconv failed, errno = %d, %s\n", converter->error, strerror(converter->error));
            switch(converter->error)
            {
            case E2BIG:
                printf("E2BIG\n");
                break;
            case EILSEQ:
                printf("EILSEQ\n");
                break;
            case EINVAL:
                printf("EINVAL\n");
                break;
            default:
                printf("unknown\n");
            }
            return -1;
        }
        return 0;
    }

    printf("converter error\n");
    return -1;
}


/*****************************************************************************
 函 數 名: charset_convert_close
 功能描述  : 釋放charset_converter_t結構
 輸入參數  : charset_converter_t *converter --->charset_converter_t*對象
 輸出參數  : 無
 返 回 值: int ---> 成功返回0,失敗返回-1
 調用函數  : 
 被調函數  : 
 
 修改歷史      :
  1.日    期   : 2020年6月10日
    作    者   : 董雨生
    修改內容   : 新生成函數

*****************************************************************************/
int charset_convert_close(charset_converter_t *converter)
{
    if((converter != NULL))
    {
        int ret = iconv_close(converter->cd);
        if(ret == -1)
        {
            perror("iconv_close");
            return -1;
        }

        free(converter);
        return 0;
    }

    printf("converter error\n");
    return -1;
}


int main()
{
    //創建charset_converter_t結構
    const char *encTo = 
"UTF-16";
    const char *encFrom = "UTF-8";
    charset_converter_t *converter = charset_convert_init(encTo, encFrom);
    if(converter == NULL)
        return -1;

    //要轉碼的字符串
    char inbuf[1024] = "CSDN 江南、董少";
    size_t inbufLen =strlen(inbuf);

    //轉碼後的保存緩衝區
    size_t outbufLen = 1024;
    char outbuf[outbufLen];
    memset(outbuf, 0, outbufLen);
    

    //轉碼之前打印一下字符串的相關信息
    printf("inbuf: %s, len: %ld\n", inbuf, strlen(inbuf));
    printf("utf-8:\n");
    for(int i = 0; i < inbufLen; ++i)
    {
        printf("%02x ", inbuf[i]);
    }
    printf("\n\n\n");


    //開始轉換
    //因爲iconv()函數會改變inbuf和outbuf指針,因此實現定義兩個臨時指針指向於這兩,然後用這兩個指針去操作
    char *inbufSrc = inbuf;
    char *outbufSrc = outbuf;

    int ret = charset_convert_iconv(converter, &inbufSrc, &inbufLen, &outbufSrc, &outbufLen);
    if(ret == -1)
        return -1;


    //轉碼之後打印一下字符串的相關信息
    printf("outbuf: %s len: %ld\n", outbuf, outbufSrc - outbuf);
    printf("utf16:\n");
    for(int i = 0; i < (int)(outbufSrc - outbuf); ++i)
    {
        printf("%02x ", outbuf[i]);
    }
    printf("\n");

    //釋放關閉charset_converter_t結構
    ret = charset_convert_close(converter);
    if(ret == -1)
        return -1;

    return 0;
}
  •  演示效果如下:與上面的演示案例①一致,但是不知道爲什麼會在前面補齊f

九、演示案例③

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