十四、实现 strchr()和 strrchr()函数
示例:
#define _CRT_SECURE_NO_WARNINGS // 使用 C 风格字符串操作的函数需要定义这个宏
#include <iostream>
using namespace std;
// 返回在字符串 s 中第一次出现 c 的位置,如果找不到,返回 0。
// babcddefae a
const char* mystrchr(const char* s, int c)
{
char* p = (char *)s; // 用于遍历字符串 s 的指针。
while (*p) // 遍历字符串,如果找到了,循环中会 return。
{
if (*p==c) return p;// 如果找到了,返回当前位置。
p++; // 如果没有找到,继续找。
}
return 0; // 返回空地址。
}
// 返回在字符串 s 中最后一次出现 c 的位置,如果找不到,返回 0。
// 注意:不要从字符串的最后一个字符往前找,因为用 strlen()获取字符串长度时,需要遍历一次
字符串,效率更低。
// babcddefae a strlen(s)
const char* mystrrchr(const char* s, const int c)
{
char* p = (char*)s; // 用于遍历字符串 s 的指针。
char* p1 = 0; // 如果没找到,p1 为 0,如果已经找到,p1 为最后一次出现 c 的
位置。
while (*p) // 遍历字符串,不管有没有找到,都要遍历整个字符串。
{
if (*p == c) p1=p; // 如果找到了,把 p1 更新为当前位置。
p++;
}
return p1; // 返回 p1。
}
int main()
{
const char* p1 = mystrchr("abccdacb", 'a');
if (p1!=0) cout << p1 << endl; // 显示 abccdacb
const char* p2 = mystrchr("abccdacb", 'e');
if (p2 != 0) cout << p2 << endl; // 什么也没有显示。
const char* p3 = mystrrchr("abccdacb", 'a');
if (p3 != 0) cout << p3 << endl; // 显示 acb
const char* p4 = mystrrchr("abccdacb", 'e');
if (p4 != 0) cout << p4 << endl; // 什么也没有显示。
}
十五、实现 strcmp()和 strncmp()函数
示例:
#define _CRT_SECURE_NO_WARNINGS // 使用 C 风格字符串操作的函数需要定义这个宏
#include <iostream>
using namespace std;
// 功能:比较 str1 和 str2 的大小。
// 返回值:相等返回 0,str1 大于 str2 返回 1,str1 小于 str2 返回 - 1;
int mystrcmp(const char* str1, const char* str2)
{
size_t pos = 0; // 数组下标。
// abc0 ab0
while (true)
{
if (str1[pos] > str2[pos]) return 1; // str1>str2
if (str1[pos] < str2[pos]) return -1; // str1<str2
if (str1[pos] == 0 && str2[pos] == 0) return 0; // 两个字符串相等。
pos++; // 如果没有分出大小,继续比较下一个字符。
}
}
// 功能:比较 str1 和 str2 前 n 个字符的大小。
// 返回值:相等返回 0,str1 大于 str2 返回 1,str1 小于 str2 返回 - 1;
int mystrncmp(const char* str1, const char* str2, const size_t n)
{
size_t pos = 0; // 数组下标。
for (size_t pos=0; pos<n; pos++)
{
if (str1[pos] > str2[pos]) return 1; // str1>str2
if (str1[pos] < str2[pos]) return -1; // str1<str2
if (str1[pos] == 0 && str2[pos] == 0) return 0; // 两个字符串相等。
}
return 0; // 如果比较了 n 次还没有分出大小,返回 0。
}
// 两个字符串比较的方法是比较字符的 ASCII 码的大小
// 从两个字符串的第一个字符开始,如果分不出大小
// 就比较第二个字符,如果全部的字符都分不出大小,就返回 0,表示两个字符串相等。
int main()
{
cout << strcmp("abcde", "abcdef") << endl;
cout << mystrcmp("abcde", "abcdef") << endl;
}
十六、实现 strstr()函数
char *strstr(const char* str,const char* substr);
返回子串 substr 在目标串 str 中第一次出现的位置,如果找不到,返回 0。
BF 算法,即暴力(Brute Force)算法。
KMP(看毛片)算法是一种改进的字符串匹配算法,由 D.E.Knuth,J.H.Morris 和 V.R.Pratt 提出(简
称 KMP 算法)。
示例:
#define _CRT_SECURE_NO_WARNINGS // 使用 C 风格字符串操作的函数需要定义这个宏
#include <iostream>
using namespace std;
// 返回子串 substr 在目标串 str 中第一次出现的位置,如果找不到,返回 0。
const char* mystrstr(const char *str,const char *substr)
{
size_t ii = 0, jj = 0; // 目标串和子串的数组下标,都从 0 开始。
size_t len = strlen(str), slen = strlen(substr); // 目标串和子串的长度。
while ( (ii < len) && (jj < slen) ) // 如果 ii==len,表示目标串已结束;如果 jj==slen,表
示已成功匹配。
{
if (str[ii] == substr[jj]) { // 如果目标串和子串数组下标指向的字符是相等的。
ii++; jj++; // 继续比较后面的字符。
}
else { // 如果目标串和子串数组下标指向的字符不相等,这次白
干了。
ii = ii - jj; // 目标串数组下标回退 jj。
jj = 0; // 子串数组下标回退到 0,准备重来。
ii++; // 目标串往右移一个字符,准备重来。
}
}
if (jj == slen) return str+(ii-jj); // 如果循环终止时,jj==slen,表示查找成功了。
return 0; // 查找失败。
}
int main()
{
const char* p1 = mystrstr("aabcde", "abc");
if (p1) cout << p1 << endl; // 显示 abcde。
const char* p2 = mystrstr("aabcde", "ba");
if (p2) cout << p2 << endl; // 什么也没有显示。
}
十七、删除字符串右边指定的字符
函数的原型:
void deleterchr(char *str,const const int cc = ' '); "abc0yy" "abc"
"西施 " "西施" 方法二:遍历 1 次字符串,写 1 次内存。
方法一:遍历 1-2 次字符串(调用 strlen()函数算一次),写 n 次内存。
示例:
#define _CRT_SECURE_NO_WARNINGS // 使用 C 风格字符串操作的函数需要定义这个宏
#include <iostream>
using namespace std;
// 删除字符串左边指定的字符。
void deleterchr(char* str, const int cc = ' ')
{
if (str == 0) return; // 如果传进来的是空地址,直接返回,防止程序崩溃。
char* p = str; // 指向字符串的首地址。
char* piscc = 0; // 右边全是字符 cc 的第一个位置。
while (*p != 0) // 遍历字符串。
{
if (*p == cc && piscc == 0) piscc = p; // 记下字符 cc 的第一个位置。
if (*p != cc) piscc = 0; // 只要当前字符不是 cc,清空 piscc。
p++;
}
if (piscc != 0) *piscc = 0;
}
int main()
{
char str[51];
strcpy(str, "abcdee aaaaa aa");
deleterchr(str,'a');
cout << str << "=" << endl;
}
十八、删除字符串左边指定的字符
函数的原型:
void deletelchr(char* str, const int cc = ' ');
// yyyabycd abycd
示例:
#define _CRT_SECURE_NO_WARNINGS // 使用 C 风格字符串操作的函数需要定义这个宏
#include <iostream>
using namespace std;
// 删除字符串左边指定的字符。
void deletelchr(char* str, const int cc = ' ')
{
if (str == 0) return; // 如果传进来的是空地址,直接返回,防止程序崩溃。
char* p = str; // 指向字符串的首地址。
while (*p == cc) // 遍历字符串,p 将指向左边第一个不是 cc 的字符。
p++;
memmove(str, p, strlen(str) - (p - str)+1); // 把结尾标志 0 也拷过来。
}
// memcpy()没有考虑内存重叠的情况,如果内存有重叠,其行为是不确定的。
// memmove()函数在 memcpy()函数的基础上加入了对内存重叠拷贝的处理,保证其正确性。
// 如果能确定拷贝内存没有重叠,memcpy()比 memmove()更高效,如果有重叠,只能 memmove()。
int main()
{
char str[51];
strcpy(str, " aaaaabbaaaa bcdee aaaaa aa");
deletelchr(str);
cout << "=" << str << "=" << endl;
}
十九、删除字符串中间的字符串
函数的原型:
void deletestr(char* str, const char* substr);
// aaxyzaa0 aaaa0
// aaxyxyzzaa aaxyzaa aaaa
示例:
#define _CRT_SECURE_NO_WARNINGS // 使用 C 风格字符串操作的函数需要定义这个宏
#include <iostream>
using namespace std;
// aaxyzaa aaaa
// aaxyxyzzaa aaxyzaa aaaa
// 删除字符串中间的字符串。
void deletestr(char* str, const char* substr)
{
if (str == 0 || substr == 0) return; // 如果传进来的是空地址,直接返回,防止程序崩溃。
size_t slen = strlen(substr); // 子串的长度。
if (slen == 0) return; // 如果子串的内容为空,直接返回,否则会死循环。
while (true) {
char* p = strstr(str, substr); // 在目标串中查找子串。
if (p == 0) return; // 如果目标串中没有子串,函数返回。
size_t len = strlen(str); // 获取目标串的长度。
// aaxyzaa0 aaaa0
// p
// p+slen
memmove(p, p + slen, len - (p - str) - slen + 1); // 从目标串中删除子串。
// deletestr(str, substr); // 递归调用自己。
}
}
// memcpy()没有考虑内存重叠的情况,如果内存有重叠,其行为是不确定的。
// memmove()函数在 memcpy()函数的基础上加入了对内存重叠拷贝的处理,保证其正确性。
// 如果能确定拷贝内存没有重叠,memcpy()比 memmove()更高效,如果有重叠,只能 memmove()。
int main()
{
char str[51];
strcpy(str, "aaxyxyzzaa");
deletestr(str, "xyz");
cout << "=" << str << "=" << endl;
}