真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

字符編碼詳解及利用C++STLstring遍歷中文字符串-創(chuàng)新互聯(lián)

作者:非妃是公主
專欄:《筆記》《C++》
個性簽:順境不惰,逆境不餒,以心制境,萬事可成?!鴩?/mark>

成都創(chuàng)新互聯(lián)公司主營華安網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,app開發(fā)定制,華安h5小程序制作搭建,華安網(wǎng)站營銷推廣歡迎華安等地區(qū)企業(yè)咨詢

在這里插入圖片描述

文章目錄
  • C++遍歷英文字符串
  • C++遍歷中文字符串(不會出問題情況)
  • C++遍歷中文字符串(會出現(xiàn)問題的情況)
  • 英文字符的表示
  • 中文字符的表示
    • 定長編碼
    • 變長編碼
    • Unicode編碼
  • 正確的中文字符串遍歷方式
  • 參考資料

C++遍歷英文字符串

C++遍歷英文字符串很簡單,基本有兩種方法

// 方法1
for(int i=0; i< str.size(); i++){cout<< str[i];
}

// 方法2
for(auto it = str.begin(); it != str.end(); it++){cout<< *it;
}

以上兩種方法基本就可以很好地遍歷英文字符串了!

C++遍歷中文字符串(不會出問題情況)

但是中文字符串呢?我們來試一下:
在這里插入圖片描述
發(fā)現(xiàn)是可以正常輸出的!

C++遍歷中文字符串(會出現(xiàn)問題的情況)

再繼續(xù)試!這次這樣嘗試,因為我們遍歷字符串一般都是要對字符串中的字符進(jìn)行操作,如果單純只是輸出或者顯示而已,沒必要去遍歷字符,直接cout<就可以了,所以接下來試一下字符的操作:
在這里插入圖片描述
這次添加了一個if (str[i] == '非') continue;的判斷,但是‘非’依然輸出了出來。
調(diào)試一下:
在這里插入圖片描述
i = 0,但是str[i] == '非'確實false,這是怎么回事呢?
我們知道,遍歷字符串主要就是要對字符串進(jìn)行操作,可是如今字符串判斷相等卻出現(xiàn)了問題……
這里給出測試代碼,感興趣的老哥可以自己去調(diào)試:

void test1() {string str = "非妃是公主";
    cout<< "這時test1:";
    for (int i = 0; i< str.size(); i++) {   
        if (str[i] == '非') continue;
        cout<< str[i];
    }
    cout<< endl;
}

void test2() {string str = "非妃是公主";
    cout<< "這時test2:";
    for (auto it = str.begin(); it != str.end(); it++) {cout<< *it;
    }
    cout<< endl;
}

int main() {test1();
    test2();
}
英文字符的表示

其實,這里涉及到了一個編碼的問題,ASCII碼值是一個字符集表,里面編碼了26個英文字母的大小寫(大寫字母65~90,小寫字母97~120),還有其它英文字符(比如空格、單引號……),其中有一些甚至是不可顯示的(比如換行符、分組符……)。具體可以查看ASCII碼表.

利用ASCII(美國信息交換標(biāo)準(zhǔn)代碼)就可以實現(xiàn)英文的字符映射了,因為英文字母只有那么些,所有的單詞都是根據(jù)這些字母進(jìn)行排列組合形成的。

下面為ASCII碼表的節(jié)選(開頭和結(jié)尾部分):
在這里插入圖片描述
在這里插入圖片描述
可以看到,從0~127,ASCII碼表有128個編碼,而 2 8 = 256 2^8=256 28=256,也就是說可以用1個字節(jié)(Byte,等于8個bit)大小的內(nèi)存空間來編碼所有的英文字符,因此char利用這樣的字節(jié)編碼到字符進(jìn)行映射就可以實現(xiàn)英文字符串的運算。

中文字符的表示

從上面不難看出,英文可以利用一個很小的字符集(ASCII——美國信息交換標(biāo)準(zhǔn)代碼)去表示所有單詞(因為只有26個字母等優(yōu)先的符號),但中文不可以,中華漢字博大精深,其中包含了幾千甚至上萬的漢字(如果還包括一些繁體字、生僻字等數(shù)量會更大)!

