目录
字符函数
使用字符函数需要包含头文件
#include <ctype.h> |
islower
int islower( int c );
- islower可以判断参数部分的c是否是小写字母
- 如果是小写字母就返回非0值,如果不是小写就返回0
- 字符本质是整数,每个字符都有对应的ASCII码
将小写字母转换成大写字母
#include <stdio.h>
#include <ctype.h>
int main()
{
char arr[] = "Test String";
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++)
{
int ret = islower(arr[i]);
if (ret)
{
arr[i] = arr[i] - 32;
}
}
printf("%s\n", arr);
return 0;
}
ispower
int isupper( int c );
- isupper可以判断参数部分的c是否是大写字母
- 如果是大写字母就返回非0值,如果不是大写就返回0
大小写字母转换函数
- int tolower( int c ); 将大写字母转化成小写字母
- int toupper( int c ); 将小写字母转换成大写字母
将小写字母转换成大写字母
int main()
{
char arr[] = "Test String";
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++)
{
int ret = islower(arr[i]);
if (ret)
{
arr[i] = toupper(arr[i]);
}
}
printf("%s\n", arr);
return 0;
}
字符串函数
使用字符串函数需要包含的头文件
#include <string.h> |
strlen使用和模拟实现
size_t strlen ( const char * str );
- 字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )
- 参数指向的字符串必须要以 '\0' 结束
- 注意函数的返回值为size_t,是无符号的
判断输出的结果
int main()
{
const char* str1 = "abcdef";
const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
运行结果
- strlen(str2)返回结果是无符号的3
- strlen(str1)返回结果是无符号的6
- 无符号数减无符号数的值也是无符号的
- 3 - 6 = -3 ,我们把-3当成无符号数
- 有符号的-3补码是11111111 11111111 11111111 11111101,最高位的1说明这个数是负数
- 无符号的-3就是把最高位的1当成的数值位,所以无符号的-3的原反补都是 11111111 11111111 11111111 11111101,转成10进制就是4294967293
有人肯定会有这样的疑问:为啥我看过一段代码就是strlen(str2) - strlen(str1)的值输出的是-3,而不是4294967293
原因是:%d打印的是有符号的十进制,所以我们把无符号的整形解读成有符号数,所以正确的是要用%u来打印,%u打印的是无符号的整数,是以十进制的形式来输出
strlen的模拟实现\
size_t my_strlen(const char* str)
{
size_t count = 0;
assert(str);
while (*str)
{
count++;
str++;
}
return count;
}
int main()
{
char arr1[] = "hello world";
printf("%zd\n", my_strlen(arr1));
return 0;
}
strcpy
字符串拷贝函数
char* strcpy(char * destination, const char * source );
将源头的数据拷贝到目标空间去
destination:目的地
source:源头
使用strcpy的注意事项
- 源字符串必须以 '\0' 结束。
- 会将源字符串中的 '\0' 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可修改。
- 函数返回的是目标空间的起始地址
int main()
{
char arr1[20] = { 0 };
char arr2[] = "hello world";
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
注意:如果目标空间不能修改,那么就会报错
strcpy的模拟实现
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
//当*src是'\0'时,我们就把'\0'传给*dest,while判断语句就为0
//while循环退出时dest在向后走一步,src同理
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = { 0 };
char arr2[] = "hello world";
my_strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
strncpy
更安全的字符串拷贝
char * strncpy ( char * destination, const char * source, size_t num );
destination:目的地
source:源头
num:要拷贝的字符个数
strncpy使用说明
- 拷贝num个字符从源字符串到目标空间
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加'\0',直到num个。
- 如果源字符串中有6个字符,但是只拷贝3个字符过去,那么就不会额外多追加'\0'
strncpy的模拟实现
char* my_strncpy(char* dest, const char* sour, size_t num)
{
assert(dest && sour);
int i = 0;
char* ret = dest;
while (num--)
{
*dest++ = *sour++;
if (*sour == '\0')
{
while (num--)
{
*dest++ = *sour;
}
return ret;
}
}
return ret;
}
int main()
{
char arr1[] = "hello worldxxxxxxxx";
char arr2[] = "hello bit";
char* ret = my_strncpy(arr1, arr2, 11);
printf("%s\n", arr1);
return 0;
}
strcat
字符串追加
char *strcat( char *strDestination, const char *strSource );
strDestination:目标空间
strSource:源头
将源头的数据追加到目标空间'\0'的后面
使用strcat的注意事项
- 源字符串必须以 '\0' 结束。
- 目标字符串中也得有 '\0' ,否则没办法知道追加从哪里开始。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 函数返回的是目标空间的起始地址
strcat的模拟实现
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest)
{
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;
}
思考下自己给自己追加可以吗?
结果:死循环+越界访问,因为两个指针指向的都是同一块空间,会把'\0'给覆盖掉(自己模拟的实现不了,但是库里面的strcat是可以的)。自己给自己追加最好还是用strncat。
strncat
更安全的字符串追加函数
char * strncat ( char * destination, const char * source, size_t num );
destination:目标空间
source:源头
num:要追加的字符个数
strncat的使用说明
- 将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加⼀个 '\0' 字符
如果source指向的字符串的长度小于num的时候,只会将字符串中到'\0'的内容追加到destination指向的字符串末尾,'\0'追加后不会再额外追加其他
strncat的模拟实现
char* my_strncat(char* dest, const char* sour, size_t num)
{
assert(dest && sour);
char* ret = dest;
while (*dest)
{
dest++;
}
while (num--)
{
*dest++ = *sour++;
if (*sour == '\0')
{
*dest = *sour;
return ret;
}
}
*dest = '\0';
return ret;
}
int main()
{
char str1[100] = "To be\0\\123456789xxx";
char str2[20] = "or not to be";
my_strncat(str1, str2, 5);
printf("%s\n", str1);
return 0;
}
strcmp
字符串比较函数
int strcmp( const char *string1, const char *string2 );
比较的是对应位置上的字符串大小,小的字符所在的字符串小于另外一个字符串
注意:比较的不是字符串的长度
标准规定
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
- 比较两个字符串中对应位置上字符ASCII码值的大小
strcmp的模拟实现
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
str1++;
str2++;
}
return *str1 - *str2;
}
int main()
{
char arr1[] = "abcd";
char arr2[] = "abcf";
int ret = my_strcmp(arr1, arr2);
if (ret > 0)
{
printf("%s大于%s\n", arr1, arr2);
}
else if (ret == 0)
{
printf("%s等于%s\n", arr1, arr2);
}
else
{
printf("%s小于%s\n", arr1, arr2);
}
return 0;
}
strncmp
int strncmp ( const char * str1, const char * str2, size_t num );
较str1和str2的前num个字符,如果相等就继续往后比较,最多比较num个字母,如果提前发现不一样,就提前结束,大的字符所在的字符串大于另外一个。如果num个字符都相等那就返回0
strstr
char * strstr ( const char * str1, const char * str2);
返回str2指向的字符串在str1指向字符串中第一次出现的位置
如果str2不是str1的一部分,则返回 null 指针
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
//如果*str2只有'\0'那就输出str1指向的字符串
if (*str2 == '\0')
{
return (char*)str1;
}
const char* cur1 = str1;
const char* cur2 = str2;
while (*str1)
{
cur1 = str1;
cur2 = str2;
while (*cur1 == *cur2 && *cur2 != '\0')
{
cur1++;
cur2++;
}
if (*cur2 == '\0')
{
return (char*)str1;
}
str1++;
}
return NULL;
}
int main()
{
char arr1[] = "abaacaaaddf";
char arr2[] = "aaa";
char* ret = my_strstr(arr1, arr2);
if (ret != NULL)
{
printf("%s\n", ret);
}
else
{
printf("找不到");
}
return 0;
}
strtok
char * strtok ( char * str, const char * sep);
例子:zhangsan@qq.com
- sep参数指向一个字符串,定义了用作分隔符的字符集合,例如zhangsan和qq和com都被'@'’和'.'给分割了,所以我们把这些分割符集合成字符串"@."
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。例如zhangsan或qq或com都属于一个标记,都是被"@."这两个分割符给分割的,所以str就指向了"zhangsan@qq.com"
- strtok函数找到str中的下一个标记,并将其用'\0'结尾,返回⼀个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串⼀般都是临时拷贝的内容并且可修改)例子:"zhangsan@qq.com",str会找到一个被分割符分割的标记,并把分割符改成'\0',并把标记的起始地址返回。也就是说一开始str找到分割符是'@',所以就把'@'改成了'\0',并把"zhangsan"起始地址z的地址给返回
- strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。也就是说第一个参数不为NULL只需要执行一次就行了。例子:"zhangsan@qq.com",这里面有3个标记,但是第一个标记就是"zhangsan",保存他在字符串中的位置就是保存分割符的位置,这里就是保存把'@'改成'\0'的位置,这样方便下一次寻找标记
- strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。这里就是从保存把'@'改成'\0'的地址向后开始找分割符,找到了就把分割符修改成'\0',并保存分割符的位置,并且把标记的起始地址返回。例子:"zhangsan\0qq.com",我们从'\0'的位置开始找分割符'.',找到了就把'.'修改成'\0',并且保存'\0'的地址,并且返回"qq"的起始地址
- 如果字符串中不存在更多的标记,则返回 NULL 指针。
int main()
{
char arr1[] = "zhangsan@qq.com";
char* p = "@.";
char arr2[30] = { 0 };
strcpy(arr2, arr1);
char* ptr = NULL;
for (ptr = strtok(arr2, p); ptr != NULL; ptr = strtok(NULL, p))
{
printf("%s\n", ptr);
}
return 0;
}
strerror
strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。
char * strerror ( int errnum );
在不同的系统和C语言标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头文件中说明的,C语言程序启动的时候就会使用一个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会讲对应的错误码,存放在errno中,而一个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是 有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
#include <errno.h>
#include <string.h>
#include <stdio.h>
int main()
{
FILE* pFile;
//在这个程序的文件夹中打开unexit.txt的文件,用读的形式
pFile = fopen("unexit.txt", "r");
//如果pFile是NULL就说明打开失败
//使用库函数发生的错误,就会把对应的错误码会放在全局变量errno
if (pFile == NULL)
{
printf("Error oprning file unexit.txt:%s\n", strerror(errno));
}
else
{
printf("打开文件成功\n");
}
return 0;
}
注意:
如果第一个库函数出错了,第二个库函数也出错了,那它反应的就是第二个错误码对应的错误信息。每次发生错误都会更新全局的errno,使用要及时判断
perror
perror函数可以直接将错误的信息打印出来。perror函数打印完参数部分的字符串后,会在额外打印一个冒号和空格,再打印错误信息。