目录
一. strstr模拟实现
二. strtok模拟实现
三.关于strerror和perror的说明
一. strstr模拟实现
库函数strstr函数首部:char * strstr ( const char *str1, const char * str2);
函数的功能是在str1指向的主字符串中寻找子串str2,并且返回主字符串中子字符串第一次出现的位置(主字符串中第一个子字符串的首地址)。
如果主字符串中找不到子字符串则函数返回空指针。
函数的实现思路是暴力遍历法:
用两层循环来实现,外层循环用一个循环变量i遍历主字符串str1,每当在主字符串中找到子字符串的首元素就进入第二层循环进行两个字符串的匹配,若匹配失败,指针i回溯到匹配的起始位置继续寻找下一个子串首字符,重复上述步骤。
内层循环以两个字符串的终止符或不相等的对应字符为结束标志。
匹配成功的标志是内层循环维护子串str2的指针指向子串str2的终止符。
模拟实现:(这里使用str1和str2的指针运算代替下标i和j的运算)
char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); if (!(*str2)) 如果子串为空字符串则返回主串的首地址 { return (char*)str1; } const char* RE1 = NULL; 用于记录str1指针回溯的位置 const char* RE2 = str2; 用于记录str2指针回溯的位置 while (*str1) 找到主串中的\0则停止循环 { RE1 = str1; while (*str1++ == *str2++) { if (!(*str2)) 匹配子串中的\0则代表在主串中找到了子串 { return (char*)RE1; } if (!(str1)) 匹配过程中找到主串的\0则函数直接返回NULL { return NULL; } } str1 = RE1; str2 = RE2; str1,str2指针回溯 str1++; } return NULL; }
另外一种可读性更好,思路更加清楚的写法:
char* my_strstr2(const char* str1, const char* str2) { assert(str1 && str2); if (!(*str2)) 如果子串为空字符串则返回主串的首地址 { return (char*)str1; } const char* pstr2 = str2; pstr1和pstr2用于内层循环进行字符串匹配 const char* pstr1 = str1; while (*str1) { pstr1 = str1; 令pstr1和pstr2指向进行字符串匹配的起始位置 pstr2 = str2; while (*pstr1 && (*pstr1 == *pstr2)) { 找到匹配过程中的终止字符或不相等的对应字符后跳出循环 pstr1++; pstr2++; if ('\0' ==*pstr2 ) 只有在匹配过程中pstr2指向子字符串终止符才算匹配成功 { return (char*)str1; } } str1++; } return NULL; } 此种写法很容易进行越界检查和思路梳理
测试代码:
int main() { char arr1[] = "acdsdssdsfdgdh"; char arr2[] = "fdg"; char* retlib = strstr(arr1, arr2); char* retmy = my_strstr(arr1, arr2); char* retmy2 = my_strstr2(arr1, arr2); if (retlib || retmy || retmy2) { printf("%s\n", retlib); printf("%s\n", retmy); printf("%s\n", retmy2); } return 0; }
二. strtok模拟实现
库函数strtok函数首部: char * strtok ( char * str, const char * sep );
1.sep参数是个字符串,定义了用作分隔符的字符集合;
2.第一个参数指定一个字符串,它包含了0个或者多个sep字符串中的字符作为分割标记;
3.strtok函数找到str中的分割标记(sep字符串中的字符),并将其用 \0 替换,返回一个指向这个分割出来的子串的指针(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改);
4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个分割标记,strtok函数将保存它在字符串中的位置;
5.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记;
6.如果字符串中不存在更多的标记,则返回 NULL 指针;
模拟实现strtok:
char* my_strtok(char* String, const char* label)
{
assert(label);
static flag = 1; 用于标记被分割的主串是否被完全遍历
static char* Mark = NULL; 用于记录被分割主串中的分割位置
if (flag && !String) 第一次传空指针则返回空指针,以及后续主串若已经被完全遍历再次调
用该函数时则返回空指针
{
return NULL;
}
else if (String) 传入的String为非空指针时(传入新的被分割主串)
{
flag = 0; 重置flag
Mark = NULL; 重置Mark
char* ret = String; 记录分割段的起始位置
const char* plabel = label; plabel用于遍历分隔符字符串
while (*String)
{
plabel = label;
while (*plabel && *plabel != *String)
{
plabel++;
}
if (*plabel) 若在主串中找到分割符,则完成分割操作
{
*String = '\0';
Mark = String;
return ret;
}
String++;
}
flag = 1; 从这里跳出循环代表主串已经被完全遍历,flag标记为1
return ret;
}
else
{
char* pString = Mark+1; 用pSring作为继续遍历主串的指针变量
char* ret = Mark + 1; 记录分割段的起始位置
const char* plabel = label; plabel用于遍历分隔符字符串
while (*pString)
{
plabel = label;
while (*plabel && *plabel != *pString)
{
plabel++;
}
if (*plabel) 若在主串中找到分割符,则完成分割操作
{
*pString = '\0';
Mark = pString;
return ret;
}
pString++;
}
flag = 1; 从这里跳出循环代表主串已经被完全遍历,flag标记为1
return ret;
}
}
测试代码:
int main() { char str1[] = "sdf@cc.gif@bit"; char str2[] = "sdf@cc.gif@bit"; char str3[] = "sdf@cc.gif@bit"; char str4[] = "sdf@cc.gif@bit"; char label[] = "@."; char* pstr = NULL; for (pstr = strtok(str1, label); pstr != NULL; pstr = strtok(NULL, label)) { printf("%s\n", pstr); } printf("-----------\n"); for (pstr = my_strtok(str2, label); pstr != NULL; pstr = my_strtok(NULL, label)) { printf("%s\n", pstr); } printf("-----------\n"); for (pstr = strtok(str3, label); pstr != NULL; pstr = strtok(NULL, label)) { printf("%s\n", pstr); } printf("-----------\n"); for (pstr = my_strtok(str4, label); pstr != NULL; pstr = my_strtok(NULL, label)) { printf("%s\n", pstr); } printf("-----------\n"); return 0; }
三.关于strerror和perror的说明
sterror函数首部:char * strerror ( int errnum );
C语言标准库中有一个全局整形变量errno,当用户调用库函数发生错误时,库函数会将错误码(一个整形数值)存在errno中,将errno作为实参传入strerror函数中就可以将错误码(一个整形数值)翻译为对应的错误信息,并以字符串首地址的形式返回给用户,用户可以将描述错误信息的字符串打印出来。比如打开文件时,用于检查是否发生错误可以调用该函数。
#include <stdio.h> #include <string.h> #include <errno.h>//必须包含的头文件 int main () { FILE * pFile; pFile = fopen ("unexist.ent","r"); if (pFile == NULL) { printf ("Error opening file unexist.ent: %s\n",strerror(errno)); } //errno: Last error number return 0; }
perror函数首部:void perror( const char *string ) ;
形参中的const char *string是用户传入的自定义信息,perror函数可以自动访问全局变量errno,并将错误信息和用户传入的自定义信息一起打印出来,使用起来更加方便。
比如打开文件时,用于检查是否发生错误也可以调用该函数。
#include <stdio.h> #include <string.h> #include <errno.h>//必须包含的头文件 int main () { FILE * pFile; pFile = fopen ("unexist.ent","r"); if (pFile == NULL) { perror("error message :"); } return 0; }