因此,1個Byte不能滿足中文編碼的需要,我們需要2個、3個甚至4個Byte進(jìn)行編碼才能把中文表示出來!

這里就包含了兩種編碼的方式,定長編碼和變長編碼。

定長編碼

定長編碼:顧名思義,就是每個字符對應(yīng)的編碼的長度都是相等的,這里不得不提到GB2312編碼和GBK編碼。

  • GB2312編碼:就是把漢字編碼成兩個字節(jié),一個字節(jié)有 2 8 = 256 2^8=256 28=256種不同的編碼,兩個字節(jié)就有 2 16 = 65536 2^{16}=65536 216=65536種不同的編碼,也就是說我們最多可以編碼65536種情況,這些對于常用的文字應(yīng)該可以了吧……但是,值得一提的是,GB2312并沒有使用完全這些編碼,它只用了一部分,那么剩下的呢?GB2312為了保持向下兼容ASCII,它避免了和ASCII進(jìn)行沖突編碼,這要浪費一部分編碼空間,但依然還是有空余的,這些空余下的位置暫且留著,GB2312沒有使用!
  • GBK編碼:和 GB2312 一樣,GBK 也是雙字節(jié)編碼,同樣為了向下兼容 GB2312, GBK使用了GB2312 沒有用到的那些編碼區(qū)域,簡單地說,就是進(jìn)一步拓展了編碼集,GBK比GB2312編碼了更多的漢字。

可以說,GBK編碼是對GB2312編碼的補充!

關(guān)于定長編碼的詳細(xì)規(guī)則可以在這篇文章里看到,總結(jié)的十分全面https://zhuanlan.zhihu.com/p/453675608

變長編碼

變長編碼:是一種包含多個長度編碼的字節(jié)結(jié)構(gòu)!換句話說,這種類型的編碼既可以使用1個字節(jié),也可以使用2個字節(jié),3個字節(jié),以及4個字節(jié),那么就來了一個問題,我怎么知道這個編碼到底是用了幾個字節(jié)呢?到底是1個字節(jié),2個字節(jié)還是3個、4個字節(jié)呢?也就是如何進(jìn)行解析呢?

這里,以GB18030編碼為例進(jìn)行說明,它也是一種變長編碼,有1個字節(jié),2個字節(jié)以及4個字節(jié)大小的編碼,下圖為GB18030編碼的字節(jié)結(jié)構(gòu)示意圖:
在這里插入圖片描述
從圖中可以很容易地看出:

  1. 前8位為00-80大小的,只有一個字節(jié)
  2. 前8位為81-FE,9~16位位40-FE的有兩個字節(jié),基本就是兼容GBK編碼(但是和GBK還是有區(qū)別的,詳細(xì)區(qū)別讀者可以自行查閱)
  3. 前8位位81-84,9-16位為30-39的,17-24位為81-FE的,25-32位為30-39的有4個字節(jié)!
  4. ……
  5. 根據(jù)表格,按照上述的字節(jié)區(qū)間編碼結(jié)構(gòu)就可以進(jìn)行解碼了。

4個字節(jié)可以表示更多的字符。

其實GB是國標(biāo)的意思:

國家標(biāo)準(zhǔn)GB18030-2000《信息交換用漢字編碼字符集基本集的補充》是我國繼GB2312-1980和GB13000-1993之后最重要的漢字編碼標(biāo)準(zhǔn),是我國計算機系統(tǒng)必須遵循的基礎(chǔ)性標(biāo)準(zhǔn)之一。
GB18030-2000編碼標(biāo)準(zhǔn)是由信息產(chǎn)業(yè)部和國家質(zhì)量技術(shù)監(jiān)督局在2000年3月17日聯(lián)合發(fā)布的,并且將作為一項國家標(biāo)準(zhǔn)在2001年的1月正式強制執(zhí)行。 GB18030-2005《信息技術(shù)中文編碼字符集》是我國制訂的以漢字為主并包含多種我國少數(shù)民族文字(如藏、蒙古、傣、彝、朝鮮、維吾爾文等)的超大型中文編碼字符集強制性標(biāo)準(zhǔn),其中收入漢字70000余個。

