THE C programming language
练习1-22
编写 一个程序,把较长的输入行“折”成短一些的两行或多行,折行的位置在输入行的第n列之前的最后一个非空格之后。要保证程序能够智能地处理输入行很长以及在指定的列前没有空格或制表符的情况。
解题思路:
1.如何存放输入的内容 本文采用数组
2.抓题目要点。第一个是输入的字符。可以分为‘\t’,’\n’和其他字符。
第二个是位置。位置是否超过限制。如果没超过,继续将输入内容存入到数组中。如果超过MAXCOL的话,首先要找到最后一个非空格符之后的位置。接着打印之前的内容,最后超出MAXCOL的部分换行打印。
#define MAXCOL 10
#define TABINS 8
//声明
char line[MAXCOL];
int exptab(int pos);
void printl(int pos);
int findblank(int pos);
int newpos(int pos);
int main()
{
int c, pos;
pos = 0;
while ((c=getchar())!=EOF)
{
line[pos] = c;//将输入的内容存储到数组line中,pos表示数组line的下标
if (c == '\t')
{
pos = exptab(c);
}
else if (c == '\n')//遇到换行符后
//打印其前面的内容,并换行。重置pos变量
{
printl(pos);
pos = 0;
}
//对于其他字符,首先让pos自增1,然后与MAXCOL比较
//当它大于MAXCOL时,执行下面的语句
else if(++pos>MAXCOL)
{
pos = findblank(pos);
printl(pos);
pos = newpos(pos);
}
}
return 0;
}
int exptab(int pos)
{
line[pos] = ' ';//至少会输出一个空格符
for (++pos; pos < MAXCOL&&pos%TABINS != 0; ++pos)
{//循环可执行的条件是不发生折行的同时,要满足所在位置pos不在制表符终止位的整数倍上
line[pos] = ' ';
}
if (pos < MAXCOL)//扩展过后的位置还没有到折行的位置,这说明还有空余的空间
//需要返回其位置
{
return pos;
}
else
{//若超出应该折行的位置,则需要打印之前的内容,换行并重置位置
printl(pos);
return 0;
}
}
void printl(int pos)//打印0到pos-1之间的字符。pos=0时不用调用该函数
{
int i;
for (i=0;i<pos;i++)
{
putchar(line[i]);
}
if (pos > 0)
{
putchar('\n');
}
}
//查找MAXCOL前的最后一个非空格
int findblank(int pos)
{
if (pos >= 0 || line[pos] != ' ')
{
pos--;
}//只有两种情况,一种是没找到空格,一种是找到了
if (pos == 0) return MAXCOL;
else return pos + 1;//返回空格之后的位置,并将其视为折行位置
}
//将MAXCOL的内容换行输入
int newpos(int pos)
{
int i, j;
if (pos <= 0 || pos >= MAXCOL)//pos=MAXCOL不用换行找新位置
//pos > MAXCOL时,由于函数printl每输出一个字符串后都会自动换行
//所以MAXCOL之后的字符串会换行后输入,所以只需要重置pos值即可
{
return 0;
}
else
{
i = 0;
for (j = pos; j < MAXCOL; j++)
{
line[i] = line[j];
i++;
}
return i;//返回新的位置
}
}
练习2-4
重新编写函数squeeze(s1,s2),将字符串s1中任何与字符串s2中的字符匹配的字符都删除。
错误代码:
出现的错误:
- 没有充分理解函数参数。被调用的squeeze函数中的局部(临时)变量new占用的空间在调用结束时会被销毁,所以其内容无法保留。在主函数中输出的结果为销毁后的“乱码”结果。
- 没有理解数组名是地址。s3代表数组的首地址,是一个常量,所以不能够通过赋值操作,对其进行修改。所以s3不是一个可修改的左值。
char* squeeze(char* str1, char* str2)
{
int i, j, k;
char new[MaxSize] = {0};
j = k = 0;
for (j = 0; str2[j] != '\0'; j++)
{
for (i = 0; str1[i] != '\0' && str1[i] != str2[j]; i++)//如果循环执行到字符串str1末尾,就没必要再比较两个字符串的字符
;
if (str1[i] == '\0')
{
new[k] = str2[i];
k++;
}
}
new[k] = '\0';
return new;
}
int main()
{
char s1[MaxSize];
char s2[MaxSize];
char s3[MaxSize];
scanf("%s %s", s1,s2);
s3=squeeze(s1, s2);
printf("%s", s3);
return 0;
}
正解:
void squeeze(char* str1, char* str2)
{//题目要求将str1中任何与str2中的字符相匹配的字符都删除,所以最终是要返回字符串str1
//此处str2起到排除掉str1中不符合要求的字符
int i, j, k;//i表示字符串str2的下标,j表示未处理的字符串str1的下标,k表示处理过后str1的下标
for (j = k = 0; str1[j] != '\0'; j++)
{//具体过程就是看str1中每个字符是否在str2中出现过,如果出现过就排除,而没出现过的字符放回str1中并覆盖原先的内容
//最终将更新的字符串str1输出
for (i = 0; str2[i] != '\0' && str2[i] != str1[j]; i++)//如果循环执行到字符串str2末尾,就没必要再比较两个字符串的字符
;
if (str2[i] == '\0')//表示在str2中没有找到与str1中的“目标字符”相匹配的字符
{
str1[k] = str1[j];//用“目标字符”将原先的内容覆盖掉
k++;
}
}
str1[k] = '\0'; //处理过后str1需要一个结束标志
}
int main()
{
char s1[MaxSize];
char s2[MaxSize];
scanf("%s %s", s1,s2);
squeeze(s1, s2);
printf("%s", s1);//将处理过后的s1输出
return 0;
}