目录
前言
C语言中字符函数和字符串函数是非常重要的,可以方便我们对字符和字符串进行处理。本文中主要介绍几种常见的字符函数和字符串函数。
一.strlen求字符串长度
-
1. strlen函数
- strlen函数的介绍
-
size_t strlen ( const char * str );
- 字符串的结束标志是 \0
- strlen 函数是用来求字符串的长度的,是从字符串的第一个字符开始往后统计字符个数,
- 遇到 \0 就停止统计,如果没有遇到 \0 ,就会一直往后统计,直到遇到 \0 才会停止统计
- 所以strlen是用来统计字符串 \0 之前的字符个数的函数
- 需要注意的是strlen函数返回的是无符号整型
strlen函数返回的无符号整型的案例:
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf("hehe\0");//输出结果为hehe
}
else
{
printf("haha\n");
}
return 0;
}
分析:
strlen返回的是无符号整型,两个无符号整型相减的结果也应为无符号整型
所以应该将strlen("abc") - strlen("abcdef")的到的结果 -3 转为无符号整型,由于整型在内存中是以补码方式进行存储的,所以要先得到-3的补码,
然后再将补码的符号位改为0。
-3的原码为 10000000 00000000 00000000 00000011
-3的反码为 11111111 11111111 11111111 11111100
-3的补码为 11111111 11111111 11111111 11111101
转成无符号数 01111111 11111111 11111111 11111101
可以看出,-3转为无符号整型后的结果是一个非常大的数,
因此,上面代码的输出结果为hehe
实现strlen函数的三种方法
方法一:计数器
#include<assert.h>
size_t my_strlen(const char* str)
{
int count = 0;
assert(str != NULL);
while (*str != '\0')//while(*str)
{
count++;//统计个数
str++;
}
return count;
}
定义一个变量count来统计字符个数,如果该字符不是 \0 ,则count++,最后返回count的值
方法二:指针-指针
#include<assert.h>
size_t my_strlen(const char* str)
{
assert(str != NULL);//检测指针的有效性
char* start = str;//记录起始地址
while (*str != '\0')
{
str++;
}
return str - start;
}
指针-指针的绝对值是指针和指针之间元素的个数
要统计字符串中的字符个数,首先要得到该字符串的起始地址和结束地址(\0前的地址),然后将两地址相减,得到的就是字符串之间的字符个数
方法三:递归方式
size_t my_strlen(const char* str)
{
if (*str != '\0')
{
return 1 + my_strlen(str + 1);
}
else
{
return 0;
}
}
因为统计字符串的长度是先从首字符开始的,所以如果首字符是 \0 ,那么该字符串长度就为0,如果首字符不是 \0 的,那么该字符串中至少有1个不为 \0 的有效字符,字符串的长度就是1+后面字符的个数,依次类推
假设求的是字符串 “abcd” 的长度,那么用递归求解的具体步骤如下:
- 首字符是 a ,不是 \0 ,字符串 “abcd\0” 的长度为 1 + “bcd\0”的长度
- “bcd\0”首字符是 b ,不是 \0 ,字符串 "bcd\0" 的长度为 1 + “cd\0”的长度
- “cd\0”的首字符是 c ,不是 \0 ,字符串“cd\0”的长度为 1 + “d\0”的长度
- “d\0”的首字符是 d ,不是 \0 ,字符串“d\0”的长度为 1 + “\0” 的长度
- “\0”的首字符是 \0 ,由于字符串统计的是\0之前的字符个数,因此“\0”长度为0
- 最后得出字符串“abcdef”的长度为0+1+1+1+1 = 4
二.长度不受限制的字符串函数
1. strcpy字符串拷贝函数
strcpy 函数的介绍
char * strcpy ( char * destination, const char * source );
strcpy 是字符串拷贝函数
有两个形参,第一个形参是目标字符串,第二个形参是源字符串,该函数是将源字符串里的内容拷贝到目标字符串,当拷贝完源字符串里的 \0 后就停止拷贝,并返回目标空间的起始地址
需要注意的是:
- 目标字符串的空间一定要大于源字符串所占的空间大小
- 目标字符串必须可修改
- 要拷贝源字符串中的 \0
模拟实现strcpy
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);
//拷贝src中的字符串到dest指向的空间,包含\0
while (*dest++ = *src++)//当\0拷贝过去后,结果为假,停止循环
{
;
}
return ret;//返回目标空间的起始地址
}
思路如下:
1.找到目标字符串的起始地址和源字符串的起始地址
2.将目标字符串的起始地址存一份
3.从源字符串起始字符开始,依次拷贝到目标字符串,当拷贝完源字符串中的\0后就停止拷贝
4.返回目标字符串的起始地址
2. strcat字符串追加函数
strcat 函数的介绍
char * strcat ( char * destination, const char * source );
strcat是字符串追加函数,作用是将源字符串拷贝到目标字符串的结束位置处,
返回的是目标空间的起始地址。
需要注意的是:
- 源字符串的首字符要覆盖目标字符串的 \0
- 要拷贝源字符串的\0
- 目标字符串要保证可以被修改,并且空间能容纳追加后的字符串
- 不保障自己给自己追加( \0 被覆盖 )
模拟实现strcat
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);
while (*dest != '\0')//找\0
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
1.找到目标字符串中 \0 的地址并存一份
2.找到源字符串的起始地址和结束地址(包含\0)
3.源字符串的起始字符拷贝时,要覆盖目标字符串中的\0
源字符串起始字符之后的字符依次拷贝到目标字符串的结尾处
当拷贝完\0后就停止拷贝
4.返回目标空间的起始地址
3. strcmp字符串比较函数
strcmp函数的介绍
int strcmp ( const char * str1, const char * str2 );
strcmp是字符串比较函数,用来比较两字符串对应位置上字符的大小
- 如果第一个字符串对应位置上的字符大于第二个字符串对应位置上的字符,那么返回的是大于0的数字
- 如果第一个字符串对应位置上的字符大于第二个字符串对应位置上的字符,那么返回的是小于0的数字
- 如果第一个字符串对应位置上的字符大于第二个字符串对应位置上的字符,那么返回0
注意:两字符串不能被修改
模拟实现strcmp,更符合VS的写法
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)//对应字符相等的情况
{
if (*str1 == '\0')//两字符串相等
{
return 0;
}
str1++;
str2++;
}
//对应字符不相等的情况
if (*str1 > *str2)//大于
{
return 1;
}
else//小于
{
return -1;
}
}
模拟实现strcmp,更符合C语言标准的写法
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)//对应字符相等的情况
{
if (*str1 == '\0')//两字符串相等
{
return 0;
}
str1++;
str2++;
}
//对应字符不相等的情况
return (*str1 - *str2);
}
思路如下:
1.找到两个字符串的起始地址
2.从两字符串的起始字符开始比较,比较的结果有三种情况:相等,大于和小于
(1)如果对应字符相等,那么就继续看这两个字符是否为\0,如果是,那么两字符串相等,
如果不是,那么就继续往后比较
(2)如果对应字符不相等,那么就看两字符相减的结果是否大于0,
如果结果大于0,那么就说明第一个字符串大于第二个字符串,
如果结果小于0,那么就说明第一个字符串小于第二个字符串
三.长度受限制的字符串函数
长度受限制的字符串函数可以指定要操作的字符个数,比如strncpy函数可以指定拷贝源字符串中多少个字符到目标字符串中,而不是一定要找到 \0 才停止拷贝
1. strncpy
strncpy函数的介绍
char * strncpy ( char * destination, const char * source, size_t num );
strncpy函数可以指定拷贝源字符串中多少个字符到目标字符串中
- 不追加 \0
- 但是如果指定拷贝源字符串的字符个数如果大于源字符串的长度,那么超出的部分就在目标字符串后面补 \0
- 使用该函数要保证目标空间能容纳拷贝后的字符串和目标字符串可修改
strncpy的使用
char arr1[10] = "abcdefgh";
char arr2[] = "hello";
strncpy(arr1, arr2, 5);
printf("%s\n", arr1);//输出hellofgh
模拟实现strncpy
#include<assert.h>
char* my_strncpy(char* dest, const char* src, size_t num)
{
char* ret = dest;
assert(dest && src);
//拷贝src中的字符串到dest指向的空间
while (num && (*dest++ = *src++))
{
num--;
}
//提前遇到源字符串中的\0
if (num)
{
while (--num)
{
*dest++ = '\0';//超出的长度用\0来补
}
}
return ret;//返回目标空间的起始地址
}
2. strncat
strncat函数的介绍
char * strncat ( char * destination, const char * source, size_t num );
strncat函数可以指定追加源字符串的多少个字符到目标字符串的结束位置
- 追加完指定个数的字符后,会自动在目标字符串后面再追加一个\0
- 可以实现自己给自己追加
- 使用该函数要保证目标空间能容纳拷贝后的字符串和目标字符串可修改
strncat的使用
char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1, arr2, 3);
printf("%s\n", arr1);//输出hello wor
模拟实现strncat
#include<assert.h>
char* my_strncat(char* dest, const char* src, size_t num)
{
char* ret = dest;
assert(dest && src);
while (*dest != '\0')//找目标字符串的\0
{
dest++;
}
while (num --)
{
if ((*dest++ = *src++) == 0)
{
return ret;
}
}
*dest = '\0';
return ret;
}
3. strncmp
strncmp函数的介绍
int strncmp ( const char * str1, const char * str2, size_t num );
strncmp函数,可以指定要比较的字符个数
如果指定的字符个数大于源字符串的长度,那么就用 \0 和目标字符串的对应字符相比较
如果两字符都比较完了,但还没到指定的比较次数
strncmp的使用
const char* p1 = "abcdef";
char* p2 = "abckjy";
int ret = strncmp(p1, p2, 4);//比较前4个字符
printf("%d\n", ret);//输出小于0的数
模拟实现strncmp
#include<assert.h>
int my_strncmp(const char* str1, const char* str2, size_t num)
{
assert(str1 && str2);
while (num--)
{
if (*str1++ != *str2++)
{
return (*str1 - *str2);//两字符串字符不相等
}
//比较的字符个数大于两字符串的长度
else if (*(str1 - 1) == '\0')
{
return 0;//两字符串相等
}
}
return 0;//两字符串相等
}
四.字符串查找
1.strstr
strstr的介绍
const char * strstr ( const char * str1, const char * str2 );
strstr是用来查找目标字符串中是否存在子串的函数,
若查找到,则返回目标字符串中第一次出现子串的起始地址,若未查找到,则返回空指针
strstr函数的使用
char* p1 = "hello world hello world";
char* p2 = "world";
char* ret = strstr(p1, p2);
if (ret == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", ret);//输出world hello world
}
模拟实现strstr
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);//保证不为野指针
if (*str2 == NULL)//要查找的为空字符串
{
return str1;//规定返回str1
}
//查找的逻辑
char* s1 = str1;
char* s2 = str2;
char* cur = str1;//记录有可能匹配成功的地址
while (*cur)
{
s1 = cur;
s2 = str2;
while ((*s1 == *s2) && (*s1 != '\0') && (*s2 != '\0'))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cur;//找到子串
}
cur++;//未匹配成功,尝试从下一个位置匹配
}
return NULL;//找不到子串
}
str函数查找部分的思路
(str1 指向目标字符串的起始地址,str2指向子字符串的起始地址)
1. 创建两个新的字符指针s1和s2分别存放str1 和str2
2. 再创建一个字符指针cur,用于记录有可能匹配成功的地址
3. 将cur 赋给s1,让s1向后查找,看当两字符串的对应字符是否相等
- 如果相等,就让两字符串依次向后比较字符(str1++,str2++)
如果在比较的过程中发现两字符串有一个字符不相等那么退出本次查找,下一次则从记录位置的下一个位置开始查找(cur++)
如果比较过程中发现 *s2 等于 \0(找到子串),或 *s1 等于\0(没找到 / 找到),则停止查找,再判断 *s2 是否等于 \0,如果是 ,则返回 cur 存的第一次出现子串的起始地址
- 如果不相等,那么说明本次未匹配成功,需要尝试从下一个位置匹配(cur++)
4. 当发现 *cur 等于 \0 时,说明未查找到子串,就停止查找,并返回NULL
五.错误信息报告
1. strerror
strerror 函数的介绍
char * strerror ( int errnum );
当我们在使用C语言标准库中的函数的时候,如果这些函数出错了,那么就会返回错误码,strerror函数可以将错误码对应的错误信息(字符串)的起始地址返回
- errno 是记录当前错误码的全局变量
- 这些错误码一般是放在errno.h这个头文件中说明的
- 只适用于库函数出错时
strerror函数的使用
#include<stdio.h>
#include<errno.h>
int main()
{
//fopen 打开文件
FILE* pf = fopen("data.txt", "r");//"r"-以读文件形式,打开文件
if (pf == NULL)//如果这个文件不存在,就打开失败,返回空指针
{
//只适用于库函数
printf("打开文件失败,原因是:%s", strerror(errno));
return 1;
}
else
{
printf("打开文件成功");
//...
fclose(pf);//关闭文件
pf = NULL;
}
return 0;
}
2.perror
void perror ( const char * str );
perror函数是将 errno 中错误码对应的错误信息打印
先打印str指向的字符串,打印冒号,再打印一个空格,再打印错误码对应的错误信息
例如:
strerror 和perror 的区别: strerror 只是拿到错误码对应错误信息的字符串的地址,而perror是直接将错误码对应的错误信息打印
六.字符函数
1.字符分类函数
字符分类函数是C标准库中的一组函数,用于对字符进行分类和判断。这些函数返回值为真或假,通常以整数0或 非0表示。
空白字符: 空格' ',换页 '\f' , 换行 '\n' , 回车 '\r' , 垂直制表符 '\v' 等
数字字符:'0' ~ '9' 的字符
控制字符:控制文本显示或者通信流程的特殊字符,比如:回车 '\r', 制表符'\t' , 换页 '\f' 等
十六进制的字符:包括所有十进制的数字字符,字母 a~f , A~F
1.1 islower函数 :判断是否为小写字母
int islower ( int c );
函数的使用
int ret = islower('A');
printf("%d\n", ret);//0
int retu = islower('a');
printf("%d\n", retu);//非0
函数的应用:写一个代码,将一个字符串中的小写字母转大写,其它字符不变
#include<ctype.h>
int main()
{
char arr[] = "I am a Student.";
int i = 0;
while (arr[i] != '\0')
{
if (islower(arr[i]))
{
arr[i] -= 32;
}
i++;
}
printf("%s\n", arr);//输出 I AM A STUDENT.
return 0;
}
1.2 isdigit函数:判断是否为数字字符
int isdigit ( int c );
函数的使用
int ret = isdigit('a');
printf("%d\n", ret);//0
2.字符转换函数
2.1 toupper 小写转大写函数
int toupper ( int c );
函数的使用
char ch = toupper('a');
printf("%c\n", ch);//A
2.2tolower 大写转小写函数
int tolower ( int c );
函数的使用
char ch = tolower('A');
printf("%c\n", ch);//a