文章目錄
- 前言
- 字符函數和字符串函數
- 1.字符分類函數
- 2.字符轉換函數
- 3.strlen的使用和模擬實現
- 3.1 代碼演示
- 3.2 strlen返回值
- 3.3 strlen的模擬實現
- 4.strcpy的使用和模擬實現
- 4.1 代碼演示
- 4.2 模擬實現
- 5.strcat的使用和模擬實現
- 5.1 代碼演示
- 5.2 模擬實現
- 6.strcmp的使用和模擬實現
- 6.1 代碼演示:
- 6.2 模擬實現:
- 7.strncpy函數的使用
- 7.1 代碼演示
- 7.2 ?較strcpy和strncpy函數
- 8.strncat函數的使用
- 8.1 代碼演示
- 8.2 strcat和strncat對比
- 9.strncmp函數的使用
- 9.1 代碼演示
- 9.2 strcmp和strncmp?較
- 10.strstr的使用和模擬實現
- 10.1 代碼演示
- 10.2 strstr的模擬實現
- 11.strtok函數的使用
- 11.1 代碼演示
- 11.2 注意事項
- 12.strerror函數的使用
- 12.1 代碼演示
- 12.2 perror
前言
在編程的過程中,我們經常要處理字符和字符串,為了?便操作字符和字符串,C語?標準庫中提供了 ?系列庫函數,接下來我們就學習?下這些函數。
字符函數和字符串函數
1.字符分類函數
C語?中有?系列的函數是專?做字符分類的,也就是?個字符是屬于什么類型的字符的。 這些函數的使?都需要包含?個頭?件是 ctype.h
這些函數的使??法?常類似,我們就講解?個函數的事情,其他的?常類似:
int islower ( int c );
islower 是能夠判斷參數部分的 c 是否是?寫字?的。 通過返回值來說明是否是?寫字?,如果是?寫字?就返回?0的整數,如果不是?寫字?,則返回 0。
練習:
寫?個代碼,將字符串中的?寫字?轉?寫,其他字符不變。
#include <stdio.h>
#include <ctype.h>
int main ()
{int i = 0;char str[] = "Test String.\n";char c;while (str[i]){c = str[i];if (islower(c)) c -= 32;putchar(c);i++;}return 0;
}
也可以寫成
while(str[i]!='\0')
{if(islower(str[i])){str[i]=toupper(str[i]);}i++;
}
2.字符轉換函數
C語?提供了2個字符轉換函數:
int tolower ( int c ); //將參數傳進去的?寫字?轉?寫
int toupper ( int c ); //將參數傳進去的?寫字?轉?寫
上?的代碼,我們將?寫轉?寫,是-32完成的效果,有了轉換函數,就可以直接使? tolower 函 數。
#include <stdio.h>
#include <ctype.h>
int main ()
{int i = 0;char str[] = "Test String.\n";char c;while (str[i]){c = str[i];if (islower(c)) c = toupper(c);putchar(c);i++;}return 0;
}
3.strlen的使用和模擬實現
size_t strlen ( const char * str );//size_t是無符號整型
功能:統計參數 str 指向的字符串的?度。統計的是字符串中 ‘\0’ 之前的字符的個數。
參數:
? str :指針,指向了要統計?度的字符串。 返回值:返回了 str 指向的字符串的?度,返回的?度不會是負數,所以返回類型是 size_t 。
if((strlen("abc")-strlen("abcdef"))>0)//size_t為無符號整型,所以3-6一定大于0
{printf(">\n");//所以輸出>
}
else
{printf("<=\n");
}
解決方案
(int)strlen("abc")-(int)strlen("abcdef")
3.1 代碼演示
#include <stdio.h>
#include <string.h>
int main()
{const char* str = "abcdef";printf("%zd\n", strlen(str));return 0;
}
使?注意事項:
? 字符串以 ‘\0’ 作為結束標志,strlen函數返回的是在字符串中 ‘\0’ 前?出現的字符個數(不包 含 ‘\0’ )。
? 參數指向的字符串必須要以 ‘\0’ 結束。
? 注意函數的返回值為 size_t ,是?符號的( 易錯 )
? strlen的使?需要包含頭?件
3.2 strlen返回值
#include <stdio.h>
#include <string.h>
int main()
{const char* str1 = "abcdef";const char* str2 = "bbb";if(strlen(str2) - strlen(str1) > 0)//size_t類型恒大于0{printf("str2 > str1\n");} else{printf("srt1 > str2\n");}return 0;
}
3.3 strlen的模擬實現
?式1:
//計數器?式
int my_strlen(const char * str)
{int count = 0;assert(str);//assert斷言 頭文件<assert.h>while(*str){count++;str++;}return count;
}
?式2:
//不能創建臨時變量計數器
int my_strlen(const char * str)
{assert(str);if(*str == '\0')return 0;elsereturn 1 + my_strlen(str+1);//str+1是下一個字符的地址
}
比如
my_strlen("abcdef")=1+my_strlen("bcdef")
?式3:
//指針-指針的?式
int my_strlen(char *s)
{assert(str);char *p = s;while(*p != '\0')p++;return p - s;
}
4.strcpy的使用和模擬實現
string copy
char* strcpy(char * destination, const char * source );
功能:字符串拷?,拷?到源頭字符串中的 \0 為?
參數:
destination :指針,指向?的地空間
source :指針,指向源頭數據
返回值:
strcpy 函數返回的?標空間的起始地址
4.1 代碼演示
#include <stdio.h>
#include <string.h>
int main()
{char arr1[10] = {0};char arr2[] = "hello";strcpy(arr1, arr2);//\0也會拷貝進去printf("%s\n", arr1);return 0;
}
使?注意事項:
?源字符串必須以 ‘\0’ 結束.。
? 會將源字符串中的 ‘\0’ 拷?到?標空間。
? ?標空間必須?夠?,以確保能存放源字符串。
? ?標空間必須可修改。(不能是常量字符串,因為常量字符串不可以修改)
4.2 模擬實現
#include <stdio.h>
#include <assert.h>
//1.參數順序
//2.函數的功能,停?條件
//3.assert
//4.const修飾指針
//5.函數返回值
char* my_strcpy(char *dest, const char*src)//src不能修改
{ char *ret = dest;//dest++后變了 所以先存起來assert(dest != NULL);assert(src != NULL);while((*dest++ = *src++))//先用再++,當*src為\0時,終止循環{;}return ret;
}
int main()
{char arr1[10] = {0};char arr2[] = "hello";my_strcpy(arr1, arr2);printf("%s\n", arr1);return 0;
}
void my_strcpy(char* dest,char* src)
{
//拷貝\0前面的內容
while(*src!='\0'){*dest=*src;dest++;src++;}*dest=*src;//拷貝\0
}
5.strcat的使用和模擬實現
連接字符串
char * strcat ( char * destination, const char * source );
功能:字符串追加,把 source 指向的源字符串中的所有字符都追加到 destination 指向的空間 中。
參數:
destination :指針,指向?的地空間
source :指針,指向源頭數據
返回值:
strcat 函數返回的?標空間的起始地址
5.1 代碼演示
#include <stdio.h>
#include <string.h>
int main()
{char arr1[20] = "hello ";char arr2[] = "world";strcat(arr1, arr2);printf("%s\n", arr1);return 0;//輸出hello world
}
使用注意事項:
? 源字符串必須以 ‘\0’ 結束。
? ?標字符串中也得有 \0 ,否則沒辦法知道追加從哪?開始。
? ?標空間必須有?夠的?,能容納下源字符串的內容。
? ?標空間必須可修改。
5.2 模擬實現
#include <stdio.h>
#include <assert.h>
char* my_strcat(char *dest, const char*src)
{char *ret = dest;assert(dest != NULL);assert(src != NULL);//這兩行等價于assert(dest&&src)//1.找到目標空間中的\0while(*dest)//while(*dest!='\0'){dest++;}while((*dest++ = *src++)){;}return ret;
}
int main()
{char arr1[20] = "hello ";char arr2[] = "world";my_strcat(arr1, arr2);printf("%s\n", arr1);return 0;
}
那如果是自拼接呢
my_strcat(arr1,arr1)
//自拼接會:1.死循環2.系統崩潰
6.strcmp的使用和模擬實現
兩個字符串比較應用場景 登錄: 輸入 :用戶名 密碼
? 數據庫 用戶名 密碼
int strcmp ( const char * str1, const char * str2 );
功能:?來?較 str1 和 str2 指向的字符串,從兩個字符串的第?個字符開始?較,如果兩個字符 的ASCII碼值相等,就?較下?個字符。直到遇到不相等的兩個字符,或者字符串結束。
參數:
str1 :指針,指向要?較的第?個字符串
str2 :指針,指向要?較的第?個字符串
返回值:
? 標準規定:
? 第?個字符串?于第?個字符串,則返回?于0的數字
? 第?個字符串等于第?個字符串,則返回0
? 第?個字符串?于第?個字符串,則返回?于0的數字
6.1 代碼演示:
#include <stdio.h>
#include <string.h>
int main()
{char arr1[] = "abcdef";char arr2[] = "abq";int ret = strcmp(arr1, arr2);printf("%d\n", ret);if(ret > 0)printf("arr1 > arr2\n");else if(ret == 0)printf("arr1 == arr2\n");elseprintf("arr1 < arr2\n");return 0;
}
6.2 模擬實現:
int my_strcmp (const char * str1, const char * str2)
{int ret = 0 ;assert(str1 != NULL);assert(str2 != NULL);while(*str1 == *str2){if(*str1 == '\0')return 0;str1++;str2++;}return *str1-*str2;//最后比較的ASII碼值的差
}
7.strncpy函數的使用
strcpy strcat strcmp 是長度不受限的字符串比較 不安全
strncpy strncat strncmp 長度受限函數
char * strncpy ( char * destination, const char * source, size_t num );
功能:字符串拷?;將 source 指向的字符串拷?到 destination 指向的空間中,最多拷? num 個字符。
參數:
destination :指針,指向?的地空間
source :指針,指向源頭數據
num :從source指向的字符串中最多拷?的字符個數
返回值:
strncpy 函數返回的?標空間的起始地址
7.1 代碼演示
#include <stdio.h>
#include <string.h>
int main()
{char arr1[20] = {0};char arr2[] = "abcdefghi";char* str = strncpy(arr1, arr2, 5);printf("%s\n", arr1);printf("%s\n", str);return 0;
}
長度足夠,讓拷貝幾個就幾個,長度小于num補\0
7.2 ?較strcpy和strncpy函數
strcpy 函數拷?到 \0 為?,如果?標空間不夠的話,容易出現越界?為。
strncpy 函數指定了拷?的?度,源字符串不?定要有 \0 ,同時在設計參數的時候,就會多?層 思考:?標空間的??是否夠?, strncpy 相對 strcpy 函數更加安全。
8.strncat函數的使用
char * strncat ( char * destination, const char * source, size_t num );
功能:字符串追加;將 source 指向的字符串的內容,追加到 destination 指向的空間,最多追 加 num 個字符。
參數:
destination :指針,指向了?標空間
source :指針,指向了源頭數據
num :最多追加的字符的個數
返回值:返回的是?標空間的起始地址
8.1 代碼演示
#include <stdio.h>
#include <string.h>
int main()
{char arr1[20] = "hello ";char arr2[] = "world";char* str = strncat(arr1, arr2, 5);//從\0開始追加,追加萬還加\0printf("%s\n", arr1);printf("%s\n", str);//若num>長度,則追加\0return 0;
}
8.2 strcat和strncat對比
? 參數不同, strncat 多了?個參數
? strcat 函數在追加的時候要將源字符串的所有內容,包含 \0 都追加過去,但是 strncat 函 數指定了追加的?度。
? strncat 函數中源字符串中不?定要有 \0 了。
? strncat 更加靈活,也更加安全。
9.strncmp函數的使用
int strncmp ( const char * str1, const char * str2, size_t num );
功能:字符串?較;?較 str1 和 str2 指向的兩個字符串的內容,最多?較 num 字符。
參數:
str1 :指針,指向?個?較的字符串
str2 :指針,指向另外?個?較的字符串
num :最多?較的字符個數
返回值:
? 標準規定:
? 第?個字符串?于第?個字符串,則返回?于0的數字
? 第?個字符串等于第?個字符串,則返回0
? 第?個字符串?于第?個字符串,則返回?于0的數字
9.1 代碼演示
#include <stdio.h>
#include <string.h>
int main()
{char arr1[] = "abcdef";char arr2[] = "abcqw";int ret1 = strncmp(arr1, arr2, 3);printf("%d\n", ret1);int ret2 = strncmp(arr1, arr2, 4);printf("%d\n", ret2);return 0;
}
9.2 strcmp和strncmp?較
? 參數不同
? strncmp可以?較任意?度了
? strncmp函數更加靈活,更加安全
10.strstr的使用和模擬實現
char * strstr ( const char * str1, const char * str2);
功能:strstr 函數,查找 str2 指向的字符串在 str1 指向的字符串中第?次出現的位置。 簡??之:在?個字符串中查找?字符串。 strstr 的使?得包含<string.h>
參數:
str1 :指針,指向了被查找的字符串
str2 :指針,指向了要查找的字符串
返回值:
? 如果str1指向的字符串中存在str2指向的字符串,那么返回第?次出現位置的指針
? 如果str1指向的字符串中不存在str2指向的字符串,那么返回NULL(空指針)
10.1 代碼演示
/* strstr example */
#include <stdio.h>
#include <string.h>
int main ()
{char str[] ="This is a simple string";char * pch;pch = strstr (str,"simple");if (pch != NULL)printf("%s\n", pch);elseprintf("查找的字符串不存在\n");return 0;
}
int main()
{char arr1[]="this is an apple\n";const char* p="is";//放的是i的地址char* ret = strstr(arr1,p);printf("%s\n",ret);return 0;
}
10.2 strstr的模擬實現
char * strstr (const char * str1, const char * str2)//str1 str2不希望被修改
{char *cp = (char *) str1;char *s1, *s2;//特殊情況:str2是空字符串時,直接返回str1 if ( !*str2 )return((char *)str1);while (*cp){s1 = cp;s2 = (char *) str2;while ( *s1 && *s2 && !(*s1-*s2) )s1++, s2++;if (!*s2)return(cp); //返回第?次出現的起始 cp++;}return(NULL); //找不到則返回NULL
}
先來一種簡單情況 在abcdef 中找 bcd
str1指向a時,不符合,所以++指向b,此時與*str2相等,然后str2++,再str1++,直到str2指向\0時結束
再來一種復雜情況
從第一個b開始時沒有
可能存在多次匹配
還得有兩個指針變量 存放他們兩個的起始變量 然后對他們++ 讓起始位置向后平移
char* my_strstr(const char* str1,const char* str2)
{const char* s1=NULL;const char* s2=NULL;const char* cur=str1;while(*cur){s1=cur;s2=str2;while(*s1!='\0'&&*s2!='\0'&&*s1==*s2){s1++;s2++;}if(*s2=='\0'){return char* cur;}cur++;}return NULL;
}
strstr函數的實現有多種,可以暴?查找,也有?種?效?些的算法:KMP,有興趣的可以去學習。
11.strtok函數的使用
char *strtok(char *str, const char *delim);
//delim參數指向了一個字符串,定義了用作分隔符的字符的集合
功能
? 分割字符串:根據delim 參數中指定的分隔符,將輸?字符串str 拆分成多個?字符串。
? 修改原始字符串: strtok 會直接在原始字符串中插?’\0’ 終?符,替換分隔符的位置,因 此原始字符串會被修改。
參數
1.str :?次調?時傳?待分割的字符串;后續調?傳?NULL ,表?繼續分割同?個字符串。
2.delim :包含所有可能分隔符的字符串(每個字符均視為獨?的分隔符)。
返回值
? 成功時返回指向當前?字符串的指針。
? 沒有更多?字符串時返回NULL 。
使?步驟
- ?次調?:傳?待分割字符串和分隔符。
- 后續調?:傳?NULL 和相同的分隔符,繼續分割。
- 結束條件:當返回NULL 時,表?分割完成。
11.1 代碼演示
int main()
{char arr[]="fnianxu@yeah.net";char arr2[30]={0};//"fnianxu\0yeah\0net"strcpy(arr2,arr);const char* sep="@.";//傳入NULL在\0之后繼續進行char* ret=NULL;//初始化ret = strtok(arr2,sep);printf("%s\n",ret);ret = strtok(NULL,sep);printf("%s\n",ret);ret = strtok(NULL,sep);printf("%s\n",ret);
}
運行結果如下:
但一直傳入空指針有些麻煩,所以看以下示例
#include <stdio.h>
#include <string.h>
int main()
{char arr[] = "192.168.6.111";const char* sep = ".";const char* str = NULL;char buf[30] = {0};strcpy(buf, arr); //將arr中的字符串拷?到buf中,對buf的內容進?切割 for (str = strtok(buf, sep); str != NULL; str = strtok(NULL, sep)){printf("%s\n", str);}return 0;
}
11.2 注意事項
? 破壞性操作: strtok 會直接修改原始字符串,將其中的分隔符替換為’\0’ 。如果需要保留原字符串,應先拷??份。
? 連續分隔符:多個連續的分隔符會被視為單個分隔符,不會返回空字符串。
? 空指針處理:如果輸?的str 為NULL 且沒有前序調?,?為未定義。
12.strerror函數的使用
char* strerror ( int errnum );
功能
1.strerror 函數可以通過參數部分的errnum 表示錯誤碼,得到對應的錯誤信息,并且返回這個錯誤信息字符串?字符的地址。
2.strerror 函數只針對標準庫中的函數發?錯誤后設置的錯誤碼的轉換。
3.strerror 的使?需要包含<string.h>
在不同的系統和C語?標準庫的實現中都規定了?些錯誤碼,?般是放在 errno.h 這個頭?件中說 明的,C語?程序啟動的時候就會使??個全局的變量 errno 來記錄程序的當前錯誤碼,只不過程序啟動的時候errno 是0,表?沒有錯誤,當我們在使?標準庫中的函數的時候發?了某種錯誤, 就會將對應的錯誤碼,存放在 errno 中,??個錯誤碼的數字是整數,很難理解是什么意思,所 以每?個錯誤碼都是有對應的錯誤信息的。strerror函數就可以將錯誤碼對應的錯誤信息字符串的地 址返回。
參數:
errnum :表示錯誤碼
這個錯誤碼?般傳遞的是 errno 這個變量的值,在C語?有?個全局的變量叫: errno ,當庫函數 的調?發?錯誤的時候,就會講本次錯誤的錯誤碼存放在 errno 這個變量中,使?這個全局變量需要 包含?個頭?件 errno.h 。
返回值:
函數返回通過錯誤碼得到的錯誤信息字符串的?字符的地址。
12.1 代碼演示
#include <errno.h>
#include <string.h>
#include <stdio.h>
//我們打印?下0~10這些錯誤碼對應的信息
int main()
{int i = 0;for (i = 0; i <= 10; i++) {printf("%d: %s\n", i, strerror(i));}return 0;
}
在Windows11+VS2022環境下輸出的結果如下:
0: No error
1: Operation not permitted
2: No such file or directory
3: No such process
4: Interrupted function call
5: Input/output error
6: No such device or address
7: Arg list too long
8: Exec format error
9: Bad file descriptor
10: No child processes
舉例:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{FILE * pFile = NULL;//fopen函數以讀的形式打開?件,如果?件不存在,則打開失敗。 pFile = fopen ("unexist.ent", "r");if (pFile == NULL){printf ("錯誤信息是:%s\n", strerror(errno));return 1;//錯誤返回 }//讀文件 fclose(pf);//關閉文件return 0;//空指針不能解引用
}
輸出:
錯誤信息是:No such file or directory
12.2 perror
也可以了解?下 perror 函數, perror 函數相當于?次將上述代碼中的第11?完成了,直接將錯誤 信息打印出來。 perror 函數打印完參數部分的字符串后,再打印?個冒號和?個空格,再打印錯誤 信息。
perror有能力直接打印錯誤信息,打印的時候,先打印傳給perror的字符串,然后打印冒號,再打印空格,最后打印錯誤碼對應信息
perrpr=printf+strerror
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{FILE * pFile = NULL;pFile = fopen ("unexist.ent", "r");if (pFile == NULL){perror("錯誤信息是");return 1;}return 0;
}
輸出:
錯誤信息是: No such file or directory