C++代碼判斷字符編碼類型及編碼格式轉(zhuǎn)換(utf-8耀石、gbk)

這篇文章主要是將go語言實現(xiàn)的版本改為C/C++版本實現(xiàn)牵囤,主要思路是一樣的,具體思路請看:
GO代碼實現(xiàn)判斷字符編碼格式及編碼格式轉(zhuǎn)換(utf-8滞伟、gbk)
而本文更主要說明windows及l(fā)inux平臺下utf-8與gbk的轉(zhuǎn)換揭鳞。


判斷是否是gbk

bool isGBK(unsigned char* data, int len)  {
    int i  = 0;
    while (i < len)  {
        if (data[i] <= 0x7f) {
            //編碼小于等于127,只有一個字節(jié)的編碼,兼容ASCII
            i++;
            continue;
        } else {
            //大于127的使用雙字節(jié)編碼
            if  (data[i] >= 0x81 &&
                data[i] <= 0xfe &&
                data[i + 1] >= 0x40 &&
                data[i + 1] <= 0xfe &&
                data[i + 1] != 0xf7) {
                i += 2;
                continue;
            } else {
                return false;
            }
        }
    }
    return true;
}

判斷是否是utf-8

int preNUm(unsigned char byte) {
    unsigned char mask = 0x80;
    int num = 0;
    for (int i = 0; i < 8; i++) {
        if ((byte & mask) == mask) {
            mask = mask >> 1;
            num++;
        } else {
            break;
        }
    }
    return num;
}


bool isUtf8(unsigned char* data, int len) {
    int num = 0;
    int i = 0;
    while (i < len) {
        if ((data[i] & 0x80) == 0x00) {
            // 0XXX_XXXX
            i++;
            continue;
        }
        else if ((num = preNUm(data[i])) > 2) {
        // 110X_XXXX 10XX_XXXX
        // 1110_XXXX 10XX_XXXX 10XX_XXXX
        // 1111_0XXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
        // 1111_10XX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
        // 1111_110X 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
        // preNUm() 返回首個字節(jié)8個bits中首??0bit前面1bit的個數(shù)梆奈,該數(shù)量也是該字符所使用的字節(jié)數(shù)        
        i++;
        for(int j = 0; j < num - 1; j++) {
            //判斷后面num - 1 個字節(jié)是不是都是10開
            if ((data[i] & 0xc0) != 0x80) {
                    return false;
                }
                i++;
        }
    } else {
        //其他情況說明不是utf-8
        return false;
    }
    } 
    return true;
}

utf-8 與 gbk 互轉(zhuǎn)

1. windows

