当前位置:网站首页>【字符串函数内功修炼】strlen + strstr + strtok + strerror(三)
【字符串函数内功修炼】strlen + strstr + strtok + strerror(三)
2022-08-04 22:41:00 【Albert Edison】
文章目录
1. strlen - 求字符串长度
函数介绍
size_t strlen(const char* string);
1、字符串已经 \0
作为结束标志,strlen 函数返回的是在字符串中 \0
前面出现的字符个数(不包含 \0
)。
2、参数指向的字符串必须要以 \0
结束。
3、注意函数的返回值为 size_t,也就是无符号的整型。
代码示例
#include <stdio.h>
int main()
{
int len = my_strlen("abcdef");
printf("%d\n", len);
return 0;
}
运行结果
模拟实现
计数器实现
代码示例
#include <stdio.h>
#include <assert.h>
int my_strlen(const char* str) {
assert(str);
int cnt = 0;
while (*str != '\0') {
cnt++;
str++;
}
return cnt;
}
int main()
{
char str[] = "abcdef";
int len = my_strlen(str);
printf("%d\n", len);
return 0;
}
assert 是断言,判断 str 是不是空指针;
运行结果
递归实现
代码示例
#include <stdio.h>
#include <assert.h>
int my_strlen(const char* str) {
assert(str);
if (*str) {
return 1 + my_strlen(str + 1);
}
else
return 0;
}
int main()
{
char str[] = "abcdef";
int len = my_strlen(str);
printf("%d\n", len);
return 0;
}
运行结果
指针实现
代码示例
#include <stdio.h>
#include <assert.h>
int my_strlen(const char* str) {
assert(str);
const char* start = str; //记录首元素的地址
while (*str != '\0') {
str++;
}
return str - start; //最后一个元素的地址 - 首元素地址 = 中间的元素个数
}
//升级版
int my_strlen(char *s)
{
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}
int main()
{
char str[] = "abcdef";
int len = my_strlen(str);
printf("%d\n", len);
return 0;
}
运行结果
2. strstr - 字符串查找
函数介绍
char* strstr(const char* str2, const char* str1);
strstr 函数可以在一个字符串(str1)中查找另一个字符串(str2);
如果 str2 存在于 str1 中,那么就返回 str2 在 str1 中第一次出现的起始位置;
如果在 str1 中找不到 str2,那么就返回 空指针(NULL)。
它的第一个参数是 str1 的起始位置,第二个参数是 str2 的起始位置。
代码示例
假设我们在字符串
"abcdefabcdef"
中查找字符串 `“bcd”
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "abcdefabcdef";
char str2[] = "bcd";
char* ret = strstr(str1, str2);
if (NULL == ret) {
printf("找不到\n");
}
else {
printf("%s\n", ret);
}
return 0;
}
运行结果
strstr 函数的返回值是字符串"bcd"
在字符串"abcdefbcd"
中第一次出现的位置的起始位置,而不是出现几次就返回几个起始位置。
模拟实现
strstr 函数的模拟实现相对复杂,在实现过程中我们需要设置 3 个指针变量来辅助实现函数功能。
cur 指针: 记录每次开始匹配时的起始位置,当从该位置开始匹配时就找到了目标字符串,便于返回目标字符串出现的起始位置;当从该位置开始没有匹配成功时,则从 cur++ 处开始下一次的匹配。
s1 和 s2 指针: 通过判断 s1 和 s2 指针解引用后是否相等来判断每个字符是否匹配成功,若成功,则指针后移比较下一对字符;若失败,s1 指针返回 cur 指针处,s2 指针返回待查找字符串的起始位置。
例如,在字符串 "abbbcdef"
中查找字符串 "bbc"
:
首先 s1 指向的元素为 a,s2 指向的元素为 b,此时 s1 与 s2 不相等
则 cur 指针后移,接着将 cur 指针赋值给 s1 指针,此时,s1 指向的元素为 b,s2 指向的元素为 b,则s1 与 s2 匹配成功
那么 cur 指针不动,s1 和 s2 指针后移继续比较,此时,s1 指向的元素为 b,s2 指向的元素为 b,则s1 与 s2 匹配成功
那么 cur 指针不动,s1 和 s2 指针后移继续比较,此时,s1 指向的元素为 b,s2 指向的元素为 c,则s1 与 s2 不相等
那么 cur 指针向后移一位,s1 返回 cur 指向的位置,s2 返回待查找字符串起始位置
然后开始进行下一轮的匹配,直到当 s2 指向的内容为\0
时,便说明待查找字符串中的字符已经被找完,也说明了从当前 cur 位置开始匹配能够找到目标字符串,所以此时返回指针 cur 即可。
代码示例
#include <stdio.h>
char* my_strstr(const char* str, const char* substr) {
const char* s1 = str;
const char* s2 = substr;
const char* cur = str;
assert(str && substr);
if (*substr == '\0') {
return (char*)str;
}
while (*cur) {
s1 = cur;
s2 = substr;
//1. 如果 *s1=\0,说明被查找的字符串已经走完了,不可能找到
//2. 如果 *s2=\0,说明把字符串已经找完了
while ((*s1 != '\0') && (*s2 != '\0') && (*s1 == *s2)) {
s1++;
s2++;
}
if (*s2 == '\0') {
return (char*)cur;
}
cur++;
}
// 找不到的情况
return NULL;
}
int main()
{
char str1[] = "abbbcdef";
char str2[] = "bbc";
char* ret = my_strstr(str1, str2);
if (NULL == ret) {
printf("找不到\n");
}
else {
printf("%s\n", ret);
}
return 0;
}
运行结果
3. strtok - 字符串分割
函数介绍
char* strtok(char* str, const char* sep);
strtok 函数能通过给定的一系列字符将一个字符串分割成许多子字符串的函数。
它的第一个参数 str 是需要被分割的字符串的首地址;
第二个参数 sep 是一个字符串的首地址,该字符串是用作分隔符的字符集合。
返回值是查找到的标记的首地址。
注意:
1、sep 参数是个字符串,定义了用作分隔符的字符集合
2、第一个参数指定一个字符串,它包含了 0 个或者多个由 sep 字符串中一个或者多个分隔符分割的标记。
3、strtok 函数找到 str 中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。
(注:strtok 函数会改变被操作的字符串,所以在使用 strtok 函数切分的字符串一般都是临时拷贝的内容并且可修改。)
4、strtok 函数的第一个参数不为 NULL ,函数将找到 str 中第一个标记,strtok 函数将保存它在字符串中的位置。
5、strtok 函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
6、如果字符串中不存在更多的标记,则返回 NULL 指针。
代码示例
假设我们要将字符串
"[email protected]"
以"@"
字符和"."
字符分割开
int main()
{
const char* p = "@."; //分隔符的字符集合
char arr[] = "[email protected]"; //待分割字符串
char buf[50] = {
0 };
strcpy(buf, arr);//将数据拷贝一份使用,防止原数据被修改
char* str = NULL;
str = strtok(buf, p); //第一次传参需传入待分割字符串首地址
while (str != NULL) {
//说明还未分割完
printf("%s\n", str); //打印标记
str = strtok(NULL, p); //对同一个字符串进行分割,第二次及以后的第一个参数为NULL
}
return 0;
}
运行结果
当 strtok 函数找到第一个标记时,将其后的@
字符改为\0
并返回第一个标记的首地址,所以我们以返回的地址为首地址,开始打印字符串的时候就只会打印出 edisonchen,第二次再对该字符串调用 strtok 函数时将从@
字符后面开始寻找下一个标记。
4. strerror - 错误报告函数
函数介绍
char* strerror(int errnum);
strerror 函数可以把 错误码 转换为对应的错误信息,返回错误信息对应字符串的起始地址。
代码示例
我们可以先来看一下,就那些错误信息
#include <stdio.h>
#include <string.h>
int main()
{
int i = 0;
for (i = 0; i < 10; ++i) {
printf("%s\n", strerror(i));
}
return 0;
}
运行结果
但是在实际过程中肯定不可能把所有的错误代码全部打印出来,举个栗子
fopen 函数的功能是打开一个文件,当其执行成功时会返回打开文件的首地址,执行失败时会返回一个空指针(NULL)。
那么我们假设使用 fopen 打开一个不存在的文件,然后用 strerror 来显示报错信息
代码示例
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
FILE* pf = fopen("test.txt", "r");
if (NULL == pf) {
//出错误的原因是什么
printf("%s\n", strerror(errno));
return 0;
}
//读文件
//...
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
运行结果
当我们要打开一个不存在的文件(test.txt)来阅读的时候,显然 fopen 函数会执行失败,于是 pf 指针接收的便是空指针(NULL);
当库函数使用的时候,如果发生错误,会把 errno 这个全局的错误变量设置为本次执行库函数产生的错误码;
errno 是 C 语言提供的一个全局变量,可以直接使用,放在 errno.h 文件中的。
5. 字符分类函数
如下表
tolower、toupper - 字符转换
这里以 tolower 和 toupper 为例,其他的不过多讲解
代码示例
大小写互相转换
#include <stdio.h>
#include <ctype.h>
int main()
{
char ch = 0;
ch = getchar();
if (islower(ch)) {
ch = toupper(ch);
}
else {
ch = tolower(ch);
}
printf("%c\n", ch);
return 0;
}
运行结果
大写转小写
小写转大写
代码示例
把 str 字符串转换成小写
#include <stdio.h>
#include <ctype.h>
int main()
{
int i = 0;
char str[] = "HELLO WORLD\n";
char c;
while (str[i])
{
c = str[i];
if (isupper(c)) {
c = tolower(c);
}
putchar(c);
i++;
}
return 0;
}
运行结果
. 总结
以上就是我们 C 语言中常用的字符和字符串库函数,其实理解起来不难,重要是多加使用练习
边栏推荐
猜你喜欢
golang打开文件和读写文件
3D激光SLAM:LeGO-LOAM---两步优化的帧间里程计及代码分析
Numpy on the superposition of two arrays
老叶的三束玫瑰
As hot as ever, reborn | ISC2022 HackingClub White Hat Summit was successfully held!
直播带货为农产品开拓销售渠道
【Social Marketing】WhatsApp Business API: Everything You Need to Know
使用cpolar优化树莓派上的网页(2)
If you can't get your heart, use "distributed lock" to lock your people
从“草原牛”到“数字牛”:蒙牛的数字化转型之道
随机推荐
【TCP/IP 五 ICMP】
软测人面试 ,HR 会问到哪些问题?学会涨薪3000+
【3D建模制作技巧分享】ZBrush如何重新拓扑
ANT1.7下载以及配置方法
【3D建模制作技巧分享】ZBrush如何设置笔刷快捷键
亿流量大考(3):不加机器,如何抗住每天百亿级高并发流量?
VSCode - common shortcut keys (continuous recording
puzzle(022.1)黑白迭代
golang打开文件和读写文件
智慧养老整体解决方案
【C - 基本概念】
单片机原理[一] 学好单片机必会的五张图
【项目实战】仿照Room实现简单管理系统
边缘检测——(纯享版)
idea 仓库地址连接不上问题
Nacos配置中心之客户端长轮询
第二讲 软件生命周期
关于el-table列表渲染
Redis理解
Detailed usage of LocalDateTime