学习字符串和字符串函数,首先要学习字符串;之后便可学习字符串函数是如何对字符串进行处理并且可以模拟其函数的功能写出自己的字符串函数;当然自己的是没有库里的好的,但其可以加深自己对字符串函数的理解。接下来我们一起来走入字符串的世界:
一,字符串
字符串是一个或多个字符的序列,如下所示:
“Zing went the strings of my heart!"
双引号不是字符串的一部分。双引号仅仅告诉编译器它括起来的是字符串。
C语言中没有专门用于存放字符串的变量类型,字符串都被储存在char类型的数组中。数组大家都很熟悉吧,它由连续的储存单元组成,显然字符串中的字符是一个一个的储存在一个一个的相邻的单元。
我们可以看一下: 这很清楚的展示了字符串在char类型数组的储存,但这有一个要注意:图中数组末尾位置的字符是\0;所以数组的容量必须比字符串的字符数多1。在编译器中会自动在结尾加上\0。这就是字符串。
二,字符串函数
在编程的过程中,我们经常要处理字符和字符串,为了方便操作字符和字符串,C语⾔标准库中提供了⼀系列库函数,接下来我们就学习⼀下这些函数。
1. 字符分类函数
C语言中有⼀系列的函数是专门做字符分类的,也就是⼀个字符是属于什么类型的字符的。这些函数的使用都需要包含⼀个头文件是 "ctype.h"
函数 | 符合条件,则该值不为零(即 true)。否则为 0(即 false)。 |
isalnum | 检查字符是十进制数字还是大写或小写字母。 |
isalpha | 检查字符是否为字母。 |
isblank | 检查字符是否为空白字符。 |
iscntrl | 检查字符是否为控制字符 |
isdigit | 检查字符是否为十进制数字 |
isgraph | 检查字符是否具有图形表示 |
islower | 检查字符是否为小写字母 |
isprint | 检查字符是否可打印 |
isspace | 检查字符是否为空白字符 |
isupper | 检查字符是否为大写字母 |
ispunct | 检查字符是否为标点字符 |
isxdigit | 检查字符是否为十六进制数字 |
这些函数可以帮助我们去对字符进行简洁快速的判断!
2. 字符转换函数
字符转换函数也包含在头文件ctype.h中,它只有两个在字母大小写之间转换的函数:
tolower | 将大写字母转换为小写 |
toupper | 将小写字母转换为大写 |
tolower:
int tolower ( int c );
传入值和返回值是以 int 值的形式,传入值会隐式转换为整数型,返回值可以隐式转换为 char。
在编程中,字符通常通过它们的ASCII值来表示。当你将一个字符转换为整数时,实际上就是获取它的ASCII值。
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
char arr[] = "Zing went the strings of my heart";
for (int i = 0; i < sizeof(arr); i++)
{
arr[i]=tolower(arr[i]);
}
return 0;
}
如果字符是大写字母并且具有小写等效字母,则将字符转换为其小写等效项。如果无法进行此类转换,则返回的值为传入值不变。
同理toupper:
如果字符是小写字母并且具有大写等效字母,则将字符转换为其大写等效项。如果无法进行此类转换,则返回的值为传入值不变。
3. strlen的使用和模拟实现
strlen()是用来计算字符串的长度库函数:
size_t strlen ( const char * str );
传入字符串的首地址,返回/0之前的字符串长度
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
char arr[] = "Zing went the strings of my heart";
size_t a = strlen(arr);
printf("%zu", a);
return 0;
}
相信大家学会了strlen的使用,接下来我们来手撕strlen函数:
明白strlen的传入值与返回值 size_t strlen ( const char * str ):
size_t my_strlen(char* str)
{
return len;
}
实现其功能,能计算字符串长度;
这有很多的方法,我这提供三种以供参考:
- 利用for或while循环,以for为例子:
size_t my_strlen(const char* str) { size_t len = 0; for (; *str++ != 0; len++) { ; } return len; }
- 利用函数递归(不创造新变量)来实现:
size_t my_strlen(const char* str) { if (*str != 0) { return 1 + my_strlen(++str); } else return 0; }
-
指针-指针:
#include<stdio.h> #include<string.h> #include<ctype.h> #include<assert.h> size_t my_strlen(const char* str) { assert(str);//断言str不是空指针,使程序更安全,要包含<assert.h>的头文件 const char* p = str; while (*str) { str++; } return str-p;//指针-指针的结果是指针同类型的二者之间的元素个数差数 } int main() { char arr[] = "Zing went the strings of my heart"; size_t a = my_strlen(arr); printf("%d", a); return 0; }
strlen思想是很简单的明白了;对于里面的指针和指针操作我之后会出详细记录说明的。
4. strcpy的使用和模拟实现
strcpy()是用来拷贝字符串的函数:
char * strcpy ( char * destination, const char * source );
将source(源头)字符串中/0之前1的字符拷贝到destination(目的地)字符数组中并返回destination的首地址
在Vs中使用请第一行:
#define _CRT_SECURE_NO_WARNINGS
因为Vs认为strcpy是不安全的,下面strcat,strncpy等也要加。
我们来学习使用strcpy:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<assert.h>
int main()
{
char arr1[50] = " ";//arr1空间必须比arr2大,防止溢出
char arr2[] = "Zing went the strings of my heart";//arr2中必须有/0
strcpy(arr1,arr2);
return 0;
}
成功将arr2中字符串拷贝1=到arr1中了;
接下来我们来手撕strcpy:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<assert.h>
char* my_strcpy(char* arr1, const char* arr2)
{
assert(arr1 || arr2);
char* p = arr1;//记录arr1的首地址,用于返回。
while (*arr2)
{
*arr1++ = *arr2++;
}
return p;
}
int main()
{
char arr1[50] = " ";//arr1空间必须比arr2大,防止溢出
char arr2[] = "Zing went the strings of my heart";//arr2中必须有/0
char* a = my_strcpy(arr1, arr2);
printf("%s", a);
return 0;
}
正确实现了自己的strcpy。
5. strcat的使用和模拟实现
strcat()(用于拼接字符串)函数接受两个字符串作为参数:
char * strcat ( char * destination, const char * source );
该函数把第2个字符串的备份附加在第1个字符串末尾,并把拼接后形成的新字符串作为第1个字符串,第2个字符串不变。strcat()函数的类型也是char* (即,指向char的指针)。返回第1个字符的首地址。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[50] = "xxx";//arr1空间必须比arr2大,防止溢出
char arr2[] = "Zing went the strings of my heart";//arr2中必须有/0
char* a = strcat(arr1, arr2);
printf("%s", a);
return 0;
}
使用ok了;我们来模拟实现:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<assert.h>
char* my_strcat(char* arr1, const char* arr2)
{
assert(arr1 || arr2);//断言指针不为NULL;
char* p = arr1;//记录arr1的首地址,用于返回。
while (*arr1)
{
arr1++;
}//寻找arr1的末尾
while (*arr2)
{
*arr1++ = *arr2++;//连接arr2
}
return p;
}
int main()
{
char arr1[50] = "xxx";//arr1空间必须比arr2大,防止溢出
char arr2[] = "Zing went the strings of my heart";//arr2中必须有/0
char* a = my_strcat(arr1, arr2);
printf("%s", a);
return 0;
}
完美!
6. strcmp的使用和模拟实现
strcmp()(字符串比较)函数:
int strcmp ( const char * str1, const char * str2 );
传入俩个要比较的字符串str1与str2;
返回int类型结果:
返回值 | 表明 |
---|---|
<0 | 第一个不匹配的字符在 STR1 中的值低于 STR2 中的值 |
0 | 两个字符串的内容相等 |
>0 | 第一个不匹配的字符在 STR1 中的值大于 STR2 中的值 |
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[50] = "abcf";
char arr2[] = "abcd";
int a = strcmp(arr1, arr2);
printf("%d", a);
return 0;
}
学会并使用是很简单的,相信你一定会了,接下来我们来自己实现一下吧!
字符串比较就比较每一个对应字符的ASCLL值:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<assert.h>
int my_strcmp(const char* arr1, const char* arr2)//比较字符串
{
assert(arr1 && arr2);
while (*arr1 !=0 && *arr2 !=0)
{
if (*arr1++ == *arr2++)
{
;
}
else if (*arr1 >= *arr2)
{
return 1;
}
else return -1;
}
return 0;
}
int main()
{
char arr1[10] = "abcf";
char arr2[10] = "abcd";
int a = my_strcmp(arr1, arr2);
printf("%d", a);
return 0;
}
实现完成,小小strcmp手拿把掐。
7. strncpy函数的使用
strncpy()函数,我们在之前有strcpy,他们二这的区别是啥?
strncpy():
char * strncpy ( char * destination, const char * source, size_t num );
它比strcpy函数多了一个size_t类型参数num;我们可以猜测它是不是可以指定拷贝字节数量?
来使用一下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[10] = "";
char arr2[10] = "abcd";
char* a = strncpy(arr1, arr2, 3);
printf("%s", a);
return 0;
}
结果符合!我们去查一下strncpy,没有问题,猜测没有问题!
strncpy()函数是拷贝指定字符数量的函数!
其模拟实现可以同理strcpy:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<assert.h>
char* my_strncpy(char* arr1, const char* arr2, size_t num)
{
assert(arr1 && arr2);
char* p = arr1;
while (num--)//这里while循环条件变成了num!=0循环了num次,拷贝了num个字符
{
if (*arr2 == 0)
{
*arr1++ = 0;
}
else
{
*arr1++ = *arr2++;
}
}
*++arr1 = 0;
return p;
}
int main()
{
char arr1[10] = "";
char arr2[10] = "abcd";
char* a = my_strncpy(arr1, arr2, 3);
printf("%s", a);
return 0;
}
我们超额完成任务,学习strncpy还模拟实现了,太棒了!
8. strncat函数的使用
有了上述经验,我们可以知道strncat是strcat基础上可以指定连接字符数量:
char * strncat ( char * destination, const char * source, size_t num );
没有问题;对于它的使用也同上,传入俩个字符串,将source中指定数量(num)的字符连接到destination的末尾,返回destination的首地址。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "xxw";
char arr2[] = "Zing went the strings of my heart";
char* a = strncat(arr1, arr2,11);
printf("%s", a);
return 0;
}
这里我也提供一下模拟实现的代码:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<assert.h>
char* my_strncat(char* arr1, const char* arr2, size_t num)
{
assert(arr1 && arr2);
char* p = arr1;
while (*p)
{
p++;
}
while (num--)
{
*p++ = *arr2++;
}
*p++ = 0;
return arr1;
}
int main()
{
char arr1[20] = "xxw";
char arr2[] = "Zing went the strings of my heart";
char* a = my_strncat(arr1, arr2,11);
printf("%s", a);
return 0;
}
这样可加深印像!
9. strncmp函数的使用
strncmp()是比较每个字符串的第一个字符。如果它们彼此相等,则继续处理以下对,直到字符不同,直到到达终止 null 字符,或者直到两个字符串中的 num 个字符匹配,以先发生者为准。
返回一个整数值,该值指示字符串之间的关系:
返回值 | 表明 |
---|---|
<0 | 第一个不匹配的字符在 str1 中的值低于 str2 中的值 |
0 | 两个字符串的内容相等 |
>0 | 第一个不匹配的字符在 str1 中的值大于在 str2 中的值 |
使用方式如上,可以看一下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "xxwx";
char arr2[] = "xxaxxxx";
int a = strncmp(arr1, arr2,3);
printf("%d", a);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "xxwx";
char arr2[] = "xxaxxxx";
int a = strncmp(arr1, arr2,2);
printf("%d", a);
return 0;
}
模拟如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<assert.h>
int my_strncmp(const char* arr1, const char* arr2, size_t num)//比较字符串
{
assert(arr1 && arr2);
while (num--)
{
if (*arr1++ == *arr2++)
{
;
}
else if (*arr1 >= *arr2)
{
return 1;
}
else return -1;
}
return 0;
}
int main()
{
char arr1[20] = "xxwx";
char arr2[] = "xxaxxxx";
int a = my_strncmp(arr1, arr2,3);
printf("%d", a);
return 0;
}
10. strstr的使用和模拟实现
strstr()函数是用来查找子字符串
char * strstr (const char * str1, const char * str2 );
返回指向 str1 中第一次出现的 str2 的指针,如果 str2 不是 str1 的一部分,则返回 null 指针。
使用如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "xxwx";
char arr2[] = "xw";
char* a = strstr(arr1, arr2);
printf("%s", a);
return 0;
}
直接模拟实现,因为我累了:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strstr(const char* arr1, const char* arr2)
{
assert(arr1 && arr2);
const char* p = arr1;//在记录arr1中找到arr2的位置并返回该位置
const char* s1 = NULL;//遍历arr1
const char* s2 = NULL;//遍历arr2
while (*p)
{
s1 = p;
s2 = arr2;
if (*s1 == *s2)
{
while (*s1++ == *s2++)
{
if (*s2 == 0)
{
return (char*)p;
}
}
}
p++;
}
return (char*)p;
}
int main()
{
char arr1[20] = "xxwx";
char arr2[] = "xw";
char* a = my_strstr(arr1, arr2);
printf("%s", a);
return 0;
}
11. strtok函数的使用
strtok()函数大家应该不了解吧,形式如下:
char * strtok ( char * str, const char * delimiters );
将字符串拆分为标记
对此函数的一系列调用将 str 拆分为标记,这些标记是由分隔符中的任何字符分隔的连续字符序列。
在第一次调用时,该函数需要一个 C 字符串作为 str 的参数,其第一个字符用作扫描标记的起始位置。在后续调用中,该函数需要一个 null 指针,并使用最后一个标记结束后的位置作为扫描的新起始位置。
为了确定令牌的开头和结尾,该函数首先从起始位置扫描分隔符中未包含的第一个字符(该字符将成为令牌的开头)。然后从令牌的开头开始扫描分隔符中包含的第一个字符,该字符将成为令牌的结尾。如果找到终止 null 字符,扫描也会停止。
令牌的此结尾将自动替换为 null 字符,令牌的开头由函数返回。
在对 strtok 的调用中找到 str 的终止 null 字符后,对此函数的所有后续调用(以 null 指针作为第一个参数)都将返回 null 指针。
找到最后一个令牌的点由函数在内部保留,以便在下次调用时使用(不需要特定的库实现以避免数据竞争)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "xxwx,ssd;xxd xx.";
char arr2[] = " ,;.";//定义分隔符
char*a = strtok(arr1, arr2);
for (; a!= NULL; a=strtok(NULL, arr2))
{
printf("%s\n",a);
}
return 0;
}
可以实现识别分隔符并去除分隔符等等
12. strerror函数的使用
strerror():
char * strerror ( int errnum );
解释 errnum 的值,生成一个字符串,其中包含一条消息,该消息描述错误条件,就像由库的函数设置为 errno 一样。
返回的指针指向静态分配的字符串,该程序不得修改该字符串。对此函数的进一步调用可能会覆盖其内容(不需要特定的库实现来避免数据竞争)。
用来了解错误码!
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
int main()
{
int arr1[5] = { 0,1,2,3,4 };
for(int i=0;i<5;i++)
{
char* a = strerror(arr1[i]);
printf("%s\n",a);
}
return 0;
}
完工!加油!
谢谢大家阅读,如有错误请大家指正!!!