C++遍歷英文字符串作者:非妃是公主
成都創(chuàng)新互聯(lián)公司主營華安網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營網(wǎng)站建設(shè)方案,app開發(fā)定制,華安h5小程序制作搭建,華安網(wǎng)站營銷推廣歡迎華安等地區(qū)企業(yè)咨詢 文章目錄
專欄:《筆記》《C++》
個性簽:順境不惰,逆境不餒,以心制境,萬事可成?!鴩?/mark>
- C++遍歷英文字符串
- C++遍歷中文字符串(不會出問題情況)
- C++遍歷中文字符串(會出現(xiàn)問題的情況)
- 英文字符的表示
- 中文字符的表示
- 定長編碼
- 變長編碼
- Unicode編碼
- 正確的中文字符串遍歷方式
- 參考資料
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)是可以正常輸出的!
再繼續(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編碼。
可以說,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)示意圖:
從圖中可以很容易地看出:
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變換格式的縮寫。
3+6+6+6=21
,仔細(xì)想一下,剛好覆蓋Unicode 字符集的編碼范圍是0x0000 - 0x10FFFF
,沒有任何問題。從圖中可以看到非妃是公主
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)查看詳情吧