计算机中的非数值处理的对象基本上都是字符串,目前对于字符串的使用和处理也越来越常见,也是很多公司笔试和面试的常见内容。
//比较两个字符串
串是由零个或多个字符组成的有限序列,一般记为
s = 'a1a2……an', n表示字符串的长度。
字符串需要
注意:
1. 称两个字符串是相等的,只有当两个串的长度相等,并且对应位置上的字符都相等时才可以。
2. 空格串和空串不是一回事。因为空格也是字符。
串的表示方法有以下3种:
1. 定长顺序存储方式。
这种方法用一组地址连续的存储单元存储串值。因为这种方法为每个定义的串变量分配一个固定长度的存储区,所以一般要预先知道所要使用的串的大概的长度,并且经常造成存储空间的浪费,因为很可能预先分配的这段空间没有全部被占用。
2. 堆分配存储表示
根据串的大小动态的分配所需的存储空间。显然这种方法比较好,对存储空间的利用率较高,应用也较为广泛。
串的结构定义:
typedef struct
{
char *ch;
int length;
}HString;
串的基本操作函数如下:
//生成一个字符串
Status StrAssign(HString &T, char *chars)
{
int i = 0,j = 0;
char *c = NULL;
//释放T原来所占的空间
if(T.ch)
{
free(T.ch);
}
//求字符串的长度
for(i=0,c = chars; *c ; ++i,++c);//正确的写法
//然后根据长度分配空间,并且将字符串的内容复制到空间中
if(!i)
{
T.ch = NULL;
T.length = 0;
}
else
{
if(!(T.ch = (char*)malloc(i*sizeof(char))))
{
exit(OVERFLOW);
}
for(j=0;j<i;j++)
{
T.ch[j] = chars[j];
}
T.length = i;
}
return OK;
}
这里,求字符串的长度用了比较笨的一种方法,一个字符依次计算,直到读到字符结束符。
这个循环我写错了两次:
第一次:
char c;
for(i=0,c = chars[0]; c ; ++i,++chars);
显然,c作为控制循环结束的条件,却一直没变。
第二次:
for(i=0, c = chars[0]; c ; ++i,c++);
输入参数chars所指向的字符串中才具有结束符‘\0’,而我只是让c再不停的增加,c是我定义的一个char型变量,因此永远不会结束,或是按ASCII码加到255的时候才会结束。
这个地方其实是用C语言的字符串处理函数strlen()也可以完成求长度的操作,
//比较两个字符串
int StrCompare(HString S, HString T)
{
int i;
for(i=0;i<S.length&&T.length;i++)
{
if(S.ch[i] != T.ch[i])
{
return S.ch[i] - T.ch[i];
}
}
return S.length - T.length; //0是通过长度的比较产生的。
}
无需多说,只需要注意相等的时候是通过两个字符串的长度比较完成的。
//字符串的连接操作
Status Concat(HString &T, HString S1, HString S2)
{
int i = 0;
if(T.ch)
{
free(T.ch);
}
if(!(T.ch = (char*)malloc((S1.length + S2.length) * sizeof(char))))
{
exit(OVERFLOW);
}
for(i=0;i<S1.length;i++)
{
T.ch[i] = S1.ch[i];
}
T.length = S1.length + S2.length;
for(i=S1.length;i<T.length;i++)
{
T.ch[i] = S2.ch[i-S1.length];
}
return OK;
}
联结操作也很简单,分配所需空间,然后依次将字符赋值到合适的位置上即可。注意数组的下标的操作。
//定位获得一个子串
Status subString(HString &Sub,HString S, int pos, int len)
{
int i = 0;
if(pos<1 || pos>S.length || len <0 || len>S.length - pos + 1)
{
return ERROR;
}
if(Sub.ch)
{
free(Sub.ch);
}
//如果是空串
if(!len)
{
Sub.ch = NULL;
Sub.length = 0;
}
//如果非空串
else
{
Sub.ch = (char*)malloc(len*sizeof(char));
if(!Sub.ch)
{
exit(OVERFLOW);
}
for(i=pos-1;i<pos+len-1;i++) //i的起始值,循环控制条件的结束值,总共是len个数据。
{
Sub.ch[i-pos+1] = S.ch[i];
}
//子串的长度设置
Sub.length = len;
}
return OK;
}
获得子串的时候,要注意一些边界条件,防止用户输入参数有误带来的错误。
另外就是最后给子串赋值的循环操作,注意i的结束条件,注意子串数组下标的处理。
当然, 这都是C语言中的处理方法,按照一个一个的字符来进行处理。
3. 串的链式存储结构
和链表的存储结构类似,只不过结构中的数据元素是字符串。这个时候仍然存在问题,那就是该为数据元素分配多大的空间。当然,可以预先分配一个较大的空间。如下:
#define CHUNKSIZE 80
typedef struct Chunk{
char ch[CHUNKSIZE];
struck Chunk *next;
}
也可以只定义一个字符串指针:
typedef struct Chunk{
string * ch;
struck Chunk *next;
}
这个时候需要注意的是,每次需要为将要存入节点的字符串动态分配一个空间,然后让指针域ch只向这个存储空间。
当然,以上只是定义了节点的数据类型,在实际应用中,需要定义串的结构:
typedef struct{
Chunk *head, *tail;
int curlen; //字符串的长度
}
设尾指针的作用是方便进行连个链表的联结,但是要注意处理串尾的无效字符。