06
—
高級(jí)字符串查找
? ? 接下來(lái)的一組函數(shù)簡(jiǎn)化了從一個(gè)字符串中查找和抽取一個(gè)子串的過(guò)程细办。
1.查找一個(gè)字符串前綴
? ? strspn和strcspn函數(shù)用于在字符串的起始位置對(duì)字符計(jì)數(shù)懒熙。它們的原型如下:
size_t?strspn(char?const?*str,?char?const?*group);size_t?strcspn(char?const?*str,?char?const?*group);
? ? group字符串指定一個(gè)或多個(gè)字符昼弟。strspn返回str第一個(gè)不在字符串 group中出現(xiàn)的字符下標(biāo)啤它。例如,如果group包含了空格舱痘、制表符等空白字符变骡。
下面例子,
int?len1,?len2;char?buffer[]?=?"25.142.330,Smith,J,239-4123";?len1?=?strspn(buffer, "0123456789");len2?=?strspn(buffr,?".0123456789");
? ? 變量len1將被置為2衰粹,變量len2將被置為11锣光。下面的代碼將計(jì)算第一個(gè)指向字符串中第一個(gè)非空白字符的指針。
ptr?=?buffer?+?strspn(buffer,?"\n\r\f\t");
? ? strcspn函數(shù)和strspn函數(shù)正好相反铝耻,它將返回第一個(gè)在group中的字符的下標(biāo)誊爹。strcspn這個(gè)詞中的字母c來(lái)源于對(duì)一組字符求補(bǔ)這個(gè)概念。
2.查找標(biāo)記
? ? 一個(gè)字符串常常包含了幾個(gè)有意義的部分瓢捉,它們被分隔符分隔開(kāi)來(lái)频丘。每次為了處理這些部分,你首先必須把它們從字符串中抽取出來(lái)泡态。
? ? 這個(gè)任務(wù)正是strtok函數(shù)所實(shí)現(xiàn)的功能搂漠。它從字符串中隔離各個(gè)單獨(dú)的稱為標(biāo)記(token)的部分,并丟棄分隔符某弦。它的原型如下:
char *strtok(char *str, char const *sep);
? ? sep參數(shù)是個(gè)字符串桐汤,定義了用作分隔符的字符集合。第1參數(shù)指定一個(gè)字符串靶壮,它包含零個(gè)或多個(gè)由sep字符串中一個(gè)或多個(gè)分隔符分隔的標(biāo)記怔毛。strtok找到str的下一個(gè)標(biāo)記,并將其用NUL結(jié)尾腾降,然后返回一個(gè)指向這個(gè)標(biāo)記的指針拣度。若沒(méi)有可檢索的字符串,則返回一個(gè)空指針螃壤。
? ? 下面是一個(gè)簡(jiǎn)短的例子抗果,
char?str[80]?=?"This?is?-?cxsjcrmdjt?-?official?account";const?char?sep[2]?=?"-";char *token;/* 獲取第一個(gè)子字符串 */token = strtok(str, sep);/* 繼續(xù)獲取其他子字符串 */while(token?!= NULL){? ? printf("%s\n", token);? ? token = strtok(NULL, s);}
? ? 編譯并運(yùn)行上面程序,這將產(chǎn)生以下結(jié)果:
This is
cxsjcrmdjt
official account
? ? 如果你愿意奸晴,你可以在每次調(diào)用strtok函數(shù)時(shí)使用不同的分隔符集合冤馏。當(dāng)一個(gè)字符串的不同部分由不同的字符集合分隔的時(shí)候,這個(gè)技巧很管用寄啼。
警告:
? ? 由于strtok函數(shù)保存它處理的函數(shù)的局部狀態(tài)信息宿接,所以你不能用它同時(shí)解析兩個(gè)字符串赘淮,因此,如果while循環(huán)體內(nèi)調(diào)用了一個(gè)在內(nèi)部調(diào)用strtok函數(shù)的函數(shù)睦霎,上述代碼將會(huì)失敗梢卸。
07
—
錯(cuò)誤信息
? ? 當(dāng)你調(diào)用一些函數(shù),請(qǐng)求操作系統(tǒng)執(zhí)行一些功能如打開(kāi)文件時(shí)副女,如果出現(xiàn)錯(cuò)誤蛤高,操作系統(tǒng)是通過(guò)設(shè)置一個(gè)外部的整型變量errno進(jìn)行錯(cuò)誤代碼報(bào)告的。strerror函數(shù)把其中一個(gè)錯(cuò)誤代碼作為參數(shù)并返回一個(gè)指向用于描述錯(cuò)誤的字符串的指針碑幅。這個(gè)函數(shù)的原型如下:
char *strerror(int error_number);
08
—
字符操作
? ? 標(biāo)準(zhǔn)庫(kù)包含了兩組函數(shù)戴陡,用于操作單獨(dú)的字符,它們的原型位于頭文件ctype.h沟涨。第一組函數(shù)用于對(duì)字符分類恤批,而第二組用于轉(zhuǎn)換字符。
1.字符分類
? ? 每個(gè)分類函數(shù)接受一個(gè)包含字符值的整型參數(shù)裹赴。函數(shù)測(cè)試這個(gè)字符并返回一個(gè)整型值喜庞,表示真或假。下表列出了這些分類函數(shù)以及它們每個(gè)所執(zhí)行的測(cè)試棋返。
?
2.字符串轉(zhuǎn)換
? ? 轉(zhuǎn)換函數(shù)可將大小寫(xiě)字母相互轉(zhuǎn)換延都。
int?tolower(int ch);int?toupper(int?ch);
? ? 如其函數(shù)名一樣,toupper函數(shù)返回對(duì)應(yīng)的大寫(xiě)形式睛竣,tolower函數(shù)返回對(duì)應(yīng)的小寫(xiě)形式晰房。如果函數(shù)的參數(shù)并不是一個(gè)可以大小寫(xiě)轉(zhuǎn)換的字符,函數(shù)將不修改參數(shù)直接返回射沟。
提示:
? ? 或許你會(huì)認(rèn)為這些函數(shù)的實(shí)現(xiàn)簡(jiǎn)單殊者,可以很輕易地被自己的實(shí)現(xiàn)替代,但是直接操控字符將會(huì)降低程序的可移植性验夯,考慮以下例子猖吴,它試圖測(cè)試ch是否是一個(gè)大寫(xiě)字符。
if?(ch?>=?'A'?&&?ch?<= 'Z')
? ? 這條語(yǔ)句在使用ASCII字符集的機(jī)器上能夠運(yùn)行簿姨,但在使用EBCDIC字符集的機(jī)器上將會(huì)失敗距误。若使用下面這條語(yǔ)句:
if (isupper(ch))
? ? 無(wú)論機(jī)器使用哪種字符集簸搞,它都能順利執(zhí)行扁位。
09
—
內(nèi)存操作
? ? 根據(jù)定義,字符串由一個(gè)NUL字節(jié)結(jié)尾趁俊,所以字符串內(nèi)部不能包含任何NUL字符域仇。但是,非字符串?dāng)?shù)據(jù)內(nèi)部包含零值的情況并不罕見(jiàn)寺擂,你無(wú)法使用字符串函數(shù)來(lái)處理這種類型的數(shù)據(jù)暇务,因?yàn)楫?dāng)它們遇到第一個(gè)NUL字節(jié)時(shí)將停止工作泼掠。
? ? 不過(guò),我們可以使用另外一組相關(guān)的函數(shù)垦细,它們的作用于字符串函數(shù)類似择镇,但這些函數(shù)能夠處理任意的字節(jié)序列。下面是它們的原型括改。
void?*memcpy(void *dst, void const *src, size_t length);void?*memmove(void?*dst,?void?const?*src,?size_t?length);void?*memcmp(void?const?*a,?void?const?*b,?size_t?length);void?*memchr(void?const?*a,?int?ch,?size_t?length);void?*memset(void?*a,?int?ch,?size_t?length);
? ? 每個(gè)原型都包含一個(gè)顯式的參數(shù)說(shuō)明需要處理的字節(jié)數(shù)腻豌。但和strn開(kāi)頭的函數(shù)不同,它們?cè)谟龅絅UL字節(jié)時(shí)并不會(huì)停止操作嘱能。
? ? memcpy從src的起始位置復(fù)制length個(gè)字節(jié)到dst的內(nèi)存起始位置吝梅。你可以用這種方法復(fù)制任何類型的值,第三個(gè)參數(shù)指定復(fù)制值的長(zhǎng)度(以字節(jié)計(jì))惹骂。如果src和dst以任何形式出現(xiàn)了重疊苏携,它的結(jié)果是未定義的。
例如对粪,
char?temp[SIZE],?values[SIZE];...memcpy(temp, values, SIZE);
? ? 它從數(shù)組values復(fù)制SIZE個(gè)字節(jié)到數(shù)組temp右冻。
? ? 但是,如果兩個(gè)數(shù)組都是整型數(shù)組該怎么辦衩侥?下面的語(yǔ)句可以完成這項(xiàng)任務(wù):
memcpy(temp, values, sizeof(values));
? ? 前兩個(gè)參數(shù)并不需要使用強(qiáng)制類型轉(zhuǎn)換国旷,因?yàn)樵诤瘮?shù)的原型中,參數(shù)的類型是void*型指針茫死,而任何類型的指針都可以轉(zhuǎn)換為void*型指針跪但。
? ? 如果數(shù)組只有部分內(nèi)容需要被復(fù)制,那么需要復(fù)制的數(shù)量必須在第三個(gè)參數(shù)中指明峦萎。對(duì)于長(zhǎng)度大于一個(gè)字節(jié)的數(shù)據(jù)屡久,要確保把數(shù)量和數(shù)據(jù)類型的長(zhǎng)度相乘,如:
memcpy(dst,?array,?count?*?sizoef(array[0]));
? ? 你也可以使用這種技巧復(fù)制結(jié)構(gòu)體或者結(jié)構(gòu)體數(shù)組爱榔。
? ? memmove函數(shù)的行為和memcpy差不多被环,只是它的源和目的操作數(shù)可以重疊。雖然它不需要以下面這種方式實(shí)現(xiàn)详幽,不過(guò)memmove的過(guò)程和這種方法的過(guò)程相同:把源操作數(shù)復(fù)制到一個(gè)臨時(shí)位置筛欢,這個(gè)臨時(shí)位置不會(huì)與源或目標(biāo)操作數(shù)重疊,然后再把它從這個(gè)臨時(shí)位置復(fù)制到目標(biāo)操作數(shù)唇聘。memmove通常無(wú)法使用某些機(jī)器所提供的特殊的字節(jié)-字符串處理指令來(lái)實(shí)現(xiàn)版姑,所以它可能比memcpy慢一些。但是迟郎,如果源和目標(biāo)參數(shù)真的可能存在重疊剥险,就應(yīng)該使用memmove。
? ? memcmp對(duì)兩端內(nèi)存的內(nèi)容進(jìn)行比較宪肖,這兩端內(nèi)存分別起始于a和b表制,共比較length個(gè)字節(jié)健爬。這些值按照無(wú)符號(hào)字符逐字節(jié)進(jìn)行比較,函數(shù)的返回類型和strcmp函數(shù)一樣----負(fù)值表示a小于b么介,正值表示a大于b娜遵,零表示a等于b。由于這些值是根據(jù)一串無(wú)符號(hào)字節(jié)進(jìn)行比較的壤短,所以如果memcmp函數(shù)用于比較不是單字節(jié)的數(shù)據(jù)如整數(shù)或浮點(diǎn)數(shù)時(shí)可能會(huì)出現(xiàn)不可預(yù)料的結(jié)果魔熏。
? ? memchr從a的起始位置開(kāi)始查找字符ch第一次出現(xiàn)的位置,并返回一個(gè)指向該位置的指針鸽扁,它共查找length個(gè)字節(jié)蒜绽。如果在這length個(gè)字節(jié)中未找到該字符,函數(shù)就返回一個(gè)NULL指針桶现。
? ? memset函數(shù)把從a開(kāi)始的length個(gè)字節(jié)都設(shè)置為值ch躲雅。如:
memset(buffer,?0,?SIZE);
? ? 把buffer的前SIZE個(gè)字節(jié)都初始化為0。
10
—
總結(jié)
? ? 字符串就是零個(gè)或多個(gè)字符的序列骡和。該序列以一個(gè)NUL字節(jié)結(jié)尾相赁,字符串的長(zhǎng)度就是它所包含的字符的數(shù)目。標(biāo)準(zhǔn)庫(kù)提供了處理字符串的函數(shù)慰于,它們的原型位于頭文件string.h中钮科。
? ? strlen函數(shù)用于計(jì)算一個(gè)字符串的長(zhǎng)度,它的返回值是一個(gè)無(wú)符號(hào)整數(shù)婆赠,所以把它用于表達(dá)式時(shí)應(yīng)該小心绵脯。strcpy函數(shù)把一個(gè)字符串從一個(gè)位置復(fù)制到另一個(gè)位置,而strcat函數(shù)把一個(gè)字符串的一份拷貝添加到另一個(gè)字符串的后面休里。這兩個(gè)函數(shù)都假定它們的參數(shù)是有效的字符串,而且如果源字符串和目標(biāo)字符串出現(xiàn)重疊,函數(shù)的結(jié)果是未定義的蛆挫。strcmp對(duì)兩個(gè)字符串進(jìn)行詞典序的比較。它的返回值提示第1個(gè)字符串是大于妙黍、小1還是等于第2個(gè)字符串悴侵。
? ? 長(zhǎng)度受限的函數(shù)strncpy, strncat和 strncmp都類似它們對(duì)應(yīng)的不受限制版本,區(qū)別在于這些函數(shù)還接受一個(gè)長(zhǎng)度參數(shù)。在strncpy中,長(zhǎng)度指定了多少個(gè)字符將被寫(xiě)入到目標(biāo)字符數(shù)組中拭嫁。如果源字符串比指定長(zhǎng)度更長(zhǎng),結(jié)果字符串將不會(huì)以NUL字節(jié)結(jié)尾可免。strncat函數(shù)的長(zhǎng)度參數(shù)指定從源字符串復(fù)制過(guò)來(lái)的字符的最大數(shù)目,但它的結(jié)果始終以一個(gè)NUL字節(jié)結(jié)尾, strcmp函數(shù)的K度參數(shù)用于限定字符比較的數(shù)目。如果兩個(gè)字符串在指定的數(shù)日里不存在區(qū)別,它們便被認(rèn)為是相等的做粤。
? ? 用于查找字符串的函數(shù)有好幾個(gè). strchr函數(shù)查找一個(gè)字符申中某個(gè)字符第1次出現(xiàn)的位置浇借。strrchr函數(shù)查找一個(gè)字符串中某個(gè)字符最后-次出現(xiàn)的位置。strpbrk在一個(gè)字符串4查找一個(gè)指定字符集中任意字符第1次出現(xiàn)的位置驮宴。strstr函數(shù)在一個(gè)字符串中查找另一個(gè)字符申第1次出現(xiàn)的位置逮刨。
? ? 標(biāo)準(zhǔn)庫(kù)還提供了了一些更高級(jí)的字符串查找函數(shù)呕缭。strspn函數(shù)計(jì)算一個(gè)字符串中第一個(gè)不在指定字符集出現(xiàn)的字符下標(biāo)堵泽。strtok函數(shù)把一個(gè)字符串分隔成幾個(gè)部分修己,每次調(diào)用它都返回一個(gè)指向下一個(gè)標(biāo)記位置的指針。
? ? sterror把一個(gè)錯(cuò)誤碼作為它的參數(shù)迎罗。它返回一個(gè)指向字符串的指針睬愤,該字符串用于描述這個(gè)錯(cuò)誤。
? ? 標(biāo)準(zhǔn)庫(kù)還提供了檢驗(yàn)和轉(zhuǎn)換字符的函數(shù)纹安。具體查表尤辱。
? ? memxxx函數(shù)提供了內(nèi)存操作的功能。?