一、函数strchr与strrchr
- 注意:
- 这两个函数的功能,都是在指定的字符串 s 中,试图找到字符 c。
- strchr() 从左往右找,strrchr() 从右往左找。
- 字符串结束标记 ‘\0’ 被认为是字符串的一部分。
- 图解:
- 示例代码:
#include <stdio.h>
#include <string.h>
// 自己写的库
/**
* @brief 查找字符c在字符串s的位置
* @note 从左到右来查找这个字符的位置
* @param s:指定要查找的字符串
c:待查字符
* @retval 成功:返回指向找到的字符的指针
失败:返回NULL
*/
char* MyLib_StrLChr(const char *s, int c)
{
// 1、如果传进来的字符串是空的,那就没有必要继续下面的判断了
if ( s == NULL)
return NULL; // 失败,返回NULL
// 2、移动指针s获取字符串的字符并与字符c判断是否一致,一致就退出
while ( (*s != '\0') && (*s != c))
{
s++; // 指针s会一直往字符串的末尾移动(一直到'\0'为止)
}
// 3、判断是结束符'\0',还是找到的字符c
if ( *s == '\0')
return NULL;
else
return (char *)s;
}
/**
* @brief 查找字符c在字符串s的位置
* @note 从右到左来查找这个字符的位置
* @param s:指定要查找的字符串
c:待查字符
* @retval 成功:返回指向找到的字符的指针
失败:返回NULL
*/
char* MyLib_StrRChr(const char *s, int c)
{
// 1、如果传进来的字符串是空的,那就没有必要继续下面的判断了
if ( s == NULL)
return NULL; // 失败,返回NULL
const char *tmp_p = s; // 记住最初的这个字符串的地址(也就是字符串首字符的地址)
// 2、先将指针s移动到字符串的末尾
while ( *s != '\0')
{
s++;
}
// 3、从右往左判断c是否存在
while ( (*s != c) && ( s != tmp_p))
{
s--;
}
// 3、判断是结束符'\0',还是找到的字符c
if ( s == tmp_p)
return NULL;
else
return (char *)s;
}
// 主函数
int main(int argc, char const *argv[])
{
// (1)、官方写的库
// 1、strchr函数(从左往右找)
const char *s1 = "shijienameda";
int c1 = 'i';
char * p1 = strchr(s1, c1);
printf("p1 == %s\n", p1);
// 2、strrchr函数(从右往左找)
const char *s2 = "shijienameda";
int c2 = 'i';
char * p2 = strrchr(s2, c2);
printf("p2 == %s\n", p2);
// (2)、自己写的库
// 1、MyLib_StrChr函数(从左往右找)
const char *s3 = "shijienameda";
int c3 = 'i';
char * p3 = MyLib_StrLChr(s3, c3);
printf("p3 == %s\n", p3);
// 2、MyLib_StrRChr函数(从右往左找)
const char *s4 = "shijienameda";
int c4 = 'i';
char * p4 = MyLib_StrRChr(s4, c4);
printf("p4 == %s\n", p4);
return 0;
}
二、函数strstr
- 图解:
- 示例代码:
#include <stdio.h>
#include <string.h>
// 自己写的库
/**
* @brief 在指定的一个字符串中,找到一个子串
* @note None
* @param s:指定要查找的字符串
c:待查字符
* @retval 成功:返回指向找到的字符的指针
失败:返回NULL
*/
char* MyLib_StrStr(const char *haystack, const char *needle)
{
// 初始化
const char *tmp_p1 = haystack;
const char *tmp_p2 = needle;
const char *p1 = haystack;
const char *p2 = needle;
// 1、判断输入的两个字符串是否为NULL,为NULL就直接退出
if ( (haystack == NULL) || (needle == NULL))
{
return NULL;
}
// 2、遍历haystack字符串,找到所有可能
while ( *tmp_p1 != '\0')
{
// 在进去比较循环之前,将p1和p2的最初的的位置(循环之前)进行保留
p1 = tmp_p1;
p2 = tmp_p2;
// 进入循环依次比较两个字符串的字符是否一致,防止比较的字符串在末尾
while ( (*p1 == *p2) && (*p1 != '\0') && (*p2 != '\0'))
{
p1++;
p2++;
}
if ( *p2 == '\0') // 判断p2是否指向'\0',是的话,就意味着找到了s1中的子串s2
{
return (char *)tmp_p1; // 返回s1中的子串的地址
}
tmp_p1++;
}
// 3、没有找到该子串,就返回NULL
return NULL;
}
// 主函数
int main(int argc, char const *argv[])
{
// (1)、官方写的库
const char *s1 = "chuangqianmingyueguang,yishidishangshuang";
const char *s2 = "yishi";
char *p1 = strstr(s1, s2);
printf("p1 == %s\n", p1);
// (2)、自己写的库
const char *s3 = "chuangqianmingyueguang,yishidishangshuang";
const char *s4 = "yishi";
char *p2 = MyLib_StrStr(s3, s4);
printf("p2 == %s\n", p2);
return 0;
}
三、函数strlen
- 图解:
- 示例代码:
#include <stdio.h>
#include <string.h>
// 给类型取个别名
typedef unsigned long my_size_t;
// 自己写的库
/**
* @brief 求一个指定字符串的长度
* @note None
* @param s:需要求长度的字符串
* @retval 成功:返回字符串的长度,不包含字符串结束标记'\0'
失败:返回0
*/
my_size_t MyLib_StrLen(const char *s)
{
// 1、如果传进来的字符串是空的,那就没有必要继续下面的判断了
if ( s == NULL)
return 0; // 失败,返回0
// 2、计算从左到右,一直到'\0',有多少个字符
my_size_t cnt = 0;
while ( *s != '\0')
{
cnt++;
s++;
}
// 3、返回计算的字符串的长度
return cnt;
}
// 主函数
int main(int argc, char const *argv[])
{
// (1)、官方写的库
const char buf1[256] = "wuaiyousan,riyueyuqing,riweizhao,yueweimu,qingweizhaozhaomumu";
size_t len1 = strlen(buf1);
printf("len1 == %lu\n", len1);
// (2)、自己写的库
const char buf2[256] = "wuaiyousan,riyueyuqing,riweizhao,yueweimu,qingweizhaozhaomumu";
size_t len2 = MyLib_StrLen(buf2);
printf("len2 == %lu\n", len2);
return 0;
}
四、函数strcpy与strncpy
- 注意:
- 复制src字符串的时候,也会将'\0',一并复制到dest中
- 这两个函数的功能,都是将 src 中的字符串,复制到 dest 中。
- strcpy() 没有边界控制,因此可能会由于 src 的过长而导致内存溢出。
- strncpy() 有边界控制,最多复制 n+1 个字符(其中最后一个是 ‘\0’ )到 dest 中。
- 图解:
- 示例代码:
#include <stdio.h>
#include <string.h>
// 自己写的库
/**
* @brief 复制字符串
* @note None
* @param dest:指向目标内存,存储复制的字符串
src: 指向待复制的字符串
* @retval 成功:返回一个指针,指向复制后的字符串,等价于dest
失败:返回NULL
*/
char* MyLib_StrCpy(char *dest, const char *src)
{
// 1、如果传进来的指针是有误的(比如:NULL),没有必要继续下面的内容了
if ( (dest == NULL) || (src == NULL))
{
return NULL;
}
char *tmp_p = dest; // 记录dest指针最初指向的地方
// 2、根据str字符串的结束符'\0',来进行移位赋值给dest内存
while ( *src != '\0' )
{
*tmp_p = *src; // 赋值操作,将src内存里面的内容复制到dest内存中
tmp_p++;
src++;
}
// 3、返回指向dest内存的地址
return dest;
}
/**
* @brief 复制字符串
* @note None
* @param dest:指向目标内存,存储复制的字符串
src: 指向待复制的字符串
n: 规定最多将src中的n个字符复制到dest内存中
* @retval 成功:返回一个指针,指向复制后的字符串,等价于dest
失败:返回NULL
*/
char* MyLib_StrNCpy(char *dest, const char *src, unsigned long n)
{
// 1、如果传进来的指针是有误的(比如:NULL),没有必要继续下面的内容了
if ( (dest == NULL) || (src == NULL))
{
return NULL;
}
char *tmp_p = dest; // 记录dest指针最初指向的地方
// 2、根据n来限定复制src字符串内容给dest内存的范围
int len = strlen(src);
if (n >= len)
{
n = len;
}
while ( n -- )
{
*tmp_p = *src; // 赋值操作,将src内存里面的内容复制到dest内存中
tmp_p++;
src++;
}
// 3、返回指向dest内存的地址
return dest;
}
// 主函数
int main(int argc, char const *argv[])
{
// (1)、官方写的库
// 1、strcpy函数
char dest_buf1[256] = {0}; // 目标内存1
const char str_buf1[128] = "shijienameda"; // 要复制的字符串1
char *p1 = strcpy(dest_buf1, str_buf1); // 有点危险,没有边界
printf("dest_buf1 == %s\n", dest_buf1);
printf("p1 == %s\n", p1);
// 2、strncpy函数
char dest_buf2[256] = {0}; // 目标内存2
const char str_buf2[128] = "shijienameda"; // 要复制的字符串2
char *p2 = strncpy(dest_buf2, str_buf2, 5); // 比较安全,可以控制复制的范围
printf("dest_buf2 == %s\n", dest_buf2);
printf("p2 == %s\n", p2);
// (2)、自己写的库
// 1、MyLib_StrCpy函数
char dest_buf3[256] = {0}; // 目标内存1
const char str_buf3[128] = "shijienameda"; // 要复制的字符串1
char *p3 = MyLib_StrCpy(dest_buf3, str_buf3); // 有点危险,没有边界
printf("dest_buf3 == %s\n", dest_buf3);
printf("p3 == %s\n", p3);
// 2、MyLib_StrNCpy函数
char dest_buf4[256] = {0}; // 目标内存2
const char str_buf4[128] = "shijienameda"; // 要复制的字符串2
char *p4 = MyLib_StrNCpy(dest_buf4, str_buf4, 5); // 比较安全,可以控制复制的范围
printf("dest_buf4 == %s\n", dest_buf4);
printf("p4 == %s\n", p4);
return 0;
}
五、函数strcmp与strncmp
- 注意:
- 比较字符串大小,实际上比较的是字符的 ASCII码值的大小。
- 从左到右逐个比较两个字符串的每一个字符,当能“决出胜负”时立刻停止比较。
- 图解:
- 示例代码:
#include <stdio.h>
#include <string.h>
//
// 主函数
int main(int argc, char const *argv[])
{
// (1)、官方写的库
// 1、strcmp函数
const char password1[128] = "fhq9127";
char input_pw1[128] = "fhq9127";
if ( strcmp(password1, input_pw1) == 0) // 两个字符串的字符都相等,返回0
printf("(strcmp)密码输入正确!\n");
else
printf("(strcmp)密码输入错误!\n");
// 2、strncmp函数
const char password2[128] = "fhq9127";
char input_pw2[128] = "fhq9128";
if ( strncmp(password2, input_pw2, 3) == 0) // 两个字符串的字符都相等,返回0
printf("(strcmp)密码输入正确!\n");
else
printf("(strcmp)密码输入错误!\n");
return 0;
}
六、函数strcat与strncat
- 注意:
- 这两个函数的功能,都是将 src 中的字符串,复制拼接到 dest 的末尾。
- strcat() 没有边界控制,因此可能会由于 src 的过长而导致内存溢出。
- strncat() 有边界控制,最多复制 n+1 个字符(其中最后一个是 ‘\0’ )到 dest 的末尾。
- 图解:
- 示例代码:
#include <stdio.h>
#include <string.h>
// 自己写的库
/**
* @brief 将两个字符串拼接起来
* @note None
* @param dest:指向目标内存,最终存储拼接之后的字符串
src: 指向需要拼接的字符串,最终将src拼接到dest的后面
* @retval 成功:返回一个指针,指向拼接后的字符串,等价于dest
失败:返回NULL
*/
char* MyLib_StrCat(char *dest, const char *src)
{
char *p1 = dest;
const char *p2 = src;
// 1、如果传进来的指针是有误的(比如:NULL),没有必要继续下面的内容了
if ( (dest == NULL) || (src == NULL))
{
return NULL;
}
// 2、将指针p1移动到字符串末尾(这个'\0'符号的位置)
while ( *p1 != '\0')
{
p1++;
}
// 3、将src字符串的字符,复制到dest中
while ( *p2 != '\0')
{
*p1 = *p2;
p1++;
p2++;
}
// 4、补回一个'\0'字符给dest字符串
*p1 = '\0';
// 5、返回dest最初指向的内存位置
return dest;
}
/**
* @brief 将两个字符串拼接起来
* @note None
* @param dest:指向目标内存,最终存储拼接之后的字符串
src: 指向需要拼接的字符串,最终将src拼接到dest的后面
n: 规定最多将src中的n个字节拼接到dest的后面
* @retval 成功:返回一个指针,指向拼接后的字符串,等价于dest
失败:返回NULL
*/
char* MyLib_StrNCat(char *dest, const char *src, unsigned long int n)
{
char *p1 = dest;
const char *p2 = src;
// 1、如果传进来的指针是有误的(比如:NULL),没有必要继续下面的内容了
if ( (dest == NULL) || (src == NULL) || (n<=0))
{
return NULL;
}
// 2、将指针p1移动到字符串末尾(这个'\0'符号的位置)
while ( *p1 != '\0')
{
p1++;
}
// 3、将src字符串的字符,复制到dest中
while ( (*p2 != '\0') && (n--))
{
*p1 = *p2;
p1++;
p2++;
}
// 4、补回一个'\0'字符给dest字符串
*p1 = '\0';
// 5、返回dest最初指向的内存位置
return dest;
}
// 主函数
int main(int argc, char const *argv[])
{
// (1)、官方写的库
// 1、strcat函数
char dest_buf[256] = "qingge"; // 目标字符串
const char src_buf1[128] = ",nihaoshuai"; // 要拼接的字符串1
const char src_buf2[128] = ",zhendejiade"; // 要拼接的字符串2
char *p1 = strcat(dest_buf, src_buf1);
printf("p1 == %s\n", p1);
// 2、strncat函数
char *p2 = strncat(dest_buf, src_buf2, 7);
printf("p2 == %s\n", p2);
// (2)、自己写的库
// 1、MyLib_StrCat函数
char dest_buf1[256] = "qingge"; // 目标字符串
const char src_buf3[128] = ",nihaoshuai"; // 要拼接的字符串3
const char src_buf4[128] = ",zhendejiade"; // 要拼接的字符串4
char *p3 = strcat(dest_buf1, src_buf3);
printf("p3 == %s\n", p3);
// 2、MyLib_StrNCat函数
char *p4 = MyLib_StrNCat(dest_buf1, src_buf4, 7);
printf("p4 == %s\n", p4);
return 0;
}
七、函数strtok
- 注意:
- 该函数会将改变原始字符串 str,使其所包含的所有分隔符变成结束标记 ‘\0’ 。
- 由于该函数需要更改字符串 str,因此 str 指向的内存必须是可写的。
- 首次调用时 str 指向原始字符串,此后每次调用 str 用 NULL 代替。
- 图解:
- 示例代码:
#include <stdio.h>
#include <string.h>
/**
* @brief 将某个字符串,按照指定的分隔符拆解为子串
* @note None
* @param str: 指定要拆解的字符串
delim: 分隔符,此处可以指定多个分隔符,形成一个分隔符串
* @retval 成功:指向子串的指针
失败:返回NULL,代表拆解完毕
*/
char* MyLib_StrTok(char *str, const char *delim)
{
static char *last_str_p = NULL; // 记住上一次调用函数时使用 last_str_p的地方;
char *p1 = str;
char *p2 = NULL;
// 1、将上一次返回的指向,赋值给现在的str
if (p1 == NULL)
{
p1 = last_str_p;
}
// 2、判断输入的两个字符串是否为NULL,是的话就直接退出
if ( (p1 == NULL) || (delim == NULL))
{
return NULL;
}
// 3、退出函数的准备
// 判断p1是否指向了字符串的末尾,是的话退出此函数
if (*p1 == '\0')
{
return NULL;
}
// 4、将str的值给p2,记录p1现在的位置
p2 = p1;
// 5、移动p1的位置,直到字符串结束,或者p1指针遇到分隔符delim
while ( (*p1 != '\0') && (*p1 != *delim))
{
p1++;
}
// 6、将分隔符设置为'\0',方便字符串获取
if (*p1 != '\0')
{
*p1 = '\0';
last_str_p = p1+1; // 将位置移动下一次要分隔的字符串的首字符上
}
else
{
last_str_p = p1; // 字符串分割到末尾了,让其指向字符串末尾的'\0',方便下一次进行判断
}
return p2;
}
// 主函数
int main(int argc, char const *argv[])
{
// (1)、官方写的库
char dest_buf[256] = "www.yueqian.com.cn"; // 目标字符串,必须是可写内存
char *p1 = strtok(dest_buf, "."); // 这里的分隔符(准确来说应该是分隔符串),是字符串,但是一般情况下以字符形式来切割
while ( p1 != NULL)
{
printf("p1 == %s\n", p1); // 不是说一定要用printf函数来表示,而是代表现在这个p1代表的子串,现在正在被我所用
p1 = strtok(NULL, ".");
}
// (2)、自己写的库
char dest_buf1[256] = "www.yueqian.com.cn"; // 目标字符串,必须是可写内存
char *p2 = MyLib_StrTok(dest_buf1, "."); // 这里的分隔符(准确来说应该是分隔符串),是字符串,但是一般情况下以字符形式来切割
while ( p2 != NULL)
{
printf("p2 == %s\n", p2); // 不是说一定要用printf函数来表示,而是代表现在这个p1代表的子串,现在正在被我所用
p2 = MyLib_StrTok(NULL, ".");
}
return 0;
}
至此,希望看完这篇文章的你有所收获,我是Bardb,译音八分贝,道友,下期见!