转载请注明来源 http://blog.csdn.net/imred/article/details/26058847
本人大一,正式接触C语言刚刚三个月,基础不牢,在写一道作业时走了许多弯路,也有许多收获,分享给大家。
作业题目如下“编写一个程序repl,它用命令行指定的字符串替换命令行指定文件中的单词。例如,命令行:repl file.txt you they 将用you替换file.txt文件中所有单词they。”
此时我们刚刚学到“文件的输入与输出”一章,老师说这章内容许多要自学,主要是stdio.h中的一些函数,我草草看了看,就开始胡乱的敲起了我的代码。
我首先想到的方法如下(哈哈,不要笑话我啊,原来真的没怎么学过C语言):我感觉创建临时文件的方法比较麻烦(其实并不麻烦),就想着在原文本文件中直接进行查找替换(自然不大可能),先用一个find函数查找要被替换的字符串所在位置,然后返回一个文件指针,再用一个repl函数完成后续的替换操作。这种方法的最终成品如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> //#define DEBUG FILE *find(FILE *fp,char *s,int m); /*m为被替换字符串字符数*/ FILE *repl(FILE *fp,char *s,int m,int n); /*m为被替换字符串字符数,n为替换字符串字符数*/ int main(int argc,char *argv[]) { if(argc!=4) { printf("Argument error!\n"); exit(-1); } FILE *fp,*tmpfp; if((fp=fopen(argv[1],"rb+"))==NULL) exit(-1); for(;;) { if((find(fp,tmpfp,argv[3],sizeof(argv[3])))==NULL) break; repl(fp,tmpfp,argv[2],sizeof(argv[3]),sizeof(argv[2])); } fclose(fp); return 0; } FILE *find(FILE *fp,char *s,int m) { char ch[m]; long i; for(i=1;feof(fp)==0;) { #ifdef DEBUG printf("find %d %d %#p\n",feof(fp),i,fp); #endif // DEBUG if(feof(fp)!=0) return NULL; fpos_t pos,*ptr=&pos; fgetpos(fp,ptr); fread(ch,1,m-1,fp); ch[m-1]=0; fsetpos(fp,ptr); if(strcmp(ch,s)==0) return fp; else fseek(fp,i++,SEEK_SET); if(fgetc(fp)==EOF) return NULL; } return NULL; } FILE *repl(FILE *fp,char *s,int m,int n) { #ifdef DEBUG printf("repl\n"); #endif // DEBUG char empty[1]="\b"; int i; fpos_t pos,*ptr=&pos; fgetpos(fp,ptr); for(i=1;i<=m-1;i++) { fwrite(empty,1,1,fp); fflush(fp); } fsetpos(fp,ptr); fputs(s,fp); fseek(fp,n-1,SEEK_CUR); return fp; }虽然这代码什么也干不了,但我为完成这个作业所用的四分之三的时间都用在了这个上面。弯路都走完了,就找着正路了。由于codeblocks貌似无法调试带参数的main函数(我自己尝试,Set Program's Arguments中的语句貌似只在程序运行中有效,和网上说的不太一样),我就用#ifdef与#endif之间的语句来大致了解程序运行的流程。
我走过以下几处弯路:
1、第15行我对文本文件的打开方式一开始是“r+”,是以文本模式打开的,这在使用fseek函数时就遇到了问题:文件读写指针无法偏移(一会你会知道,这是一个歪打正着的结论),《C Primer Plus》中有如下解释:在文本模式中,只要求保证如下这些调用有效:
fseek (file, 0L, SEEK_SET) | 到文件开始 |
fseek (file, 0L, SEEK_CUR) | 在当前位置不动 |
fseek (file, 0L, SEEK_END) | 到文件结尾 |
fseek (file, ftell-pos, SEEK_SET) | 到距文件开始处ftell-pos字节的位置,ftell-pos是ftell( )的返回值 |
3、认为用空字符覆盖字符相当于删除字符操作,事实上在用记事本打开文件时,空字符为一种不可显示字符,显示效果和空格一样(使用WinHex打开可以看出来,notepad++也能看出,会将空字符显示为“NULL”)。
4、认为在“rb+”打开方式下,fputs函数输出的内容不会覆盖后面的内容,只会插入,fwrite才会覆盖(真不知道当时怎么想的)。
在动过无数次小手术没有成功后,我决定必须得动大手术了,就用创建临时文件的方法,把代码完全重写了(我一开始想建个大字符数组把字符全部读进去来着,后来觉着行不通,万一有个上GiB的文本文件呢),成品代码(其实这不是最终结果,最终结果我是用“wb”方式打开临时文件的,代码也有所变化,主要是不用理会'\r'的问题了)如下:
#include <stdio.h> #include <stdlib.h> #include <string.h> FILE *copy(FILE *fp,FILE *tmpfp,char *s,int m); /*m为被替换字符串字符数*/ FILE *insert(FILE *fp,FILE *tmpfp,char *s,int n); /*m为被替换字符串字符数,n为替换字符串字符数*/ //#define DEBUG int main(int argc,char *argv[]) { if(argc!=4) { printf("Argument error!\n"); exit(-1); } FILE *fp,*tmpfp; if((fp=fopen(argv[1],"rb"))==NULL) exit(-1); if((tmpfp=fopen("tmp.txt","w"))==NULL) exit(-1); for(;;) { if(copy(fp,tmpfp,argv[3],strlen(argv[3]))==NULL) break; insert(fp,tmpfp,argv[2],strlen(argv[3])); } fclose(fp); fclose(tmpfp); remove(argv[1]); rename("tmp.txt",argv[1]); return 0; } FILE *copy(FILE *fp,FILE *tmpfp,char *s,int m) { #ifdef DEBUG printf("copy\n"); #endif // DEBUG char ch[m+1]; for(;;) { #ifdef DEBUG printf("for\n"); #endif // DEBUG fpos_t pos,*ptr=&pos; fgetpos(fp,ptr); fread(ch,1,m,fp); ch[m]=0; fsetpos(fp,ptr); fgetpos(fp,ptr); if(fgetc(fp)==EOF) return NULL; fsetpos(fp,ptr); if(strcmp(ch,s)==0) return fp; else { #ifdef DEBUG printf("fprintf\n"); #endif // DEBUG if(ch[0]!='\r') fprintf(tmpfp,"%c",ch[0]); /*由于windows平台文本模式打开方式输出'\n'时在前面自动添加一个'\r',此处输出'\r'的话会使'\r'多余*/ fseek(fp,1L,SEEK_CUR); } } } FILE *insert(FILE *fp,FILE *tmpfp,char *s,int m) { #ifdef DEBUG printf("insert\n"); #endif // DEBUG fputs(s,tmpfp); fseek(fp,m,SEEK_CUR); return fp; }最大的收获体现在第60、61行,我是这样发现的,在用程序处理完一个文本文件后,我想在每行的末尾添加一些字符继续测试,神奇的发现,用记事本打开添加后保存时行与行之间还不是一行,再次打开后吊诡的事情出现了:文本连成了一行!
百思不得其解,我就用WinHex打开程序处理过的文件,很奇怪的,我用程序连续处理同一文本文件每处理一次,换行处 0A 前就多出一个 0D 不可显示字符,也就是字符‘\r', 0A 即为’\n',一旦换行处 0D 超过两个,在换行处用记事本打开再次添加字符就会使 0D 0A统统消失,导致的结果是文本连成了一行。于是我在程序中加入了60、61两行,作业完成啦!后来查阅其他书籍,得到了61行注释中的结论。
这些收获虽然都是书本上有的,但当自己真正的体验到时,才能真正的感觉到书上所说的那些应该注意的地方究竟是什么意思。
(第一次在这里写博文,错误的地方希望得到大家指正!)
转载请注明来源 http://blog.csdn.net/imred/article/details/26058847