- 修改程序清单13.1中的程序,要求提示用户输入文件名,并读取用户输入的信息,不使用命令行参数
/* count.c -- using standard I/O */
#include <stdio.h>
#include <stdlib.h> // exit() prototype
int main( )
{
int ch; // place to store each character as read
FILE *fp; // "file pointer"
unsigned long count = 0;
char file_name[100];
printf("Input your filename:\n");
fscanf(stdin,"%s",file_name);
if ((fp = fopen(file_name, "r")) == NULL)
{
printf("Can't open %s\n", file_name);
exit(EXIT_FAILURE);
}
while ((ch = getc(fp)) != EOF)
{
putc(ch,stdout); // same as putchar(ch);
count++;
}
fclose(fp);
printf("\nFile %s has %lu characters\n", file_name, count);
return 0;
}
- 编写一个文件拷贝程序,该程序通过命令行获取原始文件名和拷贝文件名。尽量使用标准I/O和二进制模式。
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[] )
{
FILE *fp_source;
FILE *fp_target;
int valid_n;
char temp_array[40];
if(argc!=3) //command line input error
{
fprintf(stderr,"Usage:[%s] [source_file] [target_file]\n",argv[0]);
exit(EXIT_FAILURE);
}
else
{
if( ( fp_source=fopen(argv[1],"rb") )==NULL )//打开source file
{
fprintf(stderr,"Can't open source file:%s\n",argv[1]);
exit(EXIT_FAILURE);
}
if ( ( fp_target=fopen(argv[2],"wb") )==NULL ){ //打开target file
fprintf(stderr,"Can't open source file:%s\n",argv[2]);
exit(EXIT_FAILURE);
}
while( ( valid_n = fread(temp_array, sizeof(char), 20, fp_source) ) != 0 ){
//只要fread读取到有效个字符就copy到target_file
//此处要用valid_n接受有效个数提示给fwrite写入对应的二进制数量
if (ferror(fp_source)!=0 ){
fprintf(stderr,"Error in reading file:%s\n",argv[1]);
exit(EXIT_FAILURE);
}
fwrite(temp_array, sizeof(char), valid_n, fp_target );//写入target file
if(ferror(fp_target)!=0 ){
fprintf(stderr,"Error in reading file:%s\n",argv[1]);
exit(EXIT_FAILURE);
}
}
} // copy done
fclose(fp_source);
fclose(fp_target);
return 0;
}
source文件输入的是字符串时,依然会在target文件中得到同样的内容,fwrite只是将内容以二进制包的形式传送。
3.编写一个文件拷贝程序,提示用户输入文本文件名,并以该文件名作为原始文件名和输出文件名。该程序要使用 ctype.h 中的 toupper()函数,在 写入到输出文件时把所有文本转换成大写。使用标准I/O和文本模式。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main()
{
char file_name[40];
FILE *fp;
FILE *fp_temp;
char ch;
long count,end;
fprintf(stdout,"Input your file name:\n");
fscanf(stdin,"%s",file_name);
if( (fp=fopen(file_name,"r") )==NULL ) // read source file
{
fprintf(stderr,"Can't open file:%s",file_name);
exit(EXIT_FAILURE);
}
if( ( fp_temp=fopen("temp.txt","w") )==NULL ) // write temp file
{ fprintf(stderr,"error!");
exit(EXIT_FAILURE);
}
// show original contents
printf("Originally,the contents:\n ");
while( ( ch=getc(fp) ) != EOF )
{ putchar(ch); //output: stdout
if( islower(ch) )
{ ch=toupper(ch); }
putc(ch,fp_temp); //output: temp file
}
printf("\n");
fclose(fp); //close file
fclose(fp_temp);
if( ( fp=fopen(file_name,"w") ) == NULL ) //write source file
{
fprintf(stderr,"Writting %s error",file_name);
exit(EXIT_FAILURE);
}
if( ( fp_temp=fopen("temp.txt","r") ) == NULL ) //read temp file
{
fprintf(stderr,"Reading modified data error");
exit(EXIT_FAILURE);
}
while( ( ch=getc(fp_temp) ) != EOF ){ // copy data back
putc(ch,fp);
}
// show new contents
fclose(fp);
fclose(fp_temp);
fp=fopen(file_name,"r");
printf("Now,contents refresh:\n ");
while( ( ch=getc(fp) ) != EOF ){
putchar(ch);
}
fclose(fp);
remove("temp.txt");//根据文件名打开的文件就根据文件名删除
return 0;
}
问题
- 把字符从文件中用getc()提取出来, 处理为大写字母后,再ungetc()传回stream的思路做不出来。
- 使用w+模式,将temp文件写回source文件后,如果想输出验证source文件修改后的内容,直接通过rewind(fp)回到文件位置起点再循环putchar(ch)的思路无法实现。需要关闭文件,重新打开才能将内容输出出来。
- 关闭文件后,应该是同时关闭了缓冲区?下次打开会自动回到文件起点SEEK_SET
- 如果不先关闭temp文件,remove函数好像不能删除temp文件?
4.编写一个程序,按顺序在屏幕上显示命令行中列出的所有文件。使用 argc控制循环。
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
if (argc==1)
{fprintf(stderr,"Usage : %s [file_name]",argv[0]);
exit(EXIT_FAILURE);
}
else
{
int i=1;
FILE *fp;
char ch;
while( i <argc)
{
if( ( fp=fopen(argv[i],"r") )==NULL )
{
fprintf(stderr,"read file %d fail.\n",i);
continue;
}
else
{ printf("File %d:%s\n",i,argv[i]);
while( ( ch=getc(fp) ) != EOF )
putchar(ch);
putchar('\n');
}
fclose(fp);
putchar('\n');
i++;
}
}
return 0;
}
5.修改程序清单13.5中的程序,用命令行界面代替交互式界面。
/* append.c -- appends files to a file */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFSIZE 4096
#define SLEN 81
void append(FILE *source, FILE *dest);
char * s_gets(char * st, int n);
int main(int argc, char *argv[])
{
FILE *fa, *fs; // fa for append file, fs for source file
int files = 0; // number of files appended
int ch;
if ((fa = fopen(argv[1], "a+")) == NULL)
//argv[1]是用来拼接存入source file的target file
{
fprintf(stderr, "Can't open %s\n", argv[1]);
exit(EXIT_FAILURE);
}
if (setvbuf(fa, NULL, _IOFBF, BUFSIZE) != 0)
{
fputs("Can't create output buffer\n", stderr);
exit(EXIT_FAILURE);
}
// argv[2]及后面的为source文件名
for (int i=2;i<argc;i++)
{
if (strcmp(argv[i], argv[1]) == 0)
fputs("Can't append file to itself\n",stderr);
else if ((fs = fopen(argv[i], "r")) == NULL)
fprintf(stderr, "Can't open %s\n", argv[i]);
else//打开source file成功
{
if (setvbuf(fs, NULL, _IOFBF, BUFSIZE) != 0)
{
fputs("Can't create input buffer\n",stderr);
continue;
}
//创建缓冲区后进行拼接
append(fs, fa);
if (ferror(fs) != 0)
fprintf(stderr,"Error in reading file %s.\n",
argv[i]);
if (ferror(fa) != 0)
fprintf(stderr,"Error in writing file %s.\n",
argv[1]);
fclose(fs);
files++;
}
}
printf("Done appending. %d files appended.\n", files);
rewind(fa);
printf("%s contents:\n ", argv[1]);
while ((ch = getc(fa)) != EOF)
putchar(ch);
puts("\nDone displaying.");
fclose(fa);
return 0;
}
void append(FILE *source, FILE *dest)
{
size_t bytes;
static char temp[BUFSIZE]; // allocate once
while ((bytes = fread(temp,sizeof(char),BUFSIZE,source)) > 0)
fwrite(temp, sizeof (char), bytes, dest);
}
6.使用命令行参数的程序依赖于用户的内存如何正确地使用它们。重写程序清单 13.2 中的程序,不使用命令行参数,而是提示用户输入所需信息。
// reducto.c -- reduces your files by two-thirds!
#include <stdio.h>
#include <stdlib.h> // for exit()
#include <string.h> // for strcpy(), strcat()
#define LEN 50
int main()
{
FILE *in, *out; // declare two FILE pointers
int ch;
char out_name[LEN]; // storage for output filename
char input_file[LEN];
int count = 0;
printf("Specify a file to compress:\n");
fscanf(stdin,"%s",input_file);
// set up input
if ((in = fopen(input_file, "r")) == NULL)
{
fprintf(stderr, "I couldn't open the file \"%s\"\n",
input_file);
exit(EXIT_FAILURE);
}
// set up output
strncpy(out_name,input_file, LEN - 5); // copy filename
out_name[LEN - 5] = '\0';
strcat(out_name,".red"); // append .red
if ((out = fopen(out_name, "w")) == NULL)
{ // open file for writing
fprintf(stderr,"Can't create output file.\n");
exit(3);
}
// copy data
while ((ch = getc(in)) != EOF)
if (count++ % 3 == 0)
putc(ch, out); // print every 3rd char
// clean up
if (fclose(in) != 0 || fclose(out) != 0)
fprintf(stderr,"Error in closing files\n");
return 0;
}
- 编写一个程序打开两个文件。可以使用命令行参数或提示用户输入文件名:
a.该程序以这样的顺序打印:打印第1个文件的第1行,第2个文件的第1 行,第1个文件的第2行,第2个文件的第2行,以此类推,打印到行数较多文件的最后一行。
b.修改该程序,把行号相同的行打印成一行。
#include <stdio.h>
#include <stdlib.h>
void show_lineA( FILE *fp1, FILE *fp2);
void show_lineB( FILE *fp1,FILE *fp2);
int main(int argc,char *argv[]){
FILE *fp1;
FILE *fp2;
if ( argc!=3 ) //command line argument error
{ fprintf(stderr,"Usage:%s [file_1] [file_2]",argv[0]);
exit(EXIT_FAILURE);
}
if( ( fp1=fopen(argv[1],"r") )==NULL ) //open file 1
{fprintf(stderr,"Can't open file %s",argv[1]);
exit(EXIT_FAILURE);
}
if( ( fp2=fopen(argv[2],"r") )==NULL )//open file 2
{fprintf(stderr,"Can't open file %s",argv[2]);
exit(EXIT_FAILURE);
}
show_lineA(fp1,fp2);
rewind(fp1); rewind(fp2);
show_lineB(fp1,fp2);
fclose(fp1); fclose(fp2);
return 0;
}
void show_lineA( FILE *fp1,FILE *fp2){
printf("show line in requirement of A:\n");
char ch1,ch2;
while( feof(fp1)==0 || feof(fp2)==0){
//循环打印,file1打一行file2打一行
//直到把两个文件都打完
while( ( ch1=getc(fp1) ) !='\n' && ch1!=EOF )
putchar(ch1); //若file1打完,则此循环消失,file2同样
if(ch1=='\n')
putchar('\n');
while( ( ch2=getc(fp2) ) !='\n' && ch2!=EOF )
putchar(ch2);
if(ch2=='\n')
putchar('\n');
}
printf("\n");
}
void show_lineB( FILE *fp1,FILE *fp2){
printf("show line in requirement of B:\n");
char ch1,ch2;
while( ch1!=EOF || ch2!=EOF){
while( ( ch1=getc(fp1) ) !='\n' && ch1!=EOF )
putchar(ch1);
while( ( ch2=getc(fp2) ) !='\n' && ch2!=EOF )
putchar(ch2);
if(ch1!=EOF || ch2!=EOF ) //只在file1,file2都打印完一行后才换行
putchar('\n'); //若file a已经打完,则忽略它,一行行打印file b
}
printf("\n");
}
- 编写一个程序,以一个字符和任意文件名作为命令行参数。如果字符后面没有参数,该程序读取标准输入;否则,程序依次打开每个文件并报告每个文件中该字符出现的次数。文件名和字符本身也要一同报告。程序应包含错误检查,以确定参数数量是否正确和是否能打开文件。如果无法打开文件,程序应报告这一情况,然后继续处理下一个文件。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int tell_ch(char ch,FILE *fp);
int main(int argc, char *argv[])
{
char ch_string[2];
strcpy(ch_string,argv[1]);
char ch=ch_string[0];
FILE *fp;
int counter;
if(argc==1)
{ fprintf(stderr,"Usage:xxx.exe ch [file 1] [file 2] ...");
exit(EXIT_FAILURE);
}
else if(argc==2)
{
printf("Input:\n");
counter= tell_ch(ch,stdin);
fprintf(stdout,"%d %c found in your input.\n",counter,ch);
}
else
{
for (int i=2;i<argc;i++)
{
if( ( fp=fopen(argv[i],"r") ) == NULL )
{
fprintf(stderr,"Open file %d: \"%s\" filed\n",i-1);
continue;
}
counter=tell_ch(ch,fp);
printf("%d %c found in file %d: \"%s\"\n",counter,ch,i-1,argv[i]);
if (fclose(fp)!=0 )
fprintf(stderr,"Fail to close file %d",i-1);
}
}
return 0;
}
int tell_ch(char ch,FILE *fp)
{
int count=0;
char charac;
charac=getc(fp);
while ( charac !=EOF )
{
if (charac==ch)
count++;
charac=getc(fp);
}
return count;
}
- 修改程序清单 13.3 中的程序,从 1 开始,根据加入列表的顺序为每个单词编号。当程序下次运行时,确保新的单词编号接着上次的编号开始。
/* addaword.c -- uses fprintf(), fscanf(), and rewind() */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 50
int main(void)
{
FILE *fp;
char words[MAX];
int word_count=0;
if ((fp = fopen("wordy.txt", "a+")) == NULL)
{
fprintf(stdout,"Can't open \"wordy.txt\" file.\n");
exit(EXIT_FAILURE);
} // open file
//要实现每次打开程序都能知道当前输入单词的序号,可以在开头扫一遍单词数量
rewind (fp);
while( fgets(words,MAX,fp)!=NULL ) //fgets遇结尾返回NULL
word_count++;
rewind (fp);
puts("Enter words to add to the file; press the #");
puts("key at the beginning of a line to terminate.");
// input words:
while ((fscanf(stdin,"%40s", words) == 1) && (words[0] != '#')) //输入存入words[],并判定结束符#
fprintf(fp, "%4d:%s\n",++word_count,words); //添加序号,成为新字符串,传递给file
puts("File contents:");
rewind(fp);
while (fscanf(fp,"%s",words) == 1)
puts(words);
puts("Done!");
if (fclose(fp) != 0)
fprintf(stderr,"Error closing file\n");
return 0;
}
- 编写一个程序打开一个文本文件,通过交互方式获得文件名。通过一个循环,提示用户输入一个文件位置。然后该程序打印从该位置开始到下 一个换行符之前的内容。用户输入负数或非数值字符可以结束输入循环。
#include <stdio.h>
#include <stdlib.h>
int main()
{
char file_name[40];
long index;
char ch;
FILE *fp;
printf("Please input a file_name:\n");
fscanf(stdin,"%s",file_name);
if( ( fp=fopen(file_name,"r") ) ==NULL )
{
fprintf(stderr,"Can't open file:%s",file_name);
exit(EXIT_FAILURE);
}
printf("Input a index which points to a file location, ");
printf("(Enter q or -1 to quit):\n");
while( scanf("%ld",&index)==1 &&index>0)
{
if ( fseek(fp,index,SEEK_SET)!=0 )
{
printf("move too far,try again:\n");
continue;
}
while( ( ch=getc(fp) ) !='\n' )
putchar(ch);
printf("\n");
printf("Enter a new index(q or -1 to quit ):\n");
} printf("Bye!\n");
fclose(fp);
return 0;
}
- 编写一个程序,接受两个命令行参数。第1个参数是一个字符串,第 2个参数是一个文件名。然后该程序查找该文件,打印文件中包含该字符串的所有行。因为该任务是面向行而不是面向字符的,所以要使用fgets()而不是getc()。使用标准C库函数strstr()在每一行中查找指定字符串。假设文件中的所有行都不超过255个字符。
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])//xxx.exe string filename
{
FILE *fp;
char aline[256];
if( ( fp=fopen(argv[2],"r") ) ==NULL )
{
fprintf(stderr,"Can't open file:\"%s\"",argv[2] );
exit(EXIT_FAILURE);
}
while ( fgets(aline,255,fp)!=NULL )
//fgets得到最后一行时不会提示结尾(返回NULL),获取下一行空行时才会返回NULL
{
if( strstr( aline,argv[1]) !=NULL )
fputs(aline,stdout);
}
if ( fclose(fp)!=0 )
fprintf(stderr,"Error in closing file");
return 0;
}
问题
怎么在命令行中,将多段而非一个单词传给argv ? 如:“What does”,
- 创建一个文本文件,内含20行,每行30个整数。这些整数都在0~9 之间,用空格分开。该文件是用数字表示一张图片,0~9表示逐渐增加的灰度。编写一个程序,把文件中的内容读入一个20×30的int数组中。
一种把这些数字转换为图片的粗略方法是:该程序使用数组中的值初始化一个20×31 的字符数组,用值0对应空格字符,1 对应点字符,以此类推。数字越大表示字符所占的空间越大。例如,用#表示9。每行的最后一个字符(第31个) 是空字符,这样该数组包含了20个字符串。最后,程序显示最终的图片 (即,打印所有的字符串),并将结果储存在文本文件中。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main()
{
int data[20][30];
FILE *fp;
char ch;
//打开data.txt
if( ( fp=fopen("data.txt","r") )==NULL )
{
fprintf(stderr,"Can't open data file.\n");
exit(EXIT_FAILURE);
}
//data写入int[20][30]数组
for(int r=0;r<20;r++)
{
for(int c=0;c<30;c++)
{
ch=getc(fp);
while( isdigit(ch)==0 )//取到char型数字为止
ch=getc(fp);
data[r][c]= ch-'0';
}
}
//data写入int数组完成,关闭data.txt,w模式打开 picture.txt
if ( fclose(fp)!=0 )
{printf("Error in closing file.\n");
exit(EXIT_FAILURE);
}
if( ( fp=fopen("picture.txt","w") )==NULL )
{
fprintf(stderr," Open data file again failed.\n");
exit(EXIT_FAILURE);
}
//int数组传入char数组[20][31]
char picture[20][31];
char code_digit[10]={' ' , '.' , '\'' ,
':' , '~' , '*' ,
'=' , '&' , '%' , '#'};//令人头痛
for(int r=0;r<20;r++)
{
for(int c=0;c<30;c++)
{
picture[r][c]= code_digit[ data[r][c] ];
}
picture[r][30]='\0';
}
// 打印字符串,同时将字符串打印到文件中中
for(int r=0;r<20;r++)
{
fputs(picture[r] ,stdout);
fputs(picture[r],fp);
fprintf(fp,"\n");
printf("\n");
}
//关闭picture文件
if ( fclose(fp)!=0 )
{printf("Error in closing file.\n");
exit(EXIT_FAILURE);
}
return 0;
}
- 用变长数组(VLA)代替标准数组,完成编程练习12。
#include <stdio.h>
#include <stdlib.h>
void file_to_int(int row,int col,int arr[row][col],FILE *fpf);
void int_to_char(int row,int column,char arr_char[row][column+1],int arr_int[row][column]);
void show_and_store(int row,int column,char arr_char[row][column+1],FILE *fp);
int main()
{
int row=20;
int column=30;
int data[row][column];
char picture[row][column+1];
FILE *fp;
fp=fopen("data.txt","r"); //打开data.txt
file_to_int(row,column,data,fp);//file_data写入int数组
fclose(fp);
fp=fopen("picture_out.txt","w"); //open输出文件准备打印
int_to_char(row,column,picture,data);//把int数组中的数字转换为符号写入char数组
show_and_store(row,column,picture,fp);//同时打印char数组all字符到屏幕和输出文件
fclose(fp);
return 0;
}
void file_to_int(int row,int col,int arr[row][col],FILE *fpf)
{
for(int r=0;r<row;r++)
{
for(int c=0;c<col;c++)
{
fscanf(fpf,"%d",&arr[r][c]);
/*stdin和fp是等效的,可以把从标准输入 键入的数字用scanf("%d")存进int型变量,同理
也可以把file中的数字字符用scanf(%d)变为int型数字,不需要用getc获取文件中的字符再转换为数字*/
}
}
}
void int_to_char(int row,int column,char arr_char[row][column+1],int arr_int[row][column])
{
char code_digit[10]={' ' , '.' , '\'' ,
':' , '~' , '*' ,
'=' , '&' , '%' , '#'};//密码本
for(int r=0;r<row;r++)
{
for(int c=0;c<column;c++)
{
arr_char[r][c]=code_digit[ arr_int[r][c] ];
} arr_char[r][column]='\0';
}
}
void show_and_store(int row,int column,char arr_char[row][column+1],FILE *fp)
{
for(int r=0;r<row;r++){
fputs(arr_char[r],stdout);
fputs(arr_char[r],fp);
fprintf(fp,"\n");
printf("\n");
}
}
14.数字图像,尤其是从宇宙飞船发回的数字图像,可能会包含一些失真。为编程练习12添加消除失真的函数。该函数把每个值与它上下左右相邻 的值作比较,如果该值与其周围相邻值的差都大于1,则用所有相邻值的平均值(四舍五入为整数)代替该值。注意,与边界上的点相邻的点少于4 个,所以做特殊处理。
注;在13题代码中添加此distortion函数及头文件声明,在file_to_int函数得到data[r][c]数组后,调用distortion函数即可。
void handle_distortion(int row,int column,int data[row][column])
{
//将data移植到extend_data中,上下左右添上一排0来处理边界元素
int extend_data[row+2][column+2];
// line 1 and line 32全为0
for(int cc=0;cc<column+2;cc++)
{
extend_data[0][cc]=0;
extend_data[row+1][cc]=0;
}
//copy data[20][30] ,fill extend数组 from line2 to line 31
for(int r=1;r<row+1;r++)
{
extend_data[r][0]=0; //每行第一个元素先置0
for(int c=1;c<column+1;c++)
{
extend_data[r][c]=data[r-1][c-1];
}
extend_data[r][column+1]=0;//每行最后一个元素置0
}
int get_int;
for(int r=1;r<row+1;r++) //对失真点取平均值
{
for(int c=1;c<column+1;c++)
{
get_int=extend_data[r][c];
if( abs( get_int - extend_data[r][c+1] )> 1 && abs( get_int - extend_data[r][c-1] )>1 &&
abs( get_int - extend_data[r-1][c] )> 1 && abs( get_int - extend_data[r+1][c] )>1 )
extend_data[r][c]= ( extend_data[r][c-1]+extend_data[r][c+1]+extend_data[r-1][c]+extend_data[r+1][c] )/4;
}
}
//处理完毕,返回data[20][30]数组
for(int r=1;r<row+1;r++) //从extend_data的line2 to line 31
{
for(int c=1;c<column+1;c++)
{
data[r-1][c-1] = extend_data[r][c] ;//data元素放入extend_data数组的中间
}
}
}
左上角噪点已消除