题目
使程序先读取一段列标号,该列标号成对出现,表示输入行的列范围。这串列标号以一个负值结尾,作为结束标志。剩余输入行被程序读入并打印,然后输入行中被选中范围的字符串被提取出来并打印。
源代码
#include <stdio.h>
#include <stdlib.h> /*定义EXIT_SUCCESSandEXIT_FAILURE符号*/
#include <string.h>
#define MAX_COLS 20 /*最大列号*/
#define MAX_INPUT 1000 /*输入的最大长度*/
/*提前声明自定义函数*/
int read_column_numbers( int columns[], int max );
void rearrange( char *output, char const *input,int n_columns, int const columns[]);
/*主函数*/
int main(void) /*不接受参数的整型main*/
{
int n_columns; /*进行处理的列标号*/
int columns[MAX_COLS]; /*需要处理的列数,即输入的范围以及-1,成对出现*/
char input[MAX_INPUT]; /*容纳输入*/
char output[MAX_INPUT]; /*容纳输出*/
/*读取需要处理的列标号*/
n_columns=read_column_numbers(columns,MAX_COLS); /*调用read_column_numbers读取列号#26*/
while(gets(input)!=NULL){ /*循环打印,直到文章末尾*/
printf("Original input : %s\n", input );
rearrange( output, input, n_columns,columns ); /*处理输入行#46*/
printf("Rearranged line:%s\n", output );
}
return EXIT_SUCCESS;
}
/*读取范围列标号,超出规定范围不予理会*/
int read_column_numbers( int columns[], int max )
{
int num = 0; /*列标号*/
int ch; /*字符型为小整型数,用int定义防止将输入字符意外定义成EOF(整型),还保证了ch空间足够大*/
/*读取输入需要处理列标号即范围,限制条件*/
while(num<max&&scanf("%d",&columns[num]) ==1&&columns[num]>= 0 )
num+=1;
/*判断范围输入列数是否为双数*/
if(num%2!=0){
puts("Last column number is not paired."); /*打印错误信息*/
exit(EXIT_FAILURE); /*终止程序运行,向系统报错*/
}
/*丢弃该行中-1后面的所有字符清空缓冲区*/
while((ch = getchar()) != EOF && ch !='\n') /*读取字符为空或换行符时结束循环*/
;
return num;
}
/*处理输入行,将指定列的字符连接*/
void rearrange (char *output, char const *input,int n_columns, int const columns[]) /*const防止实参中的值被改动*/
{
int col; /*范围数组的下标*/
int output_col; /*输出列的计数器*/
int len; /*输入行长度*/
len = strlen(input); /*获得输入字符串长度*/
output_col = 0; /*输出列计数器初始为0*/
/*处理每对列标号*/
for(col=0;col<n_columns;col+=2){
int nchars =columns[col+1]-columns[col]+1; /*每对列标号之间的空间*/
/*当输入行结束或输出行数组已满时,结束任务*/
if(columns[col]>= len||output_col == MAX_INPUT-1)
break;
/*判断输出行数据空间是否足够*/
if(output_col+nchars>MAX_INPUT-1)
nchars=MAX_INPUT-output_col-1;
/*复制可容纳的相关数据*/
strncpy(output+output_col,input+columns[col],nchars);
output_col+=nchars;
}
output[output_col]='\0';
}
问题代码 while((ch = getchar()) != EOF && ch !=’\n’)
int ch; /*字符型就是小整型数,用int定义防止将输入字符意外定义成EOF(整型),还保证了ch空间足够大*/
while((ch = getchar()) != EOF && ch !='\n') /*读取字符为空或换行符时结束循环*/
;
过一遍程序执行过程:
当输入流输入范围列标号如:2 9 12 20 -1并回车后,缓存区便存在 2 9 12 20 -1 ‘\n’ 这6个字符。其中包括-1的5个数字都被scanf函数读取,于是缓存区便留下了’\n’这一个字符,而这个字符便处于待读取状态,如果没有进行清空操作(即去掉while((ch = getchar()) != EOF && ch !=’\n’))该字符就会被下一个gets(input)函数识别,执行时便出现这样的结果:
即按下第一个回车后直接执行到打印结果,输入输出显示都为空。
为了更直观的表现我们在命令窗口输入的范围结尾再加上几个数字,即输入:4 9 12 20 -1 2333 然后回车,这时候缓冲区就有了4 9 12 20 -1 2333 ‘\n’ 这7个字符,而scanf函数仍然读取包括-1前的5个字符,后面的两个字符仍然在缓存区待读取。此时回车后gets(input)就会直接读取剩下的字符:
显然这并不是我们想要的效果,我们要去除-1后任何因为手抖或其他干扰因数造成的无用字符,以及用于scanf识别的’\n’。
于是就需要一个“垃圾桶”来装这些“垃圾”使gets(input)只读到我们想要处理的一串字符。
而在这一个程序里的“垃圾桶”就是变量ch,清空操作就是getchar读取这些无用字符放到ch里,直到’\n’被ch读取后ch!=’\n’的值就为0,跳出循环,此时“地板”上就干干净净的,而gets(input)没的“吃”只能等你再“投食”他才做出反应,而得到的结果也没了干扰。
同样的输入,我们加上while((ch = getchar()) != EOF && ch !=‘\n‘)后执行的结果:
此时在第一个回车后程序并不会打印结果,说明缓冲区没有供gets(input)识别的’\n’,而输入新字符串后回车的结果也没有2333的干扰,因为2333和第一个’\n’已被ch依次读取了。