字符串在面试中也是经常考查的内容,C库字符串的操作函数是常考内容(例如 strstr(),strtok(),strcpy(),strcmp()等),所以,平时我们不仅要学会使用字符串的操作,也要对其具体的实现方式有所了解,特别是字符串的字符串处理函数。下面我们通过分析几道典型的例题进行学习和交流。
1.从字符串中删除给定的某个字符或者几个字符(要求尽量高效而且不增加额外的存储)
思路:如果没有后面的限制条件,题目会很简单,要做到不增加额外的存储,其实我们可以直接对源字符数组进行操作,这样也能达到不用移动字符的效果,代码如下:
//delete char
void DeleteChar(char str[],char c)
{
assert(str!=NULL);
int iDest=0,iSrc=0;
cout<<"Delete char o is:";
while(str[iSrc]!='\0')
{
if(str[iSrc]!=c)
{
str[iDest] = str[iSrc]; //str的形式要以字符数组形式给出,
//如果以字符指针形式给出则此时无法修改源字符串内容
cout<<str[iDest];
iDest++;
}
iSrc++;
}
cout<<endl;
}
结果如下
但是如果是要删除是一个字符数组中的几个字符呢?其实此时设置一个标记数组就能解决问题,代码如下:
//delete char array
void DeleteCharArr(char str[],char chrs[],int n)
{
assert(str!=NULL);
char temp[256] = {0};
for(int i = 0;i<n;i++)
temp[chrs[i]] = 1;
int iDest = 0,iSrc = 0;
cout<<"Delete chrs 'o' and 'l' is:";
while(str[iSrc]!='\0')
{
if(!temp[str[iSrc]])
{
str[iDest] = str[iSrc]; //str的形式要以字符数组形式给出,
//如果以字符指针形式给出则此时无法修改源字符串内容
cout<<str[iDest];
iDest++;
}
iSrc++;
}
cout<<endl;
}
结果如下:
2.识别出字符串中的单词个数
思路:英文单词中字母通过空格来区分的,需要设置两个指针 一个pBegin指向单词的首地址;一个pEnd指向单词的为地址,识别完后再重置pBegin和pEnd即可。代码如下:
//word number in the sentence
int WordsNum(char *str)
{
char *pBegin,*pEnd;
int num=0;
//if(str==NULL) return 0;
assert(str!=NULL);
while(*str!='\0')
{
pBegin = str;
while(*pBegin!=' ' && *pBegin!='\0')//word' 'word' 'word
pBegin++;
if(*pBegin=='\0')//the last word
{
num++;
break;
}
num++;
//cout<<*(pBegin+1)<<endl;
pEnd = pBegin+1;
str = pEnd;
}
cout<<num<<endl;
return num;
3.逆转字符串
思路:一可以当作字符数组处理,利用循环语句实现前后字符的交换;二可以利用递归的方式
//reverse the str
void ReverseString(char *str)
{
int n;
char ctemp;
n = strlen(str);//str[] = "Hello world";最后以'\0'结束
for(int i = 0;i<n/2;i++)
{
ctemp = str[i];
str[i] = str[n-i-1];//<i,n-i-1>
str[n-i-1] = ctemp;
}
cout<<str<<endl;
}
4.实现内存拷贝memcpy()
思路:这时候一定要拷贝过程中出现内存重叠的可能性,不然就导致拷贝的数据的不一致,正常情况下把源字符串从头到尾一个一个拷贝到指定的目的字符串位置,需要考虑到给定的目的字符串的地址是否也覆盖了一部分源字符串,这是如果整箱拷贝后面的数据肯定会被污染,此时应该采用逆向拷贝的方式解决这个问题。下面是重叠的示意图,
代码如下:
//memcpy
void memcpy(void *pDest,const void *pSrc,int size)
{
assert(pDest!=NULL);
assert(pSrc!=NULL);
char *pStrSrc,*pStrDest;
//pDest与pSrc共享同一块内存区域的时候采用逆向拷贝的方式避免原始数据被覆盖
if(pDest<pSrc &&((char*)pDest+size)>pDest)
{//(char *)字符单元
pStrSrc = (char*)pSrc+size-1;
pStrDest = (char*)pDest+size-1;
while(size--)
*pStrDest-- = *pStrSrc--;
}
else
{//正常的拷贝方式
pStrSrc = (char*)pSrc;
pStrDest = (char*)pDest;
while(size--)
*pStrDest++ = *pStrSrc++;
}
}
5.Ip字符串与32位整数之间的转化
思路:ip字符串中每个数值之间是以‘.’分割的,提取其中的数值可以采用题目2中的方式,用两个指针表示一个值的起始和结束位置,然后提取进行移位累加。代码如下:
//ipstring to int_32
int IpToString(char *ip)
{
char *pBegin,*pEnd;
int shift = 24;
long unsigned resultSum = 0,result=0;
pBegin=pEnd=ip;//两个指针分别指向一个ip段的开始和结束
assert(ip!=NULL);//assert
while(*pEnd!='\0')
{//'.' 区分ip段,就像空格区分单词一样
while(*pEnd!='.' && *pEnd!='\0')
pEnd++;
int temp = 0;
while(pBegin<pEnd)
{//string to int
temp = temp*10+(*pBegin-'0');
pBegin++;
}
cout<<temp<<"<<"<<shift<<"==";
result=(temp<<shift);
cout<<result<<endl;
resultSum+=result;
shift = shift-8;
if(*pEnd='\0')
break;
pBegin++;
pEnd = pBegin;
}
cout<<"resultSum == "<<resultSum<<endl;
return resultSum;
}
结果如下:
但如果是32整数转换为ip地址呢,只需要获取每个字节并做出相应的转换即可,代码如下:
//int_32 to ip
char *Int32ToIp(long unsigned num)
{ //typedef unsigned char Uchar;
cout<<num<<"-->>";
printf("%u.%u.%u.%.u\n",
(Uchar)*((char*)&num+3),//最高位
(Uchar)*((char*)&num+2),
(Uchar)*((char*)&num+1),
(Uchar)*((char*)&num+0)//最低位
);
}
结果如下:
6.将一组字符串排序(str[][] = {"Ba","Ac","aB","dA"...}==>{"Ac","Ba","aB","dA"})
思路:完全可以想过利用冒泡排序的思想,只不过平时针对的是数值,现在是字符串,代码如下:
//sort a string arrry
void SortString(char *str[],int n)
{//char *str[4] = {"Ba","Ac","aB","dA"};
char *ptemp = NULL;
for(int i = 0;i<n;i++)
{
for(int j=n-1;j>i;j--)
{
if(strcmp(str[j],str[j-1])<0)
{
ptemp = str[j];
str[j] = str[j-1];
str[j-1] = ptemp;
}
}
}
}
结果如下:
继续学习和加油~~…………