也就是說,這一個標(biāo)準(zhǔn)是我國制定的,并沒有在國際上通用!它只編碼了我國的漢字以及少數(shù)名族文字等。

Unicode編碼

Unicode 其實是一個字符集,這個字符集給世界上常用的字符都進(jìn)行了編碼,每一個字符對應(yīng)一個唯一的編碼。但值得注意的是,它并不是一個字符編碼,Unicode還需要依靠一些字符編碼規(guī)范,才能發(fā)揮作用,后面會提到。Unicode 字符集的編碼范圍是0x0000 - 0x10FFFF,相比于上面提到的字符編碼標(biāo)準(zhǔn)(帶GB的都是國標(biāo)的漢語拼音首字母,因此都是國內(nèi)的標(biāo)準(zhǔn)),Unicode是一個國際化的標(biāo)準(zhǔn)。換句話說,如果說GB2312、GBK、GB18030是國家級的字符編碼,那么Unicode就是一個國際級的字符集!

從上面提到的Unicode的范圍可以看出,如果直接編碼,我們只需要三個字符就可以編碼它。但是,比如第1個字符,如果用3個Byte進(jìn)行編碼,那么它的編碼應(yīng)該是0x000001,問題來了,前面的0并沒有包含什么信息,本來1個字節(jié)可以存儲的,卻消耗了3個字節(jié),這是一種存儲空間,以及計算機效率的浪費!

因此這里同樣采用邊長編碼,這也就解釋了上面為什么Unicode是字符集,而不是一種字符編碼了,因為如果直接使用它進(jìn)行編碼會浪費大量的空間和時間。

Unicode的編碼規(guī)則對應(yīng)utf-8、utf-16、utf-32,每個都代表一種不同的編碼規(guī)則,utf是Unicode transform format的縮寫,Unicode變換格式的縮寫。

  • utf-8編碼:是一種邊長編碼規(guī)則,可以使用1~4個字節(jié),具體地說:
    • 對于單字節(jié)的符號,字節(jié)的第一位設(shè)為 0,后面 7 位為這個符號的 Unicode 碼。因此對于英語字母,UTF-8 編碼和 ASCII 碼是相同的, 所以 UTF-8 能兼容 ASCII 編碼,這也是互聯(lián)網(wǎng)普遍采用 UTF-8 的原因之一。
    • 對于 n 字節(jié)的符號( n >1),第一個字節(jié)的前 n 位都設(shè)為 1,第 n + 1 位設(shè)為 0,后面字節(jié)的前兩位一律設(shè)為 10 。剩下的沒有提及的二進(jìn)制位,全部為這個符號的 Unicode 碼。
    • 那么緊接著就有一個問題,沒有占滿怎么半,答案是補0,從右往左占,高位占不滿的自動補0。
    • 還有1個問題,不夠位數(shù)怎么半,這個問題好解決,增加字節(jié)就是了。4個字節(jié)最多可以表示3+6+6+6=21,仔細(xì)想一下,剛好覆蓋Unicode 字符集的編碼范圍是0x0000 - 0x10FFFF,沒有任何問題。
  • utf16編碼:它也是一種變長編碼規(guī)則,但是它將字符編碼成2字節(jié)或者4字節(jié)。
    • 對于 Unicode 碼小于 0x10000 的字符, 使用 2 個字節(jié)存儲,并且是直接存儲 Unicode 碼,不用進(jìn)行編碼轉(zhuǎn)換;
    • 對于 Unicode 碼在 0x10000 和 0x10FFFF 之間的字符,使用 4 個字節(jié)存儲,這 4 個字節(jié)分成前后兩部分,每個部分各兩個字節(jié),其中,前面兩個字節(jié)的前 6 位二進(jìn)制固定為 110110,后面兩個字節(jié)的前 6 位二進(jìn)制固定為 110111, 前后部分各剩余 10 位二進(jìn)制表示符號的 Unicode 碼 減去 0x10000 的結(jié)果。20位bit正好可以表示0xFFFF。
    • 大于 0x10FFFF 的 Unicode 碼無法用 UTF-16 編碼。
  • utf-32編碼:UTF-32 是固定長度的編碼,始終占用 4 個字節(jié),足以容納所有的 Unicode 字符,所以直接存儲 Unicode 碼即可,不需要任何編碼轉(zhuǎn)換。雖然浪費了空間,但提高了效率。
