文章目录
本文重点介绍函数
- 求字符串长度 —— strlen
- 长度不受限制的字符串函数 —— strcpy、strcat、strcmp
- 长度受限制的字符串函数 —— strncpy、strncat、strncmp
- 字符串查找函数 —— strstr、strtok
- 错误信息报告函数 —— strerror
Ⅰ 字符操作函数
引用头文件
- <ctype.h>
⒈字符分类函数
函数名 | 函数功能 |
---|---|
iscntrl | 判断是否是控制字符 |
isspace | 判断是否是空白字符:空格‘ ’,换页‘\f’,换行’\n’,回车‘\r’,制表符’\t’或者垂直制表符’\v’ |
sdigit | 判断是否是十进制数字: 0~9 |
isxdigit | 判断是否是十六进制数字:包括所有十进制数字,小写字母 a ~ f,大写字母 A ~ F |
islower | 判断是否是小写字母:a ~ z |
isupper | 判断是否是大写字母 A ~ Z |
isalpha | 判断是否是字母:a ~ z 或 A ~ Z |
isalnum | 判断是否是字母或者数字:a ~ z,A ~ Z,0 ~ 9 |
ispunct | 判断是否是标点符号:任何不属于数字或者字母的图形字符(可打印) |
isgraph | 判断是否是任何图形字符 |
isprint | 判断是否是任何可打印字符,包括图形字符和空白字符 |
⒉字符转换函数
函数名 | 函数功能 |
---|---|
toupper | 将小写字母转换成大写字母 |
tolower | 将大写字母转换成小写字母 |
函数用例
int main()
{
char str[] = "i WANT TO PLAY bLACK mYTH wUKONG!";
for (int i = 0; i < strlen(str); i++)
{
if (islower(str[i]))
{
str[i] = toupper(str[i]);
}
else if (isupper(str[i]))
{
str[i] = tolower(str[i]);
}
}
printf("%s\n", str);
return 0;
}
Ⅱ 字符串操作函数
字符串函数相关知识
- 字符串末尾一定悄悄的藏着一个 ’ \0 '。
- 字符串函数引用头文件:<string.h>。
- 只能对字符串进行操作,有 ’ \0 ’ 这个限制条件
⒈strlen
求字符串长度
size_t strlen ( const char * str );
- size_t 是个无符号整形,想打印 strlen 函数的返回值最好使用 %zd 。
函数功能
- 求字符串长度,返回 ’ \0 ’ 之前出现的所有字符个数。
函数参数
- 一个字符串的首字符的地址。
函数用例
⒉strcpy
拷贝字符串
char * strcpy ( char * destination, const char * source );
函数功能
- 将 source 指向的字符串(包含 ’ \0 ')复制到 destination 指向的数组中,然后返回 destination 空间的起始地址。
- 为了避免溢出,destination 指向的数组的大小应该 足够容纳 source 指向的串中得所有内容。
函数参数
- source:指向被复制的字符串。
- destination:指向要在其中复制内容的目标数组。
返回值
- destination 数组的起始地址。
函数用例
⒊strcat
连接字符串
char * strcat ( char * destination, const char * source );
函数功能
- 将一个字符串(包含 ’ \0 ')连接到另一个字符串的后面。
- 要求目标数组里已经包含一个字符串(可以是空串),找到目标字符串的末尾并将源字符串 cv 过去。
函数参数
- source:要追加的字符串,与目的地的内存不能重叠。
- destination:指向目标数组的指针,该数组应包含一个字符串,并且足够大以包含连接的结果字符串。
返回值
- destination 数组的起始地址。
函数用例
⒋strcmp
比较字符串
int strcmp ( const char * str1, const char * str2 );
函数功能
- 比较两个字符串的是否相同。
- 从第一个字符开始,依次对比两个字符串中每个对应字符的 ASCII 码值的大小。如果第一个字符串的字符的 ASCII 码小于第二个字符串对应字符的 ASCII 码,则返回一个小于 0 的值;如果大于,则返回一个大于 0 的值。
函数参数
- str1:指向第一个字符串的起始地址。
- str2:指向第二个字符串的起始地址。
函数用例
⒌strncpy
指定拷贝字符数
char * strncpy ( char * destination, const char * source, size_t num );
函数功能
- 在实现程序复制的时候,应该限制源字符串的长度,确保目标数组在执行完复制后不会发生溢出。
- 于是就有了 strncpy。在 strcpy 的基础上添加了一个参数,用来指定拷贝的字符个数。
函数参数
- source:指向被复制的字符串。
- destination:指向要在其中复制内容的目标数组。
- num:指定从 source 指向的字符串中拷贝 num 个字符到 destination 指向的目标数组中。
函数用例
⒍strncat
指定连接字符数
char * strncat ( char * destination, const char * source, size_t num );
函数功能
- 指定从 source 指向的串中连接 num 个字符到 destination 指向的字符串的末尾。
- 与 strncpy 不同的是,strncat 总是在连接后自动追加一个结束符(’ \0 ')。
函数用例
⒎strncmp
指定比较字符数
int strncmp ( const char * str1, const char * str2, size_t num );
函数功能
- 指定比较两个字符串的前 num 个字符。
函数用例
⒏strstr
找字符串子串
const char * strstr ( const char * str1, const char * str2 );
char * strstr ( char * str1, const char * str2 );
函数功能
- 在 str1 指向的字符串中查找是否存在字符串 str2。
- 看看 str1 里面是否包含 str2,如果包含则返回首次出现 str2 字符的起始地址,反之则扔一个空指针回来。
函数参数
- str1:指向待查找的字符串。
- str2:指向被查找的字符串。
函数用例
⒐strtok
分割字符串
char * strtok ( char * str, const char * delimiters );
函数功能
- 按照 delimiters 中提供的分割符,将 str 中出现提供的分割符的位置改成 \0,并将 str 这个串在此处结尾。
char str[] = "wwwoBlack_myth_wukong0com";
char* deli = "o_0";
- 按照 deli 提供的字符对 str 进行分割;
- 将 str 中 o_0 这几个字符的位置换成 \0,www\0Black_myth_wukong0com
- 然后返回分隔的位置之前的串的首地址
函数参数
- str:指向待被分割的字符串。
- delimiters:用作分割符的字符(可以是一个字符,也可以是一个集合)。
返回值
- 返回被分解的第一个子字符串的首字符地址。
- 若无可检索的字符串,则返回 NULL1。
注意事项
- strtok 函数会改变被操作的字符串,所以在使用 strtok 函数切分的字符串一般都是临时拷贝的内容并且可修改。
- strtok 的第一个参数不为 NULL 时,函数将找到 str 中第一个分割符,strtok 函数将保存它在字符串中的位置。
- strtok 的第一个参数是为 NULL 时,函数将在同一个字符串中被保存的位置开始,查找下一个分割符。
- 如果 strtok 函数找不到分割符,则返回 NULL。
函数用例
- strtok 函数会记录 str 串中出现分割符的位置,不用担心传 NULL 会找不到 str。
⒑strerror
错误报告函数
char * strerror ( int errnum );
函数功能
- 返回错误码所对应的错误信息。
- 错误码对应的错误信息不需要去记,C 语言的库函数在执行失败的时候,都会设置错误码。
函数用例
实际使用
-
如果在可能发生错误的情况下需要先判断错误信息,这个时候时没有提供错误码的。
-
此时就需要将错误码先记录在 errno 变量里,直接将 errno 交给 strerror 即可,就不用每次手动输入错误码那么挫了。
-
errno:C 语言设置的一个存放错误码的全局变量。引用头文件:<errno.h>。当出现新的错误码时,会将旧的覆盖掉,始终记录最新的。
Ⅲ 模拟实现字符串函数
- 模拟实现字符串函数,就得保证自定义函数的(参数、返回值)同库函数一致。
⒈模拟实现 strlen
1. 计数器法
size_t my_strlen(const char* str)
{
assert(str);
size_t len = 0;
while (*str++)
{
len++;
}
return len;
}
2. 递归法
size_t my_strlen(const char* str)
{
assert(str);
if (*str != '\0')
{
return 1 + my_strlen(str + 1);
}
else
{
return 0;
}
}
3. 指针 - 指针法
size_t my_strlen(const char* str)
{
assert(str);
char* start = str;
char* end = start;
while (*++end != '\0');
return end - start;
}
⒉模拟实现 strcpy
char* my_strcpy(char* dest, const char* sour)
{
assert(dest && sour);
char* start = dest;; //源头的地址放到目标空间中会导致目标空间的地址被改变,先用 start 保留目标空间的地址
while (*dest++ = *sour++); //将 sour 指向的包含 '\0' 的字符串赋给 dest 指向的空间
return start; //返回目标空间的起始地址
}
⒊模拟实现 strcat
char* my_strcat(char* dest, const char* sour)
{
assert(dest && sour);
char* start = dest; //记录目标空间的起始地址
while (*dest) //找到目标空间的 '\0' 就停止
{
dest++;
}
while (*dest++ = *sour++); //将 sour 指向的串连接过去
return start; //返回目标空间的起始地址
}
⒋模拟实现 strcmp
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2) //对应字符 ASCII 相等才会往后找不同
{
if ('\0' == *str1) //在对应字符相等的情况下,*st1 = '\0',说明 *str1 和 *str2 都等于 '\0'
{
return 0; //此时两个字符串完全相等
}
str1++;
str2++;
}
return *str1 - *str2; //返回对应字符 ASCII 码的差值
}
代码优化
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1++ == *str2++); //循环结束后 str1 和 str2 分别指向两个不想等字符的后一位
return *(str1 - 1) - *(str2 - 1); //重新指向两个不相等的字符然后解引用,用 ASCII 码值做差
}
⒌模拟实现 strncpy
char* my_strncpy(char* dest, const char* sour, size_t num)
{
assert(dest && sour);
int i = 0;
//strncpy 是不会在末尾自动补 \0 的
for (i = 0; i < num; i++)
{
*(dest + i) = *(sour + i);
}
return dest;
}
⒍模拟实现 strncat
char* my_strncat(char* dest, const char* sour, size_t num)
{
assert(dest && sour);
char* start = dest;
while (*++dest); //找到串 1 的 '\0' 的位置
for (int i = 0; i < num; i++)
{
if (!(*dest++ = *sour++)) //两个串任何一个遇到 '\0' 都直接结束函数调用
{
return start;
}
}
*dest = '\0'; //在末尾补上一个 '\0'
return start;
}
⒎模拟实现 strncmp
int my_strncmp(const char* str1, const char* str2, size_t num)
{
assert(str1 && str2);
while (*str1++ == *str2++ && --num); //比较到第 num 个字符循环 num - 1 次即可
return *(str1 - 1) - *(str2 - 1); //让两个指针重新指向不相等字符的位置
}
⒏模拟实现 strstr
解题思路
结束条件
- s2 -> ‘\0’:当 s2 指向 \0 时,说明 str2 的全部连续的内容已经在 str1 中找到了,str2 是 str1 的子串,返回 p(str1 首次出现 str2 的地址)即可。
- p -> ‘\0’:当 p 指向 \0 时说明直到走到 str1 的尽头,s2 都没走到 \0,str1 中没有包含 str2 的子串,返回一个空指针即可。
代码实现
const char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* s1 = str1; //记录str1位置
const char* s2 = str2; //记录str2位置
const char* p = str1; //记录起始位置
while (*p) //*p 指向 \0 时说明主串已经走到头了
{
s1 = p; //字符不相等时让 s1 指向 p 的位置
s2 = str2; //字符不相等时让 s2 回到 str2 的初始位置
while (*s1 && *s2 && *s1 == *s2) //字符相等且 s1,s2 都未指向 \0 时比较下一对字符
{
s1++;
s2++;
}
if ('\0' == *s2) //s2 走到 \0 时说明 str2 是 str1 的子串
{
return p; //返回在 str1 中首次出现 str2 的地址
}
p++; //不相等时让 s1 从 p 的下一个位置开始比较
}
return NULL; //都走到这步了说明 str2 肯定不是 str1 的子串
}