K&R 第七章 输入与输出
7-1 编写一个程序,根据它自身被调用存放在argv[0]中的名字,实现将大写字母转换成小写字母或将小写字母转换成大写字母的功 能。
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc,char *argv[])
{
int c;
if(strcmp(argv[0],"lower")==0)
while((c=getchar())!=EOF)
putchar(tolower(c));
else
while((c=getchar())!=EOF)
putchar(toupper(c));
return 0;
}
7-2 编写一个程序,以合理的方式打印任何输入。该程序至少能够根据用户的习惯以八进制或十六进制打印非图形字符,并截断长文本行
#include <stdio.h>
#include <ctype.h>
#define MAXLINE 100 //一行显示字符的最大长度
#define OCTLEN 6 //八进制长度
//increment position counter for output
int inc(int pos,int n)
{
if(pos+n<MAXLINE)
return pos+n;
else
{
putchar('\n');
return n;
}
}
int main()
{
int c,pos;
pos=0;
while((c=getchar()) !=EOF)
if(iscntrl(c)||c==' ')//寻找非显示字符--删除控制符
{
pos=inc(pos,OCTLEN);
printf(" \\%03o ",c);//数字前有一个空格和一个反斜杠\,数字后又有一个空格
if(c=='\n') //换行后 pos重置为0
{
pos=0;
putchar('\n');
}
}
else
{
pos=inc(pos,1);
putchar(c);
}
return 0;
}
7-3 修改minprintf函数,使它能完成printf函数的更多功能
- va_list
va_list表示可变参数列表类型,实际上就是一个char指针
- va_start
va_start用于获取函数参数列表中可变参数的首指针(获取函数可变参数列表)
输出参数ap(类型为va_list): 用于保存函数参数列表中可变参数的首指针(即,可变参数列表)
输入参数A: 为函数参数列表中最后一个固定参数
- va_arg
va_arg用于获取当前ap所指的可变参数并将并将ap指针移向下一可变参数
输入参数ap(类型为va_list): 可变参数列表,指向当前正要处理的可变参数
输入参数T: 正要处理的可变参数的类型
返回值: 当前可变参数的值
- va_end
va_end用于结束对可变参数的处理。实际上,va_end被定义为空.它只是为实现与va_start配对(实现代码对称和"代码自注释"功能)
对可变参数列表的处理过程一般为
1、用va_list定义一个可变参数列表
2、用va_start获取函数可变参数列表
3、用va_arg循环处理可变参数列表中的各个可变参数
4、用va_end结束对可变参数列表的处理
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#define LOCATFMT 100
void minprintf(char*fmt,...)
{
va_list ap; //points to each unnamed arg
char *p,*sval;
char localfmt[LOCATFMT];
int i,ival;
unsigned uval;
double dval;
va_start(ap,fmt); //make ap point to list unnamed arg
for(p=fmt;*p;p++)
{
if(*p!='%')
{
putchar(*p);
continue;
}
i=0;
localfmt[i++]='%'; //start local fmt
while(*(p+1) &&!isalpha(*(p+1)))
localfmt[i++]=*++p;
localfmt[i++]=*(p+1);
localfmt[i] ='\0';
switch(*++p)
{
case 'd':
case 'i':
ival=va_arg(ap,int);
printf(localfmt,ival);
break;
case 'x':
case 'X':
case 'u':
case 'o':
uval =va_arg(ap,unsigned);
printf(localfmt,uval);
break;
case 'f':
dval=va_arg(ap,double);
printf(localfmt,dval);
break;
case 's':
sval=va_arg(ap,char*);
printf(localfmt,sval);
break;
default:
printf(localfmt);
break;
}
}
va_end(ap);
}
仿制printf
#include <stdio.h>
#include <stdarg.h>
void printint(int dec)
{
if(0 == dec)
{
return;
}
printint(dec / 10);
putchar(dec % 10 + '0');
}
void printstr(char *str)
{
while(*str)
{
putchar(*str);
str++;
}
}
void printfloat(float flt)
{
int tempint = (int)flt;
int tempflt = (int)(100000 *(flt - tempint));
if(tempflt % 10 > 5)
{
tempflt = tempflt /10 + 1;
}
else
{
tempflt = tempflt / 10;
}
printint(tempint);
putchar('.');
printint(tempflt);
}
void myprintf(const char *format,...)
{
va_list ap;
va_start(ap,format);
while(*format)
{
if(*format != '%')
{
putchar(*format);
format++;
}
else
{
format++;
switch(*format)
{
case 'c':
{
char val_ch = va_arg(ap,int);
putchar(val_ch);
format++;
break;
}
case 'd':
{
int val_int = va_arg(ap,int);
printint(val_int);
format++;
break;
}
case 's':
{
char * val_str = va_arg(ap,char *);
printstr(val_str);
format++;
break;
}
case 'f':
{
float val_flt = va_arg(ap,double);
printfloat(val_flt);
format++;
break;
}
default :
{
putchar(*format);
format++;
}
}
}
}
va_end(ap);
}
标准库fgets和fputs函数的代码,注意fgets最多只能读取maxline-1个字符。库函数gets和puts的功能与fgets和fputs函数类似,但它们是对stdin和stdout进行操作。注意:gets函数在读取字符串时将删除结尾的换行符('\n'),而puts函数在写入字符串时将在结尾添加一个换行符。
//get at most n chars from iop
char *fgets(char*s,int n,FILE*iop)
{
register int c;
register char *cs;
cs=s;
while(--n>0&&(c=getc(iop)) !=EOF)//最多读取n-1个字符
if((*cs++=c)=='\n')
break;
*cs='\0';
return (c==EOF&&cs==s)?NULL:s;
}
//fputs:put string s on file iop
int fputs(char*s,FILE*iop)
{
int c;
while(c=*s++)
putc(c,iop);
return ferror(iop) ?EOF:0;
}
//使用fgets函数实现getline
int getline(char*line,int max)
{
if(fgets(line,max,stdin)==NULL)
return 0;
else
return strlen(line);
}
7-6 编写一个程序,比较两个文件并打印它们第一个不相同的行
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLINE 100
void filecomp(FILE*fp1,FILE*fp2)
{
char line1[MAXLINE],line2[MAXLINE];
char *lp1,*lp2;
do{
lp1=fgets(line1,MAXLINE,fp1);
lp2=fgets(line2,MAXLINE,fp2);
if(lp1==line1 &&lp2==line2) //如果没错或读取到文件末尾,fgets返回刚读入的一行
{
if(strcmp(line1,line2)!=0)
{
printf("first different in line\n%s\n",line1);
lp1=lp2=NULL;
}
}
else if(lp1!=line1&&lp2==line2)
printf("end of first at line\n%s\n",line2);
else if(lp1==line1&&lp2!=line2)
printf("end of first at line\n%s\n",line2);
}while(lp1==line1&&lp2==line2);
}
int main(int argc,char *argv[])
{
FILE *fp1,*fp2;
//argc=3;
//argv[1]="fp1.dat";
//argv[2]="fp2.dat";
if(argc!=3)
{
fprintf(stderr,"cmp:need two file names\n");
exit(1);
}
else
{
if((fp1=fopen(*++argv,"r")) ==NULL)
{
fprintf(stderr,"comp:can't open %s\n",*argv);
exit(1);
}
else if((fp2=fopen(*++argv,"r")) ==NULL)
{
fprintf(stderr,"comp:can't open %s\n",*argv);
exit(1);
}
else
{
filecomp(fp1,fp2);
fclose(fp1);
fclose(fp2);
exit(0);
}
}
}
7-7 修改第5章的模式查找程序,使它从一个命名文件的集合中读取输入(有文本名参数时),如果没有文本名参数,则从标准输入中读取输入。当发现一个匹配行时,是否应该将相应的文件名打印出来?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXLINE 1000
void fpat(FILE*fp,char*fname,char*pattern,int except,int number)
{
char line[MAXLINE];
long lineno=0;
while(fgets(line,MAXLINE,fp)!=NULL)
{
++lineno;
if((strstr(line,pattern)!=NULL)!=except)
{
if(*fname)
printf("%s -",fname);
if(number)
printf("%ld:",lineno);
printf("%s",line);
}
}
}
int main(int argc,char*argv[])
{
char pattern[MAXLINE];
int c,except=0,number=0;
FILE *fp;
// argc=4;
// argv[1]="-n";
// argv[2]="cat";
// argv[3]="fp1.dat";
while(--argc>0&&(*++argv)[0]=='-')
while(c=*++argv[0])
switch(c)
{
case 'x':
except=1;
break;
case 'n':
number=1;
break;
default:
printf("find:illegal option %c\n",c);
argv=0;
break;
}
if(argc>=1)
strcpy(pattern,*argv);
else{
printf("Usage:find [-x] [-n] pattern[file...]\n");
exit(1);
}
if(argc==1)
fpat(stdin,"",pattern,except,number);
else
while(--argc>0) //get a named file
if((fp=fopen(*++argv,"r"))==NULL)
{
fprintf(stderr,"find:can't open %s\n",*argv);
exit(1);
}
else
{
fpat(fp,*argv,pattern,except,number);
fclose(fp);
}
return 0;
}
7-8 编写一个程序,以打印一个文件集合,每个文件从新的一页开始打印,并且打印每个文件相应的标题和页数
#include <stdio.h>
#include <stdio.h>
#define MAXBDT 3 //maximum lines at bottom page
#define MAXHDR 5 //maximum lines at head of page
#define MAXLINE 100 //maximum size of one line
#define MAXPAGE 66 //maximum lines on one page
//先打印文件名和页码 然后用足够的换行符在每页的开头留出总共MAXHDR行
int heading(char*fname,int pageno)
{
int ln=3;
fprintf(stdout,"\n\n");
fprintf(stdout,"%s page %d\n",fname,pageno);
while(ln++<MAXHDR)
fprintf(stdout,"\n");
return ln;
}
void fileprintf(FILE*fp,char*fname)
{
int lineno; //记录已经在某页纸上打印了多少行
int pageno=1;
char line[MAXLINE];
lineno=heading(fname,pageno++);
while(fgets(line,MAXLINE,fp)!=NULL)
{
if(lineno==1)
{
fprintf(stdout,"\f");//换页符
lineno=heading(fname,pageno++);
}
fputs(line,stdout);
if(++lineno>MAXPAGE-MAXBDT);
lineno=1;
}
fprintf(stdout,"\f");
}
int main(int argc,char *argv[])
{
FILE *fp;
//argc=3;
//argv[1]="fp1.dat";
//argv[2]="fp2.dat";
if(argc==1)
fileprintf(stdin," ");
else
while(--argc>0)
if((fp=fopen(*++argv,"r"))==NULL)
{
fprintf(stderr,"print:can't open %s\n",*argv);
exit(1);
}
else
{
fileprintf(fp,*argv);
fclose(fp);
}
return 0;
}
7-9 类似isupper这样的函数可以通过某种方式实现达到节省空间或者时间的目的。考虑节省空间或者时间的实现方式
//空间利用率高
int isupper(char c)
{
if(c>='A'&&c<='Z')
return 1;
else
return 0;
}
//时间利用率高,因为没有函数调用方面的开销;但要使用较多的空间,因为它是一个宏,每次执行需要进行展开
#define isupper(c) ((c)>='A'&&(c)<='Z')?1:0