正確的中文字符串遍歷方式

在這里插入圖片描述

從圖中可以看到非妃是公主5個字的漢字字符,但是,無論size和length都是10,這說明:length()和size()返回的并不是字符串的長度,而是字符串占用了多少個Byte。

進(jìn)一步推測:s[i]指的是第i個Byte,it++也指的是前進(jìn)1個Byte。

而GBK和GB13080都對GB2312向下兼容,而GB2312就包含了漢字中絕大多數(shù),部分生僻字和繁體字是不包含的,GB2312是用2個字節(jié)進(jìn)行表示的。

因此,這里i和i+1才能表示1個字符。

而2個Byte就不能用char(1個Byte)來表示了,string底層是由char實現(xiàn)的,而漢字至少包含兩個char的大小,所以要繼續(xù)用string來表示一個漢字:

遍歷算法應(yīng)該如下:

void test3() {string str = "非妃是公主";
    cout<< "這時test3:";
    for (int i = 0; i< str.size(); i = i + 2) {string tmp = "";
        tmp = tmp + str[i] + str[i + 1];
        if (tmp == "非") continue;
        cout<< tmp;
    }
    cout<< endl;
}

執(zhí)行結(jié)果如下:

在這里插入圖片描述

從輸出結(jié)果中可以看出if (tmp == "非") continue;已經(jīng)被執(zhí)行了。

同時,經(jīng)過調(diào)試可以發(fā)現(xiàn),因為字符串的==運算符應(yīng)該是經(jīng)過重載生成的,所以在調(diào)試時顯示沒有與操作數(shù)匹配的“==”運算符,無法進(jìn)行監(jiān)視。

但是從圖中可以看出,tmp的值已經(jīng)是“非”了。也實現(xiàn)了預(yù)期的結(jié)果,進(jìn)而可以實現(xiàn)字符串遍歷中對單個中文字符串的操作。

在這里插入圖片描述

同時也在交流群里向大佬交流了一下,大佬幫忙給找了一個參考代碼,此處一并貼出,并已標(biāo)明出處[5],我在這里加上注釋,對代碼進(jìn)行解釋,如下:

string text = "今天周五123";
for(size_t i = 0; i< text.length();)
{int cplen = 1;
    if((text[i] & 0xf8) == 0xf0) cplen = 4; 	 // 占用4個字節(jié),前5位為11110
    else if((text[i] & 0xf0) == 0xe0) cplen = 3; // 占用3個字節(jié),前4位為1110
    else if((text[i] & 0xe0) == 0xc0) cplen = 2; // 占用2個字節(jié),前3位為110
    // 個人感覺這行代碼好像沒什么用,如果三種情況都不符合,那么cplen就為初始化的0,是符合utf-8編碼定義的
    if((i + cplen) >text.length()) cplen = 1;
    cout<< text.substr(i, cplen)<< endl;
    i += cplen;
}

其實2個Byte基本已經(jīng)可以表示大多數(shù)中文了,除了極少的繁體字和生僻字,但是上面的代碼包含了3個Byte和4個Byte的情況,感嘆大佬的代碼確實更加完善!

最后還要感謝yyl1025老哥的答疑,問題已采納![6]

參考資料

[1] https://zhuanlan.zhihu.com/p/453675608
[2] https://zhuanlan.zhihu.com/p/427488961
[3] https://baike.baidu.com/item/ASCII/309296
[4] https://baike.baidu.com/item/GB18030/3204518
[5] https://stackoverflow.com/questions/40054732
[6] https://ask.csdn.net/questions/7874166?spm=1001.2014.3001.5505

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧


本文名稱:字符編碼詳解及利用C++STLstring遍歷中文字符串-創(chuàng)新互聯(lián)
瀏覽路徑:http://weahome.cn/article/cohhpe.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部