windows 下utf-8與gbk的轉(zhuǎn)換借助Unicode編碼來實現(xiàn)野崇,這篇博客[http://www.reibang.com/p/42d275097336]講得很詳細。主要過程即:
utf-8 >> unicode >> gbk
gbk >> unicode >> utf-8
通過window提供的API(包含在windows.h中):MultiByteToWideChar()和MultiByteToWideChar()兩個函數(shù)實現(xiàn)的亩钟。官方明確提醒使用這兩個函數(shù)要十分小心可能發(fā)生的內(nèi)存溢出問題乓梨,例如一個本來為2字節(jié)的字符串轉(zhuǎn)換后變成4字節(jié)了,如果只分配原來字符串的空間大小径荔,必然會發(fā)生內(nèi)存溢出督禽。
解決辦法:先調(diào)用一次求出需要的字符串空間長度,分配好空間后再調(diào)用一次完成轉(zhuǎn)換总处。具體請參考下面的用例狈惫。

2. linux

linux下通過 iconv.h 提供的API來進行轉(zhuǎn)換
主要有三個API:

//指定轉(zhuǎn)換類型,返回一個轉(zhuǎn)換描述符,iconv_open("GBK", "UTF-8") 將utf-8字符串轉(zhuǎn)為gbk胧谈,
//可以轉(zhuǎn)化的類型可以通過指令 iconv --list 查看忆肾。
iconv_t iconv_open(const char *tocode, const char *fromcode);

//inbuf ,outbuf分別傳入指向buff的指針的地址菱肖,outbf調(diào)用后指向buff中已保存字符串的末尾客冈,即結(jié)束符;
//inbytesleft是一個輸入輸出參數(shù)稳强,傳入一個保存有需轉(zhuǎn)換字符串長度的變量的地址场仲,成功調(diào)用后該變量為0,表示字符串所有字節(jié)均已被轉(zhuǎn)換退疫;
//outbytesleft是一個輸入輸出參數(shù)渠缕,傳入一個保存outbuf總長度的變量的地址,調(diào)用結(jié)束后該變量保存outbuf剩下的可用長度褒繁。
size_t iconv(iconv_t cd,
             char **inbuf, size_t *inbytesleft,
             char **outbuf, size_t *outbytesleft);
//關(guān)閉打開的轉(zhuǎn)換描述符
iconv_close(cd);

windows/linux 跨平臺用例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <windows.h>
#else
#include <iconv.h> 
#endif

int preNUm(unsigned char byte) {
    unsigned char mask = 0x80;
    int num = 0;
    for (int i = 0; i < 8; i++) {
        if ((byte & mask) == mask) {
            mask = mask >> 1;
            num++;
        } else {
            break;
        }
    }
    return num;
}


bool isUtf8(unsigned char* data, int len) {
    int num = 0;
    int i = 0;
    while (i < len) {
        if ((data[i] & 0x80) == 0x00) {
            // 0XXX_XXXX
            i++;
            continue;
        }
        else if ((num = preNUm(data[i])) > 2) {
        // 110X_XXXX 10XX_XXXX
        // 1110_XXXX 10XX_XXXX 10XX_XXXX
        // 1111_0XXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
        // 1111_10XX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
        // 1111_110X 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX 10XX_XXXX
        // preNUm() 返回首個字節(jié)8個bits中首??0bit前面1bit的個數(shù)亦鳞,該數(shù)量也是該字符所使用的字節(jié)數(shù)        
        i++;
        for(int j = 0; j < num - 1; j++) {
            //判斷后面num - 1 個字節(jié)是不是都是10開
            if ((data[i] & 0xc0) != 0x80) {
                    return false;
                }
                i++;
        }
    } else {
        //其他情況說明不是utf-8
        return false;
    }
    } 
    return true;
}

bool isGBK(unsigned char* data, int len)  {
    int i  = 0;
    while (i < len)  {
        if (data[i] <= 0x7f) {
            //編碼小于等于127,只有一個字節(jié)的編碼,兼容ASCII
            i++;
            continue;
        } else {
            //大于127的使用雙字節(jié)編碼
            if  (data[i] >= 0x81 &&
                data[i] <= 0xfe &&
                data[i + 1] >= 0x40 &&
                data[i + 1] <= 0xfe &&
                data[i + 1] != 0xf7) {
                i += 2;
                continue;
            } else {
                return false;
            }
        }
    }
    return true;
}
typedef enum {
    GBK,
    UTF8,
    UNKOWN
} CODING;
//需要說明的是棒坏,isGBK()是通過雙字節(jié)是否落在gbk的編碼范圍內(nèi)實現(xiàn)的燕差,
//而utf-8編碼格式的每個字節(jié)都是落在gbk的編碼范圍內(nèi)??
//所以只有先調(diào)用isUtf8()先判斷不是utf-8編碼,再調(diào)用isGBK()才有意義
CODING GetCoding(unsigned char* data, int len) {
    CODING coding;
    if (isUtf8(data, len) == true) {
        coding = UTF8;
    } else if (isGBK(data, len) == true) {
        coding = GBK;
    } else {
        coding = UNKOWN;
    }
    return coding;
}
int main() {
    char src[512] = "你好";
    int len = strlen(src);
    //printf("%s, len:%d\n",src, len);
    char dstgbk[512] = {0};
    char dstutf8[512] = {0};
    printf("coding:%d\n", GetCoding((unsigned char*)src, len));   //判斷是否是utf-8
    #ifndef WIN32
    iconv_t  cd;
    char* pSrc = src;
    char* pUTFOUT = dstutf8;
    char* pGBKOUT = dstgbk;
    size_t srcLen = (size_t)len;
    size_t outLenUTF = sizeof(dstutf8);
    size_t outLenGBK = sizeof(dstgbk);
    size_t ret;
    #endif
    #ifdef WIN32
    wchar_t * pUnicodeBuff = NULL;
    int rlen = 0;
    rlen = MultiByteToWideChar(CP_UTF8, 0, src, -1, NULL ,NULL);
    pUnicodeBuff =  new WCHAR[rlen + 1];  //為Unicode字符串空間
    rlen = MultiByteToWideChar(CP_UTF8, 0, src, -1, pUnicodeBuff, rlen); 
    rlen = WideCharToMultiByte(936, 0, pUnicodeBuff, -1, NULL, NULL, NULL, NULL); //936 為windows gb2312代碼頁碼
    WideCharToMultiByte(936, 0, pUnicodeBuff ,-1, dstgbk, rlen, NULL ,NULL);
    delete[] pUnicodeBuff;
    #else
    cd = iconv_open("GBK", "UTF-8");
    if (cd == (iconv_t)-1) {
        printf("iconv_open error\n");
    }
    ret = iconv(cd, &pSrc, &srcLen, &pGBKOUT, &outLenGBK);
    iconv_close(cd);
    #endif

    //printf("%s, len:%d\n",dstgbk, strlen(dstgbk));
    printf("coding:%d\n", GetCoding((unsigned char*)dstgbk, strlen(dstgbk)));   //判斷是否是gbk

    #ifdef WIN32
    rlen = MultiByteToWideChar(936, 0, dstgbk, -1, NULL, NULL);
    pUnicodeBuff =  new WCHAR[rlen + 1];  //為Unicode字符串空間
    rlen = MultiByteToWideChar(936, 0, dstgbk, -1, pUnicodeBuff, rlen); 
    rlen = WideCharToMultiByte(CP_UTF8, 0, pUnicodeBuff, -1, NULL, NULL, NULL, NULL);
    WideCharToMultiByte(CP_UTF8, 0, pUnicodeBuff, -1, dstutf8, rlen, NULL, NULL);
    delete[] pUnicodeBuff;
    #else
    cd = iconv_open("UTF-8", "GBK");
    if (cd == (iconv_t)-1) {
        printf("iconv_open error\n");
    }
    //pSrc = pGBKOUT; 錯誤坝冕,上面調(diào)用過一次此時iconv()后徒探,pGBKOUT指向的是dstgbk[512]可用的位置,
    //即dstgbk[512]保存gbk字符串的后一位徽诲,也就是結(jié)束符的位置
    pSrc = dstgbk;
    srcLen = strlen(dstgbk);
    ret = iconv(cd, &pSrc, &srcLen, &pUTFOUT, &outLenUTF);
    iconv_close(cd);
    #endif

    //printf("%s, len:%d\n",dstutf8, strlen(dstutf8));
    printf("coding:%d\n", GetCoding((unsigned char*)dstutf8, strlen(dstutf8)));   //判斷是否是utf-8
    getchar();
}
windows結(jié)果.png

linux結(jié)果.png

打印出枚舉類型1,0,1刹帕,代表utf-8,gbk,utf-8

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谎替,隨后出現(xiàn)的幾起案子偷溺,更是在濱河造成了極大的恐慌,老刑警劉巖钱贯,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挫掏,死亡現(xiàn)場離奇詭異,居然都是意外死亡秩命,警方通過查閱死者的電腦和手機尉共,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弃锐,“玉大人袄友,你說我怎么就攤上這事∨眨” “怎么了剧蚣?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我鸠按,道長礼搁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任目尖,我火速辦了婚禮馒吴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瑟曲。我一直安慰自己饮戳,他們只是感情好,可當我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布测蹲。 她就那樣靜靜地躺著莹捡,像睡著了一般鬼吵。 火紅的嫁衣襯著肌膚如雪扣甲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天齿椅,我揣著相機與錄音琉挖,去河邊找鬼。 笑死涣脚,一個胖子當著我的面吹牛示辈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播遣蚀,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼矾麻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了芭梯?” 一聲冷哼從身側(cè)響起险耀,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎玖喘,沒想到半個月后甩牺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡累奈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年贬派,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澎媒。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡搞乏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出戒努,到底是詐尸還是另有隱情请敦,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站冬三,受9級特大地震影響匀油,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜勾笆,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一敌蚜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窝爪,春花似錦弛车、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至邀杏,卻和暖如春贫奠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背望蜡。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工唤崭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脖律。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓谢肾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親小泉。 傳聞我的和親對象是個殘疾皇子芦疏,可洞房花燭夜當晚...
    茶點故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容