柚子快報(bào)激活碼778899分享:c語言 字符函數(shù)和字符串函數(shù)
柚子快報(bào)激活碼778899分享:c語言 字符函數(shù)和字符串函數(shù)
字符函數(shù)和字符串函數(shù)
1.字符分類函數(shù)
C語言中有一系列的函數(shù)時(shí)專門做字符分類的,也就是一個(gè)字符是屬于什么類型的字符的。
這些函數(shù)的使用都需要包含一個(gè)頭文件ctype.h
# include
# include
int main()
{
int ret = islower('a'); // 只能傳一個(gè)字符
printf("%d\n", ret);// 2 如果給的不是小寫字母 那么就是返回0
return 0;
}
//int main()
//{
// int ret = isspace (' '); // 判斷是否是空白字符 空格' ' 換行\(zhòng)n 回車\r 如果是返回非0值 不說是返回0
// printf("%d\n", ret);// 8
// return 0;
//}
// 還有很多功能的字符函數(shù) 可以通過查詢庫函數(shù) 去查找實(shí)現(xiàn)的功能
2.字符轉(zhuǎn)換函數(shù)
// 比如我們之前是如何實(shí)現(xiàn)大寫轉(zhuǎn)小寫的呢 ,是通過遍歷數(shù)組 ,小寫的通過ASCII碼-32實(shí)現(xiàn)的
// 其實(shí)我們可以通過toupper
int main()
{
char arr[] = "I am a Student";
int i = 0;
while (arr[i] != '\0')
{
/*if (arr[i] >= 'a' && arr[i] <= 'z')*/ // 這個(gè)是之前我們的思路
//if(islower(arr[i])) // 小寫轉(zhuǎn)大寫
//{
// arr[i] = toupper(arr[i]);
//}
//i++;
if (isupper(arr[i])) // 大寫轉(zhuǎn)小寫
{
arr[i] = tolower(arr[i]);
}
i++;
}
printf("%s\n", arr); // I AM A STUDENT
return 0;
}
3.strlen函數(shù)
size_t strlen ( const char * str );
字符串已經(jīng) ‘\0’ 作為結(jié)束標(biāo)志,strlen函數(shù)返回的是在字符串中 ‘\0’ 前面出現(xiàn)的字符個(gè)數(shù)(不包
含 ‘\0’ )
參數(shù)指向的字符串必須要以 ‘\0’ 結(jié)束。 注意函數(shù)的返回值為size_t,是無符號(hào)的( 易錯(cuò) ) 學(xué)會(huì)strlen函數(shù)的模擬實(shí)現(xiàn)
strlen的函數(shù)的模擬實(shí)現(xiàn)我們已經(jīng)在之前指針那一塊我們復(fù)現(xiàn)過了
在這邊講一個(gè)strlen函數(shù)容易出錯(cuò)的地方【注意函數(shù)的返回值為size_t,是無符號(hào)的( 易錯(cuò) )】
#include
int main()
{
const char*str1 = "abcdef";
const char*str2 = "bbb";
if(strlen(str2)-strlen(str1)>0)
// 如果我們想實(shí)現(xiàn)這個(gè)代碼的功能我們需要用int強(qiáng)制轉(zhuǎn)換類型
{
printf("str2>str1\n");
// 為什么執(zhí)行的是這邊呢 因?yàn)?size_t是無符號(hào)類型 所以不管怎么計(jì)算都是正數(shù)
}
else
{
printf("srt1>str2\n");
}
return 0;
}
4.strcpy的使用和模擬實(shí)現(xiàn)
上面是strcpy函數(shù)的細(xì)節(jié)
int main()
{
char arr1[] = "hello world";
char arr2[20] = { 0 };// 這個(gè)arr2不能是常量字符串 要是數(shù)組這樣的可修改對(duì)象
strcpy(arr2, arr1); // arr2 作為終點(diǎn) 講arr1的內(nèi)容復(fù)制到arr2里面
// 在遇到\0的時(shí)候復(fù)制才會(huì)結(jié)束
printf("%s\n", arr2);// \0 也被復(fù)制到arr2里面了
return 0;
}
實(shí)現(xiàn)對(duì)strcpy的模擬復(fù)現(xiàn):
// 實(shí)現(xiàn)對(duì)strcpy的復(fù)現(xiàn)
// 第一個(gè)版本
/*
void my_strcpy(char* dest, char* src)
{
while (*src != '\0')
{
*dest = *src;
dest++;
src++;
}
*dest = *src;
}
int main()
{
char arr1[] = "hello world";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
*/
// 進(jìn)行優(yōu)化
# include
// 為什么返回類型是char* 呢 因?yàn)樾枰祷啬繕?biāo)空間的起始地址 也就是dest的起始地址
char* my_strcpy(char* dest, const char* src)// const的目的是為了src所指向的對(duì)象不被修改
{
char* ret = dest;// 用ret將arr2的起始地址存起來
assert(src != NULL);// 判斷輸入進(jìn)來的src是否為空指針
assert(dest != NULL);
while (*dest++ = *src++)// 因?yàn)?\0'的ASCII碼是0
//當(dāng)* src 不是 \0(字符串結(jié)束符)時(shí),執(zhí)行指針的移動(dòng)和字符賦值操作。
// 一旦遇到 \0,由于 \0 在條件表達(dá)式中被解釋為 false,循環(huán)就會(huì)停止。
{
;
}
return ret;
}
int main()
{
char arr1[] = "hello world";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
5.strcat的使用和模擬實(shí)現(xiàn)
// strcat函數(shù) 用來鏈接字符串
int main()
{
char arr1[20] = "hello ";
char arr2[20] = "world";
strcat(arr1, arr2);// 把a(bǔ)rr2的字符串追加到 arr1中
printf("%s\n", arr1);
return 0;
}
對(duì)strcat進(jìn)行模擬實(shí)現(xiàn):
//對(duì)strcat函數(shù)進(jìn)行復(fù)現(xiàn)
# include
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*dest != '\0')// 先找到arr1 的'\0'
dest++;
while (*dest++ = *src++)// 開始把a(bǔ)rr2的元素鏈接到arr1的后面
;
return ret;// 此時(shí)的arr1和arr2 已經(jīng)完成鏈接 那么返回arr1的首元素地址 外面打印出arr1就是連接完畢的arr1
}
int main()
{
char arr1[20] = "hello ";
char arr2[20] = "world";
char* p = my_strcat(arr1, arr2);
printf("%s\n", arr1);
printf("%s\n", p);// p指向arr1的首元素地址
return 0;
學(xué)會(huì)了strcat的使用后 我們來思考一下 可以不可以讓字符串本身給自己追加字符串呢
// strcat函數(shù) 自己給自己追加字符串
int main()
{
char arr1[20] = "hello ";
strcat(arr1, arr1);// 把a(bǔ)rr2的字符串追加到 arr1中
printf("%s\n", arr1); // 我們發(fā)現(xiàn)庫函數(shù)的stract的函數(shù)可以實(shí)現(xiàn)
return 0;
}
但是實(shí)際上我們自己模擬實(shí)現(xiàn)的strcat函數(shù)是無法實(shí)現(xiàn)自己給自己追加字符串的
# include
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest != NULL);
assert(src != NULL);
while (*dest != '\0')// 先找到arr1 的'\0'
dest++;
while (*dest++ = *src++)// 原因是這里會(huì)一直覆蓋數(shù)據(jù) 導(dǎo)致src根本找不到'\0' 陷入死循環(huán)
;
return ret;
}
int main()
{
char arr1[20] = "hello ";
my_strcat(arr1, arr1);
printf("%s\n", arr1);
return 0;
}
6.strcmp 函數(shù)的使用和復(fù)現(xiàn)
我們知道兩個(gè)字符串的大小是無法直接比較的
如果我們直接比較字符串的話 那么比較的是地址
if("abc"== "aaa")// 這里比較的是兩個(gè)字符串的地址
想要比較兩個(gè)字符串的大小 那么就需要我們strcmp函數(shù)來實(shí)現(xiàn)
其實(shí)我們在之前的學(xué)習(xí)就已經(jīng)接觸過strcmp函數(shù)了
我們來看一下strcmp是如何使用的
strcmp是比較字符串的ASCII碼的
實(shí)例
// strcmp函數(shù) 兩個(gè)字符串的比較
int main()
{
char arr1[] = "abcde";
char arr2[] = "aaaaa";
int ret = strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
strcmp的模擬復(fù)現(xiàn):
// strcmp 函數(shù)的模擬復(fù)現(xiàn)
# include
int my_strcmp(const char* str1,const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
if (*str1 > *str2)
{
return 1;
}
else
{
return -1;
}
// 也可以這樣寫
// return *str1 - *str2;
}
int main()
{
char arr1[] = "abcde";
char arr2[] = "aaaaa";
int ret = strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
7.strncpy函數(shù)的使用和復(fù)現(xiàn)
strncpy的使用
int main()
{
char arr1[] = "abcde";
char arr2[] = "xxxxxxxxx";
strncpy(arr2, arr1, 4);// abcdxxxxx
// 添加了一個(gè)形參作為截?cái)帱c(diǎn) 4指的是復(fù)制幾個(gè)arr1的元素到arr2上
printf("%s\n", arr2);
return 0;
}
strncpy的模擬復(fù)現(xiàn):
// strncpy的模擬復(fù)現(xiàn)
# include
char* my_strncpy(char* dest, const char* str, size_t len)
{
char* ret = dest;
assert(str != NULL);// 判斷輸入進(jìn)來的str是否為空指針
assert(dest != NULL);
for (int i = 0; i < len; i++)
{
*dest++ = *str++;
}
*dest = '\0';// 添加字符串結(jié)束符
return ret;
}
int main()
{
char arr1[] = "abcde";
char arr2[] = "xxxxxxxxx";
my_strncpy(arr2, arr1, 4);// abcdxxxxx
// 添加了一個(gè)形參作為截?cái)帱c(diǎn) 4指的是復(fù)制幾個(gè)arr1的元素到arr2上
printf("%s\n", arr2);
return 0;
}
8.strncat的使用
// strncat的使用
int main()
{
char arr1[20] = "hello ";
char arr2[20] = "xxxx\0xxxx";
strncat(arr2, arr1, 4);// 把a(bǔ)rr1的字符串追加到 arr2中 4代表追加的arr1的元素個(gè)數(shù)
printf("%s\n", arr2); //
return 0;
}
*/
9.strncmp的使用
//strncmp的使用 也是多傳入一個(gè)參數(shù)
int main()
{
char arr1[20] = "hello ";
char arr2[20] = "hallo";
int ret = strncmp(arr1,arr2,3);// 多傳入的參數(shù)表示 比較幾次 3 代表只比較前面三個(gè)字符
printf("%d\n", ret);
return 0;
}
10.總結(jié)
前面我們學(xué)習(xí)了strcpy和 strncpy 等等
我們除了知道它們之間功能的微小差別之外我們也要考慮多出來的功能到底是為了什么
其實(shí)類似 strcpy 函數(shù)是非限制長度函數(shù)
strcpy 是限制長度函數(shù) 相對(duì)更加安全
因?yàn)槎嗔艘粋€(gè)需要考慮的參數(shù) 那么用起來相對(duì)就是更安全一些
11.strstr的使用和模擬
功能是在一個(gè)字符串中查找另一個(gè)字符串
char* strstr(const char* str1 , const char* str2);
如果str2 在str1 中壓根沒有出現(xiàn) 那么該函數(shù)就返回空指針
如果找到了返回的就是在str1找到的地址
該函數(shù)位于#include
strstr的使用:
#include
int main()
{
char arr1[] = "this is apple\n";
const char* p = "is";
//const char* p = "c"; // 找不到c就會(huì)打印NULL
char* ret = strstr(arr1, p);
printf("%s\n", ret);// is is apple 因?yàn)椴檎业絠s的地址就是 this里的i
return 0;
}
strstr的模擬實(shí)現(xiàn):(難度較大)
首先我們需要考慮幾種情況
第一種情況(最簡單的情況)
“abcdeg” 讓s1去指向這個(gè)字符串
“abc” 讓s2去指向這個(gè)字符串
一旦s2遇到了\0 那么就是找到了 這個(gè)時(shí)候要返回首元素地址
我們讓cur指向第一個(gè)字符串的首地址 找到了就返回cur指針
第二種情況(復(fù)雜)
abbbcde
bbc
我們發(fā)現(xiàn)這種情況 要復(fù)雜一些 因此我們需要不斷讓s1和s2 去對(duì)比 一旦發(fā)現(xiàn)不一樣那么就是沒找到
那么就要讓cur++ 重新再來對(duì)比一次 因此這里我們可以考慮while循環(huán)
一旦找到了 也就是s2遇到了 \0 這個(gè)時(shí)候就返回cur
第三章情況(簡單)
abc
d
根本沒找到 也就是s1 找到了\0 這個(gè)時(shí)候說明找不到 那么直接返回空指針就行了 要跳出循環(huán)
第四種情況(特殊情況)
abd
abd
這個(gè)時(shí)候 兩個(gè)\0 但是其實(shí)我們已經(jīng)找到了 那么也要跳出 查詢的循環(huán)
有了上面的分析我們就可以嘗試復(fù)現(xiàn)了
// strstr函數(shù)的模擬實(shí)現(xiàn)
// 簡單的寫法 - 暴力查找
// 其實(shí)還有一個(gè)KMP查找 也是在字符串中找字符串 效率更加高
#include
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 != NULL); // 判斷傳進(jìn)來的是不是空指針
assert(str2 != NULL);
const char* s1 = NULL;// 我們并不希望s1 和 s2 和 cur指向的對(duì)象被改變
const char* s2 = NULL;// 先給一個(gè)空指針 后面在賦值
const char* cur = str1;//我們需要一個(gè)指針來記住起始位置 因?yàn)樵诓檎业臅r(shí)候s1 和 s2 會(huì)一直往后走 無法記住起始位置
if (*str2 == '\0')// 如果別人傳進(jìn)來的是一個(gè)空字符串 那么傳進(jìn)來的就是\0 就返回str1 的地址
return (char*)str1;
// 開始查詢 這個(gè)過程有可能是很漫長的 因此我們使用while循環(huán)
while (*cur)// 當(dāng)cur找到\0的時(shí)候 意味著沒有查詢到 那么就跳出循環(huán)
{
s1 = cur;
s2 = str2;
// 當(dāng)我們遇到了相同的字符準(zhǔn)備往后查找的時(shí)候 這個(gè)過程也可能是很多次的 那么我們也用while循環(huán)
while (*s1 !='\0'&& *s2 != '\0' && *s1 == *s2)// 如果是abd 和 abd 那么\0 \0 也是相等的 要排除這個(gè)特殊情況
{
s1++;
s2++;
}
if (*s2 == '\0')
return (char*)cur;
cur++;
}
return NULL;// 跳出while循環(huán)說明 在str1, 找不到str2 那么返回空指針
}
int main()
{
char arr1[] = "this is apple\n";
char arr2[] = "app";
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
printf("找不到一點(diǎn)\n");
else
printf("%s\n", ret);
}
11.strtok 函數(shù)的使用
char* strtok(char* str, const char* sep);
我們來看一個(gè)實(shí)例:
// strtok 函數(shù)的使用
# include
int main()
{
char arr[] = "123456@yeah.com";// 這個(gè)實(shí)際上會(huì)被strtok函數(shù)切割成 123456\0yeah\0com\0
// 每次都會(huì)記住切割的地方的地址
char arr2[30] = { 0 };
strcpy(arr2, arr);
const char* sep = "@.";
char* ret = NULL;
// 這個(gè)for循環(huán)非常巧妙
for (ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
/*ret = strtok(arr2, sep);
printf("%s\n", ret);
ret = strtok(NULL, sep);
printf("%s\n", ret);
ret = strtok(NULL, sep);
printf("%s\n", ret);*/ /*這個(gè)寫法不好 需要我們自己去數(shù) */
return 0;
}
12.strerror函數(shù)的使用
char* strerror(int errnum);
// strerror 函數(shù)的使用
# include
# include
int main()
{
for (int i = 0; i <= 10; i++)
{
printf("%d : %s\n", i, strerror(i));
}
//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
return 0;
}
我們來驗(yàn)證一下:
// 我們來驗(yàn)證一下 strerror
# include
# include
int main()
{
// fopen 以讀的形式打開文件的時(shí)候,如果文件不存在,就打開失敗
FILE* pf = fopen("test.txt", "r");
//if (pf == NULL)
//{
// printf("%s\n", strerror(errno)); // No such file or directory
// return 1;
//}
if (pf == NULL)
{
perror("Error"); // 有能力直接打印錯(cuò)誤信息 Error: No such file or directory
return 1;
}
return 0;
}
柚子快報(bào)激活碼778899分享:c語言 字符函數(shù)和字符串函數(shù)
好文推薦
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。