目录
前言
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中
字符串常量适用于那些对它不作修改的字符串函数
函数介绍
1.求字符串函数
a.strlen
求字符串中的字符个数
Get the length of a string.
size_t strlen( const char *string );
可以看到size_t是unsigned int通过typedef定义的新名字
strlen用来求字符串中字符的个数
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<string.h>
int main()
{
char arr[] = "abcdef";
//这种数组在最后隐含了'\0'
int len = strlen(arr);
//字符串以'\0'作为结束标志,strlen函数返回的是'\0'前出现的字符个数(不包含strlen)
printf("%d\n",len);
return 0;
}
结果是:6
int main()
{
char arr[] = { 'b','i','t' };
int len = strlen(arr);
printf("%d\n",len);
return 0;
}
结果是随机值,因为字符串并没有以\0作为结束标志
strlen的返回值是size_t,size_t是无符号整型
int main()
{
if(strlen("abc")-strlen("abcdef")>0)
printf('>\n');
else
printf('<=\n');
return 0;
}
结果是:>,因为无符号整型-无符号整型还是一个无符号整型
模拟实现strlen
(这是计数器方法,一共三种方法分别为计数器方法,指针-指针,递归)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
unsigned int my_strlen(const char* str)
{
assert(str);//传入的数组不能为空,那么使用assert,如果传入指针为空就报错
unsigned int count = 0;
while (*str != '\0')
{
count++;
*str++;
}
return count;
}
int main()
{
char arr[] = { "abcde" };
unsigned int len = my_strlen(arr);
printf("%u\n",len);
return 0;
}
2.长度不受限制的字符串函数
a.strcpy
字符串拷贝
Copy a string.
char *strcpy( char *strDestination, const char *strSource );
把源字符串的内容覆盖到目标空间,可不是交换
int main()
{
char name[20] = { 0 };
strcpy(name,"zhangsan");
printf("%s\n",name);
//会打印zhangsan
return 0;
}
strcpy只会打印\0之前的内容
int main()
{
char name[20] = { 0 };
strcpy(name,"zhang\0san");
printf("%s\n",name);
//会打印zhang
return 0;
}
拷贝一个没有'\0'作为结尾标志的字符串到另一个字符串就会报错
int main()
{
char arr[] = "xxxxxxxxx";
char str[] = { 'a','b','c','d' };
strcpy(arr, str);
return 0;
}
拷贝一个大的源字符串到小的目标空间也会报错
int main()
{
char arr[3] = "abc";
char str[] = { 'a','b','c','d' };
strcpy(arr, str);
return 0;
}
拷贝源字符串到常量字符串也会报错(因为目标空间不可变)
int main()
{
char *arr = "xxxxxxxxx";
char str[] = { "abcde" };
strcpy(arr, str);
return 0;
}
模拟实现strcpy
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest,const char* src)
//只改变目标空间,不改变源字符串,所以用const限制源字符串
{
assert(dest&&src);
//确认传入参数的合法性,只要传入空指针就会报错
char* ret = dest;
while (*dest++ = *src++)
;
return ret;
}
int main()
{
char arr[] = "xxxxxxxxx" ;
char str[] = "abcd";
my_strcpy(arr, str);
printf("%s\n",arr);
return 0;
}
使用strcpy时要注意:
源字符串必须以'\0'作为结束标志
会将源字符串中的'\0'拷贝到目标空间
目标空间必须足够大,以确保能存放源字符串
目标空间必须可变
strcpy只负责拷贝,不考虑目标空间的大小,如果目标空间不合适,strcpy仍然会拷贝,执行结束后才会报错
b.strcat
字符串追加
Append a string.
char *strcat( char *strDestination, const char *strSource );
int main()
{
char arr1[20] = "Hello";
char arr2[] = " world";
strcat(arr1, arr2);
printf("%s\n",arr1);
return 0;
}
模拟实现strcat
char* my_strcat(char* dest, const char* src)
{
char* ret = dest;
assert(dest&&src);
while (*dest != '\0')
{
dest++;
}
while (*dest++ = *src++)
;
return ret;
}
int main()
{
char arr[20] = "abcde";
char str[] = "fghi";
my_strcat(arr, str);
printf("%s\n",arr);
return 0;
}
c.strcmp
比较字符串(注意:比较的是字符串的内容,即字符串的ASSIC码值)
Compare strings.
int strcmp( const char *string1, const char *string2 );
int main()
{
char arr[] = "abcde";
char str[] = "abq";
int ret = strcmp(arr,str);
if (ret < 0)
printf("<\n");
else if (ret == 0)
printf("=\n");
else
printf(">\n");
return 0;
}
比较的是字符串的内容,且比较到c<q时就输出结果,不再向后进行
模拟实现strcmp
int my_strcmp(const char*dest, const char*src)
{
assert(dest&&src);
while (*dest == *src)
{
if (*dest == '\0')
return 0;
dest++;
src++;
}
return (*dest - *src);
}
int main()
{
char arr[] = "abc";
char str[] = "abcde";
int len = my_strcmp(arr,str);
if (len < 0)
printf("<\n");
else if (len == 0)
printf("=\n");
else
printf(">\n");
return 0;
}
3.长度不受限制的字符串函数
strcpy
strcat
strcmp
有安全隐患,以strcpy为例,即使程序会报错,但仍然会拷贝大的源字符串到小的目标空间里
于是有了长度受限制的字符串函数
strncpy
strncat
strncmp
int main()
{
char arr1[] = "abcdef";
char arr2[] = "bit";
strncpy(arr1,arr2,5);
return 0;
}
可以控制拷贝覆盖的字节数,追加的字节数,比较的字节数
4.字符串查找
a.strstr
从子串1中查找是否含有子串2
Find a substring.
char *strstr( const char *string, const char *strCharSet );
模拟实现
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
const char* s1 = str1;
const char* s2 = str2;
const char* p = str1;
while (*p)
{
s1 = p;
s2 = str2;
while (*s2 == *s1 && *s1 != '\0' && *s2 != '\0')
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)p;
}
p++;
}
return NULL;
}
int main()
{
char email[] = "abbbcde";
char substr[] = "bbc";
char* ret = my_strstr(email, substr);
if (ret == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
b.strtok
切割字符串
Find the next token in a string.
char *strtok( char *strToken, const char *strDelimit );
int main()
{
const char* sep = "@.";
//sep是一个参数,定义了用作分隔符的字符集和
//第一个参数指定一个字符串,它包含了0或多个由sep字符串中一个或多个分隔符分割的标记
char email[] = "zhangpengwei@bitejiuyeke.com";
char arr[30] = { 0 };
strcpy(arr,email);
char* ret = strtok(arr,sep);
printf("%s\n",ret);
ret = strtok(NULL,sep);
printf("%s\n",ret);
ret = strtok(NULL, sep);
printf("%s\n",ret);
return 0;
}
和for循环结合使用
int main()
{
const char* sep = "@.";
char email[] = "zhangpengwei@bitejiuyeke.com.net";
char arr[40] = { 0 };
strcpy(arr,email);
char* ret = NULL;
for (ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n",ret);
}
return 0;
}
5.错误信息报告
strerror
返回错误码所对应的错误信息
Get a system error message (strerror) or prints a user-supplied error message (_strerror).
char *strerror( int errnum );
查看错误码对应的错误信息
int main()
{
printf("%s\n", strerror(0));
printf("%s\n", strerror(1));
printf("%s\n", strerror(2));
printf("%s\n", strerror(3));
printf("%s\n", strerror(4));
printf("%s\n", strerror(5));
return 0;
}
查看错误原因
#include <errno.h>
int main()
{
//errno - C语言设置的一个全局的错误码存放的变量
FILE* pf = fopen("text.txt","r");
if (pf == NULL)
{
printf("%s\n",strerror(errno));
return 1;
}
else
{
}
return 0;
}
6.字符操作
字符分类函数
用法:
#include<ctype.h>
int main()
{
//其他函数的用法类似,如果传入字符符合函数就返回一个正整数,否则返回0
int a = isspace(' ');
printf("%d\n",a);
return 0;
}
字符转换
int tolower(int c);//字符转小写
int toupper(int c);//字符转大写
使用方法:
#include <ctype.h>
int main()
{
printf("%c\n",tolower('W'));
//把大写字符转换为小写字符,如果传入其他字符会打印原字符不会做任何改变
return 0;
}
7.内存操作函数
a.memcpy
memcpy负责拷贝两块独立空间中的数据
Copies characters between buffers.
void *memcpy( void *dest, const void *src, size_t count );
用法
int main()
{
int arr[] = { 1,2,3,4,5,6 };
int str[] = { 0 };
memcpy(str,arr,24);
return 0;
}
模拟实现
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest+1;
src = (char*)src+1;
}
return ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,6 };
int str[10] = { 0 };
my_memcpy(str,arr,24);
return 0;
}
b.memmove
重叠内存之间的数据拷贝
例如
arr[] = { 1,2,3,4,5,6,7,8,9 };
拷贝1,2,3,4,5到3,4,5,6,7上去也就是应该得到1,2,1,2,3,4,5,8,9
(这里就不能使用memcpy了,因为memcpy先拷贝1,2到3,4的位置,此时数组变成121256789,34的位置变成了12,会把34位置的12拷贝到56上,此时数组变成121212789,最后会把5位置上的1拷贝到7的位置上,最终得到121212189,与想得到的结果不同,所以不能使用memcpy拷贝重叠内存之间的数据)
(在VS编译器下,memcpy也可实现memmove的功能,但是在其他编译器未知,原来函数也卷,笑哭,总之,拷贝重叠内存的数据用memmove就完了)
Moves one buffer to another.
void *memmove( void *dest, const void *src, size_t count );
用法
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
memmove(arr + 2, arr, 16);
int i = 0;
for (i = 0; i < 9; i++)
{
printf("%d ",arr[i]);
}
return 0;
}
模拟实现
void* my_memmove(void* dest, const void* src, size_t count)
{
assert(dest&&src);
void* ret = dest;
//从前向后拷贝
if (dest < src)
{
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
//从后向前拷贝
else
{
while (count--)
{
*((char*)dest + count) = *((char*)src + count);
}
}
return ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
my_memmove(arr + 2, arr, 16);
int i = 0;
for (i = 0; i < 9; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
c.memcmp
内存比较
Compare characters in two buffers.
int memcmp( const void *buf1, const void *buf2, size_t count );
用法
int main()
{
int arr[] = { 1,2,3,4,5,6 };
int str[] = { 1,3,2 };
int ret = memcmp(arr,str,12);
printf("%d\n",ret);
return 0;
}
d.memset
内存设置
Sets buffers to a specified character.
void *memset( void *dest, int c, size_t count );
用法
int main()
{
char arr[] = "hello bit";
memset(arr + 6, 'x', 3);
printf("%s\n",arr);
return 0;
}
注意:不能用来初始化数组,因为memset是一个字节一个字节地来修改数据