一.简述
1.前言
书上说的:“在处理字符串数据时,会比处理整数或浮点数要复杂的多,且在不同类型的应用中,所用的字符串有不同的特点。所以要想有效的实现字符串处理,就必须根据具体的情况使用合适的储存结构。”我想,这因该是这一章存在的意义吧。
2.性质定义
串
是由零个活多个字符组成的有限序列,一般记为:
s = a0a1a2···an (n>=0)
s
是串的名;用单引号括起来的字符序列是串的值;a可以是字母,也可以是其他字符,串中字符数目n
称为串的长度。零个字符的串称为空串
。
串中,任意个连续的字符组成的字序列称为该串的子串
;包含子串的串相应地被称为主串
;通常称字符在序列中的序号为该字符串在串中的位置
.
举例:
s = ‘kanna’
b = ‘anna’
c = ‘kan’
那么字符串b,c是a的子串,且s,b,c的长度分别为:5,4,3。b在s里的位置是2,c在s里的位置是1。
二.实现
1.简述串
除了我们基本的增删改查
这几个函数,我们还得构思几个函数:串的长度;返回指定位置、指定长度的字串;字串的查找……
2.串的结构
本来书上还有一个采用数组来表示串的结构,但和我们这个堆分配(malloc)
大同小异,所以我们着重研究后者。
对于串的结构,我们还是使用两个部分:用于储存文本的基址区
长度
。
3.串的初始化
我们可以用C语言风格的字符串数组,或者字符串字面值来初始化一个串,C语言风格的字符串有个显著的特点,那就是串的末尾会有一个\0
来标记串的结束。那么通过便利来获取字串的长度,然后再一个一个赋值初始化着恶搞串。
4.串的"加法"
当然,串的加法就相当于把字串1不变,把字串2的内容拼接到字串1后面,并将字串1的长度加上字串2的长度。
5.串的比较
我们先比较两个串的长度,如果长度都不一样,那么两个串肯定不一样。如果两个串长度一样,那么开始比较两个串的第一个字符,第二个字符……,直到第一个不相同的字符,那就说明不相等,如果到了末尾都还没有不同的字符,那就说明两个串是一样的。
6.串的销毁
因为我们的串是动态生成的,所以我们直接free
掉就行惹。
7.返回字串
我们还是通过移动我们的指针
,然后返回一个依次赋值的字串。
8.查找字串
查找字串有很多种方法,这里有一个时间复杂度为n*m
级别的查找算法,至于优化,是后一节的内容。我们这里的基础算法如下:
有两个指针,选择指针a和匹配指针b。
我们选择指针先移动一个位置,然后匹配指针开始移动,如果匹配指针移动了字串的长度,都还一样的话,那么说明选择指针a的位置,就是字串开始的位置。如果在匹配指针移动字串长度个之前的位置,那么就说明匹配失败,选择指针a++,且b = a,并重新移动b指针。
三.代码
#include <stdio.h>
#include <stdlib.h>
#define OK 1
#define ERROR 0
typedef struct sstring{
char *ch;
int len;
}sstring;
sstring* StringInit(char* str);
int StringShow(sstring *str);
int StringConcat(sstring* str1, sstring* str2);
int StringCompare(sstring *str1, sstring *str2);
sstring *StringGet(sstring *str, int index, int len);
int StringFind(sstring *bstr, sstring *mstr);
int main()
{
// 主函数随便改
sstring *test1 = StringInit("bbcd");
sstring *test2 = StringInit("bcd");
//StringConcat(test1, test2);
//printf("%d", StringCompare(test1, test2));
//StringShow(StringGet(test2,0,3));
printf("%d", StringFind(test1,test2));
return 0;
}
sstring* StringInit(char* ss)
{
int lenth = 0;
//首先创建要返回的
sstring *str = (sstring*)malloc(sizeof(sstring));
//动态生成失败,直接退出
if (str == NULL) exit(1);
//如果传入的是空字符串,我们就返回一个空的字符串
if (ss == NULL)
{
str->ch = NULL;
str->len = 0;
return str;
}
// 通过依次遍历,获得传入字符串中,非/0部分长度。
while(*(ss + lenth) != '\0')
{
++lenth;
}
// 修改我们字符串的长度和动态分配它的储存空间
str->len = lenth;
str->ch = (char*)malloc(sizeof(char)*lenth);
--lenth;
// 通过遍历,将C语言字符串的内容,复制到我们的新字符串中
while(lenth >= 0)
{
*(str->ch+lenth) = *(ss+lenth);
--lenth;
}
return str;
}
int StringShow(sstring *str)
{
int ptr = 0;
printf("the string len is %d context is: ", str->len);
while(ptr < str->len)
{
printf("%c", *(str->ch + ptr));
++ptr;
}
printf("\n");
return OK;
}
int StringConcat(sstring* str1, sstring* str2)
{
sstring* stringNew = NULL;
int ptr = 0;
// 如果两个串的长度都是0,那就直接返回即可
if (str1->len + str2->len == 0)
{
return OK;
}
// 否则就先生成我们的新串,修改长度与内容
stringNew = (sstring*)malloc(sizeof(sstring));
stringNew->ch = (char*)malloc(sizeof(char)*(str1->len+str2->len));
stringNew->len = str1->len+str2->len;
// 通过循环,将str1的值写入新串
for(;ptr < str1->len; ++ptr)
{
*(stringNew->ch+ptr) = *(str1->ch+ptr);
}
// 在str1写入新串的基础上,向新串写入str2
for(ptr = 0;ptr < str2->len; ++ptr)
{
*(stringNew->ch+ptr+str1->len) = *(str2->ch+ptr);
}
// 然后这里优点坑,因为传递过来的指针是形参,并不是引用
// 所以 我们只能把新串的值赋值给原来的串
// 此时,传入函数字符串的地址没变,但是len变了, ch的地址变了
*str1 = *stringNew;
return OK;
}
int StringCompare(sstring *str1, sstring *str2)
{
int i = 0;
// 长度都不一样,所以通过长度,反应关系
if (str1->len > str2->len)
{
return 1;
}
else if (str1->len < str2->len)
{
return -1;
}
else
{
// 长度一样了,只有依次对比了
for (; i < str1->len; ++i)
{
// 只要有一个字符不一样,那就根据ascii的关系去返回大小关系
if (*(str1->ch+i) < *(str2->ch+i))
{
return -1;
}
else if (*(str1->ch+i) > *(str2->ch+i))
{
return 1;
}
}
// 循环完了也没有找到不同,所以它俩是一样的
return 0;
}
}
sstring *StringGet(sstring *str, int index, int len)
{
sstring *rstr = NULL;
int i = 0;
// 如果目标串的长度小于我们要求的长度,所以直接返回空的
if (str->len < index+len)
{
return NULL;
}
else
{
// 动态生成我们的返回串
rstr = (sstring *)malloc(sizeof(sstring));
rstr->ch = (char *)malloc(sizeof(char)*str->len);
rstr->len = len;
// 然后把目标串里的值复制到我们的返回串里
for (i = 0; i < len; ++i)
{
*(rstr->ch+i) = *(str->ch+index+i);
}
return rstr;
}
}
int StringFind(sstring *bstr, sstring *mstr)
{
int fptr = 0, lptr = 0;
int mark = 0;
// 如果我们要查找的串的长度大于了目标串,那肯定找不到的,直接返回-1
if (bstr->len < mstr->len)
{
return -1;
}
// lptr是指向 我们目标串的开始指针
// 它只需要从0遍历到(目标串长度-要查找的串的长度)就行了
for (;lptr <= (bstr->len-mstr->len); ++lptr)
{
// mark是标记位,如果有不同,那就是1 没有不同就还是0
mark = 0;
// 这个是查找指针,我们要对比的内容因该是lptr+fptr
// 它的范围是 0到查找串的长度-1
for (fptr = 0; fptr < mstr->len; ++fptr)
{
// 对比的内容是 lptr+fptr
if (*(bstr->ch+lptr+fptr) != *(mstr->ch+fptr))
{
// 有不同,更新标识,并跳出这一轮 fptr的遍历
mark = 1;
break;
}
}
// fptr遍历完了,都还没有不同的,说明找到了
if (mark == 0)
{
// 那么就因该返回我们lptr的起始位置
return lptr;
}
}
// 查遍了整个串都没找到,那就只能返回 -1了
return -1;
}