题目:
4.
从标准输入读取文本并对其进行修改,然后把它写到标准输出。
首先读取一串列标号,这些列标号成对出现,表示输入行的列范围。这串列标号以一个负值结尾,作为结束标志。剩余的输入行被程序读入并打印,然后输入行中被选中范围的字符串被提取出来并打印。(每行第一列的列标号为0)
程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_CLOS 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[MAX_CLOS]);
int main(void)
{
int n_columns; //进行处理的列标号
int columns[MAX_CLOS]; //需要处理的列数
char input[MAX_INPUT]; //需要容纳的输入的数组
char output[MAX_INPUT]; //容纳输出行的数组
n_columns=read_column_numbers(columns,MAX_CLOS);
while(gets(input)!=NULL) //如果读入不存在输入行则程序结束
{
printf("Original input :%s\n",input);
rearrange(output,input,n_columns,columns);
printf("Rearranged line :%s\n",output);
}
return EXIT_SUCCESS;
}
//读取列标号,若超出规定范围则不予理会
int read_column_numbers(int columns[],int max)
{
int num=0;
int ch;
//取得列标号,若所读取的数小于0则停止
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);
}
while((ch=getchar())!=EOF&&ch!='\n'); //丢弃该行中包含最后一个数字的那部分内容
return num;
}
//处理输入行,将指定字符连接在一起,输出行以NUL结尾
void rearrange(char *output,char const*input,int n_columns,int const columns[])
{
int col; //columns数组的下标
int output_col; //输出行的列计数器
int len; //输入行的长度
len=strlen(input);
output_col=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';
}
//设input为数组1 output 为数组2
//if(columns[col]>=len)用来处理范围在数组1的长度外,且在数组2的长度内、部分在数组2的长度中、在数组2的长度外
//if(output_col==MAX_INPUT-1) 用来处理范围在数组1长度内且在数组2长度外;范围部分在数组1中且在数组2的长度外
// if(output_col+nchars>MAX_INPUT-1)用来处理范围在数组1的长度内且部分在数组2中;范围部分在数组1中且部分在数组2中
//范围在数组1的长度内且在数组2的长度内、范围部分在数组1中且在数组2内,则不需要处理。
运行结果:
注: if语句后面不能加分号,否则当条件为真时执行空语句。
rearrange函数中的各种条件语句主要是为了处理 “复制范围” 和 “输入数组 以及 “输出数组” 的关系。
1> ”范围“和”一个数组的长度“有三种关系:①范围在长度内;②范围部分在长度内;③范围在长度外;例如数组长度为10,那么范围[4,9]在长度内,[6, 10]部分在范围内;[12, 14]在范围外。 这里的范围是闭区间,例如[4,9],范围长度为9-4+1=6列。
2> 同理,”范围“和”两个数组的长度“的对应关系应有9种情况。
3> rearrange函数中的范围为columns[col]到 columns[col+1];两个数组分别为input和output数组。
5.
rearrange程序中的下列语句
if(columns[col]>=len...)
break;
当字符的列范围超出输入行的末尾时就停止复制。这条语句只有当列范围以递增顺序出现时才是正确的,但事实上并不一定如此。请修改这条语句,即使列范围不是按顺序读取时也能正确完成任务。
程序如下:
void rearrange(char *output,char const*input,int n_columns,int const columns[])
{
int col; //columns数组的下标
int output_col; //输出行的列计数器
int len; //输入行的长度
len=strlen(input);
output_col=0;
//处理每对列号
for(col=0;col<n_columns;col+=2)
{
int nchars=columns[col+1]- columns[col]+1;
if(columns[col]>=len)
continue;
//如果输入行的标号小于需要处理的列标号或者输出数组已满结束任务
if(output_col==MAX_INPUT-1)
break;
//如果输出行数据空间不够则只复制可以容纳的数据
if(output_col+nchars>MAX_INPUT-1)
nchars=MAX_INPUT-output_col-1;
if(columns[col]+nchars-1>=len)
nchars=len-columns[col];
//复制相关数据
strncpy(output+output_col,input+columns[col],nchars);
output_col+=nchars;
}
output[output_col]='\0';
}
运行结果:
6.
修改rearrange程序,去除输入中列标号的个数必须是偶数的限制。如果读入的列标号为奇数个,函数就会把最后一个列范围设置为最后一个列标号所指定的列到行尾之间的范围。从最后一个列标号直至行位的所有字符都将被复制到输出字符串。
程序如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_CLOS 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[MAX_CLOS]);
int main(void)
{
int n_columns; //进行处理的列标号
int columns[MAX_CLOS]; //需要处理的列数
char input[MAX_INPUT]; //需要容纳的输入的数组
char output[MAX_INPUT]; //容纳输出行的数组
n_columns=read_column_numbers(columns,MAX_CLOS);
while(gets(input)!=NULL) //如果读入不存在输入行则程序结束
{
printf("Original input :%s\n",input);
rearrange(output,input,n_columns,columns);
printf("Rearranged line :%s\n",output);
}
return EXIT_SUCCESS;
}
//读取列标号,若超出规定范围则不予理会
int read_column_numbers(int columns[],int max)
{
int num=0;
int ch;
//取得列标号,若所读取的数小于0则停止
while(num<max&&scanf("%d",&columns[num])==1 && columns[num]>=0)
num +=1;
//将原来这里为判断奇偶的语句删掉
while((ch=getchar())!=EOF&&ch!='\n');
//丢弃该行中包含最后一个数字的那部分内容
return num;
}
//处理输入行,将指定字符连接在一起,输出行以NUL结尾
void rearrange(char *output,char const*input,int n_columns,int const columns[])
{
int col; //columns数组的下标
int output_col; //输出行的列计数器
int len; //输入行的长度
len=strlen(input);
output_col=0;
for(col=0;col<n_columns;col+=2)
{
int nchars;
if(col+1<n_columns)
{nchars=columns[col+1]-columns[col]+1;
} else nchars=len;
//如果输入行的标号小于需要处理的列标号或者输出数组已满结束任务
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';
}
运行结果:
注: 因输入的确定范围是奇数时,是将最后一个范围扩展到input的结束位置,所以这里仍然需要按递增的顺序输入。
成败往往隐藏在那些不起眼的小细节中。