一、串的介绍
串型结构就是由若干个类型相同的元素组成的数据结构,末尾有一个结束标志,字符串就是由字符元素组成的串型结构,'\0’是它的结束标志,使用它可以存储单词、文章、汉字等文字信息。
随着计算机和编程语言的发展,字符串在程序中使用的越来越多,字符串就简称串,对它的操作都是对串里面所有字符整体操作,以’\0’为结束标志,如果字符串的末尾没有’\0’,可能会产生乱码、段错误、脏数据等问题。
字符串结构应该具备的功能、算法有:
创建:定义字符串
销毁:释放字符串
清空:删除所有的字符
复制:就是strcpy函数
连接:就是strcat函数
比较:就是strcmp函数
长度:就是strlen函数
查询字串:就是strstr函数
字符串的表示与实现
字符串一般有两实现方式,这两种方式都是使用顺序表,只是内存不同而已:
1、用栈内存存储字符,定长方式,字符的数量一旦超出表的范围,为了防止内存越界要对字符串进行截取,了解一下即可。
2、使用堆内存破碎字符,在操作字符串时,自动扩展堆内存。
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
typedef struct String
{
char* ch;
size_t size;
}String;
// 创建字符串
String* create_string(void)
{
String* str = malloc(sizeof(String));
str->ch = NULL;
str->size = 0;
return str;
}
// 计算长度
size_t len_string(String* str)
{
return strlen(str->ch);
}
// 复制
void copy_string(String* str,const char* ch)
{
size_t size = strlen(ch)+1;
if(size > str->size)
{
str->ch = realloc(str->ch,size);
str->size = size;
}
strcpy(str->ch,ch);
}
// 构建字符串
String* assign_string(const char* ch)
{
String* str = create_string();
copy_string(str,ch);
return str;
}
// 判断是否是空串
bool empty_string(String* str)
{
return NULL == str->ch;
}
// 赋值
void sav_string(String* str1,String* str2)
{
if(str1->size < str2->size)
{
str1->ch = realloc(str1->ch,str2->size);
str1->size = str2->size;
}
strcpy(str1->ch,str2->ch);
}
// 比较
int cmp_string(String* str1,String* str2)
{
return strcmp(str1->ch,str2->ch);
}
// 连接
void cat_string(String* str1,String* str2)
{
size_t size = strlen(str1->ch)+strlen(str2->ch)+1;
if(size >= str1->size)
{
str1->ch = realloc(str1->ch,size);
str1->size = size;
}
strcat(str1->ch,str2->ch);
}
// 清空字符串
void clear_string(String* str)
{
free(str->ch);
str->size = 0;
}
// 销毁字符串
void destroy_string(String* str)
{
free(str->ch);
free(str);
}
int main(int argc,const char* argv[])
{
/*
String* str1 = assign_string("hehe");
String* str2 = create_string();
// 浅拷贝,两个对象的ch指向了同一个字符串,当其它对象被销毁,另一个会受影响
*str2 = *str1; // str2->ch = str1->ch; str2->len = str1->len;
destroy_string(str1);
puts(str2->ch);
// 深拷贝,如果结构体中有成员是指针类型,且指向了堆内,这种结构变量不能直接赋值(浅拷贝),为了不出问题,我们需要实现深拷贝
sav_string(str2,str1);
destroy_string(str1);
puts(str2->ch);
*/
String* str1 = assign_string("hehe");
String* str2 = assign_string("xixi12rfaspoikjrqw;elifkj;lasejkrf;oawlikeujf;olaeirjtfasldkjf;qlwiejfa;sldkjf;qwlsekjfa;sldkfj;l");
cat_string(str1,str2);
puts(str1->ch);
return 0;
}
封装字符串的意义
1、字符串被封装成数据结构后,使用者不需要关心字符串的空间问题,但这种封装在C语言下没有太大意义,因为C语言语法的原因会使用字符串操作更麻烦。
2、我们在C++语言中重写该数据结构,因为C++的语法会让该数据结构使用起来更方便。
子串查询的算法
假定有两个字符串str1,str2,查询子串就是在字符串str1中查询是否存在str2,如果存在则返回str2首次出现的位置,这个操作叫子串查询。
char *str_str(const char *str1, const char *str2)
{
assert(NULL != str1 && NULL != str2);
for (int i = 0, j; '\0' != str1[i]; i++)
{
for (j = 0; '\0' != str2[j] && str2[j] == str1[i + j]; j++)
;
if ('\0' == str2[j])
return (char *)str1 + i;
}
return NULL;
}
char *str_str(const char *str1, const char *str2)
{
assert(NULL != str1 && NULL != str2);
int i = 0, j = 0;
while ('\0' != str1[i] && '\0' != str2[j])
{
if (str1[i] == str2[j])
{
i++;
j++;
}
else
{
i = i - j + 1;
j = 0;
}
}
return '\0' == str2[j] ? (char *)str1 + i - j : NULL;
}
char* str_str(const char* str1,const char* str2)
{
assert(NULL != str1 && NULL != str2);
int sum1 = 0 , sum2 = 0 , len = 0;
while('\0'!=str2[len]&&'\0'!=str1[len])
{
sum1 += str1[len];
sum2 += str2[len++];
}
for(int i=len; '\0' != str1[i]; i++)
{
printf("%d %d\n",sum1,sum2);
if(sum1 == sum2 && 0 == strncmp(str1+i-len,str2,len))
{
return (char*)str1+i-len;
}
else
{
sum1 -= str1[i-len];
sum1 += str1[i];
}
}
return NULL;
}