C3文件操作与习题课v02.docx
《C3文件操作与习题课v02.docx》由会员分享,可在线阅读,更多相关《C3文件操作与习题课v02.docx(22页珍藏版)》请在冰豆网上搜索。
C3文件操作与习题课v02
第三课:
文件操作与习题课
1、*文件操作:
在很多时候,我们在进行大量运算时,为防止意外情况出现(比如断电,内存不足等),需要保存运算的临时结果并在程序再开时读取之;或是为以文件形式输入输出一些要求的数据。
这时,我们就需要用到文件操作。
#ifndef_FILE_DEFINED
struct_iobuf{
char*_ptr;
int_cnt;
char*_base;
int_flag;
int_file;
int_charbuf;
int_bufsiz;
char*_tmpfname;
};
typedefstruct_iobufFILE;
#define_FILE_DEFINED
#endif
文件操作有个基础的数据类型FILE,FILE在stdio.h中的定义见右。
右方代码并不要求掌握,其中struct将会在数据结构初步中讲到。
#ifndef,#define,#endif等属于C语言预处理范畴,只在有特殊用到的程序中会进行解释,并不会在课中讲到。
如果想查阅相关信息,C语言书中一般会有相关章节。
从右边我们了解到:
FILE的本质是一个叫_iobug的结构体。
内部有关于字串指针,暂时文件名,文件缓存、缓存大小等信息。
相关函数:
·文件打开关闭函数:
fopen和fclose
·字符读写函数:
fgetc和fputc
·字符串读写函数:
fgets和fputs
·数据块读写函数:
fread和fwrite
·格式化读写函数:
fscanf和fprinf
例1:
常用写文件函数使用示例:
#include
voidmain()
{
FILE*fp;
fp=fopen("1.txt","wb");
fprintf(fp,"%s\n","Afileoutputexample.");
fputs("Hopeyouwilllikeit.\n",fp);
fputc(1,fp);
fclose(fp);
}
运行结果:
注:
1.fopen的用法为:
FILE*返回文件指针fopen(char*文件名,char*控制命令)。
控制命令"wb"的作用是:
用二进制(b)方式打开只读文件(w)。
2.fclose的用法为:
flose(FILE*待关闭的文件指针)。
3.fprintf(FILE*待写的文件指针,char*输出字串模板,字串模板参数)。
其中字串模板及字串模板参数与printf中的参数用法一致。
4.fputs(char*待输出字符串,FILE*待写的文件)。
将待写字符串写到待写文件中。
5.fputc(char/int待输出字符,FILE*待写的文件)。
将待写字符写入待写文件中。
6.*fwrite(待写信息指针,待写块大小,待写块数,待写文件指针)主要在结构体中使用,现在暂不研究。
7.文件使用完成后一定要关闭(fclose),否则其它进程将无法读写该文件,也有可能引起很多稀奇古怪的异常。
例2:
常用读文件操作实例
#include
voidmain()
{
FILE*fp;
chars[24];
fp=fopen("1.txt","rb");
fread(s,sizeof(char),23,fp);
s[23]='\0';
printf("\t%s\n",s);
fgets(s,24,fp);
printf("\t%s\n",s);
s[1]=fgetc(fp);
printf("\t%s\n",s);
fclose(fp);
}
运行结果:
注:
1.fopen的控制命令"rb",作用是以二进制方式只读打开。
2.fread的用法是fread(接收信息指针,读取块大小,读取块数,待读文件指针),将文件指针在文件中的指向位置开始向后读取读取快大小*读取块数的信息,并往接收指针指向的地址上覆写。
3.fgets的用法为fgets(char*信息接收字符串,FILE*待读文件指针)。
4.fgetc的用法为char/int读取到的字符(FILE*待读文件指针)。
5.fscanf一般用得较少,就不做介绍了……
本节相关的文件操作只作了解,相关具体使用将在以后有所讲解。
2、习题课:
(1)第一章习题:
1.闰年问题。
问题分析:
判断闰年的标准是:
不被4整除的不是闰年,剩余余的不被100整除的是闰年,剩余的不被400整除的不是闰年,被400整除的是闰年。
即:
被4整除
是
被100整除
是
被400整除
是
是闰年
否
非闰年
否
是闰年
否
非闰年
以上是按年份规划的。
按图上要求,很容易用3个if语句写出相关程序。
但同时,问题也可以按结果规划:
是闰年
被4整除且不被100整除、被400整除
非闰年
不被4整除、被100整除且不被400整除
此时,用1个if语句就可以了。
第一个表格对应的程序:
#include
voidmain()
{
intyear;
boolleap;
printf("请输入年份:
");
scanf("%d",&year);
if(year%4)
leap=false;
else
if(year%100)
leap=true;
else
if(year%400)
leap=false;
else
leap=true;
if(leap)
printf("%d年是闰年。
\n",year);
else
printf("%d年不是闰年。
\n",year);
}
第二个表格对应的程序:
#include
voidmain()
{
intyear;
boolleap;
printf("请输入年份:
");
scanf("%d",&year);
if((year%4==0&&year%100!
=0)||(year%400==0))
leap=true;
else
leap=false;
if(leap)
printf("%d年是闰年。
\n",year);
else
printf("%d年不是闰年。
\n",year);
}
运行结果:
2.输出符号菱形,上三角行数为输入。
如:
输入:
3
输出:
*
***
*****
***
*
问题分析:
首先,要看出整个菱形的组成。
菱形的组成元素:
空格‘’,符号‘*’,换行符‘\n’。
菱形的组成分布:
可以认为由一个与输入数行数相等的正三角与比输入数少一行的倒三角组成。
第二部:
对正三角每行分析:
记输入数为L,则易见:
第一行的空格数为L-1个,符号数为1个,1个换行。
第二行的空格数为L-2个,符号数为3(2*2-1)个,换行。
……
最后一行(第L行),空格数为0(L-L)个,符号数为(2*L-1)个,换行。
所以,对第i行可以很容易地得出:
空格L-i个,符号(2*i-1)个,换行。
即:
行号
空格数
符号数
换行符数
1
L-1
1(2*1-1)
1
2
L-2
3(2*2-1)
1
……
i
L-i
2*i-1
1
……
L
0(L-L)
2*L-1
1
按表能很容易地用for循环输出每个行所需的东西。
至于下三角,将行号从L-1输出到1就已经出来了。
程序代码:
#include
voidmain()
{
intL,i,j;
printf("请输入行数:
");
scanf("%d",&L);
for(i=1;i<=L;i++)
{
for(j=0;jprintf("");
for(j=0;j<2*i-1;j++)
printf("*");
printf("\n");
}
for(i=L-1;i>0;i--)
{
for(j=0;jprintf("");
for(j=0;j<2*i-1;j++)
printf("*");
printf("\n");
}
}
读者应该注意到了,语句块:
for(j=0;jfor(j=0;j<2*i-1;j++)printf("*");
printf("\n");
反复出现,其中需要输入的参数为L,i,其作用为打印某行。
由于以上的特性,可以用第一课讲义的相关知识将其封装为函数。
程序代码:
#include
voidprintline(intL,inti)
{
intj;
for(j=0;jprintf("");
for(j=0;j<2*i-1;j++)
printf("*");
printf("\n");
}
voidmain()
{
intL,i;
printf("请输入行数:
");
scanf("%d",&L);
for(i=1;i<=L;i++)
printline(L,i);
for(i=L-1;i>0;i--)
printline(L,i);
}
运行结果:
3.将16进制转换为2进制的转换器。
如:
输入:
AE81
输出:
1010111010000001
问题分析:
这道题是很典型的字符串替换题。
对这种题,一般两种做法:
switch,数组。
由于并不要求储存,直接在while里用getchar输入字符再输出就可以了。
switch程序代码:
#include
voidmain()
{
charc;
while((c=getchar())!
='\n')
{
switch(c)
{
case'0':
printf("0000");break;
case'1':
printf("0001");break;
case'2':
printf("0010");break;
case'3':
printf("0011");break;
case'4':
printf("0100");break;
case'5':
printf("0101");break;
case'6':
printf("0110");break;
case'7':
printf("0111");break;
case'8':
printf("1000");break;
case'9':
printf("1001");break;
case'A':
//大A与小a输出一致,以下同。
case'a':
printf("1010");break;
case'B':
case'b':
printf("1011");break;
case'C':
case'c':
printf("1100");break;
case'D':
case'd':
printf("1101");break;
case'E':
case'e':
printf("1110");break;
case'F':
case'f':
printf("1111");break;
default:
printf("");break;//其它字符全部输出空格
}
}
printf("\n");
}
数组程序:
#include
voidmain()
{
char*s[17]={"0000","0001","0010","0011",
"0100","0101","0110","0111",
"1000","1001","1010","1011",
"1100","1101","1110","1111",
""};
charc;
intv;
while((c=getchar())!
='\n')
{
if('0'<=c&&c<='9')//输入0~9,变为数字0~9
v=c-'0';
elseif('a'<=c&&c<='f')//输入a~f,变为数字10~15
v=c-'a'+10;
elseif('A'<=c&&c<='F')//输入A~F,变为数字10~15
v=c-'A'+10;
else//处理其它字符
v=16;
printf("%s",s[v]);
}
printf("\n");
}
运行结果:
4.输出1000以内的所有素数。
问题分析:
本题也有两种做法。
大计算量和较大储存量。
一般程序是大计算量的:
在2~sqrt(n)中,如果n不能被所有数整除,输出n。
这样写编写很简单,但运算量较大。
#include
#include
#defineN1000
voidmain()
{
intn,i,lim;
boolf;
for(n=1;n<=N;n++)
{
f=true;
lim=int(sqrtf(n));
for(i=2;i<=lim;i++)
if(n%i==0)
{
f=false;
break;
}
if(f)
printf("%d",n);
}
printf("\n");
}
由于素数还有一个性质:
只要不被除1和自己外所有素数整除就是素数。
可以利用这条性质,将已算出的素数存在数组中,能节省很大的计算量:
#include
#defineN1000
#defineSN200
voidmain()
{
ints[SN];//用于存素数
boolf;
intn,i,sn;
s[0]=1;
sn=1;//记录已存的素数数
for(n=2;n<=N;n++)
{
f=true;
for(i=1;iif(n%s[i]==0)
{
f=false;
break;
}
if(f)//n若与素数数组中的所有其他数互素
s[sn++]=n;//则将n记录在素数数组中,并将素数总数加1
}
for(i=0;iprintf("%d",s[i]);
printf("\n");
}
运行结果:
5.编写并测试函数min。
要求:
输出两个数输入,输出两数最小值。
问题分析:
这只是基础的函数使用,没有难度。
程序代码:
#include
intmin(inta,intb)
{
returna
a:
b;
}
voidmain()
{
inta=19,b=11,c=3;
printf("min(a,b)=%d\n",min(a,b));
printf("min(b,c)=%d\n",min(b,c));
printf("min(a,c)=%d\n",min(a,c));
printf("min(a,b,c)=%d\n",min(min(a,b),c));
}
运行结果:
(2)第二章习题:
1.打印7行杨辉三角。
问题分析:
作为杨辉三角,有两种做法:
纯计算,储存计算。
杨辉三角有通式:
C(列数,行数),即【行数!
/(列数!
)*(行数-列数)!
】,这种方法比较简单,不需要储存,但计算量较大。
#include
#defineN7
voidmain()
{
longT;//用于储存临时结果
inti,j,k;//i为行数,j为列数
for(i=0;i<=N;i++)
{
for(j=0;j<=i;j++)//按组合算
{
T=1;
for(k=j+1;k<=i;k++)//(j+1)(j+2)...(i)
T*=k;//----------------
for(k=1;k<=i-j;k++)//
(1)
(2)(3)...(i-j)
T/=k;//1到j已被约去
printf("%4ld",T);
}
printf("\n");
}
}
储存计算的方法就简单多了,只用把每行的结果记录下来就可以了。
每行的第i个数是它上一行第i-1个数和第i个数的和,若该书不存在则取0。
这样需要一个(N+1)*(N+1)的数组,并且数组中只有一半左右的空间会被用到,其他空间都会白白浪费。
希望读者在看完以下程序后,能自己完成一个优化程序,即不用乘法,通过一部分运算来削减储存开销,此题留作课后习题。
程序代码:
#include
#defineN8
voidmain()
{
ints[N][N];
inti,j;
inta,b;
s[0][0]=1;
for(i=1;ifor(j=0;j<=i;j++)
{
a=j!
=0?
s[i-1][j-1]:
0;
b=j!
=i?
s[i-1][j]:
0;
s[i][j]=a+b;
}
for(i=0;i{
for(j=0;j<=i;j++)
{
printf("%4d",s[i][j]);
}
printf("\n");
}
}
运行结果:
2.输出1000000以内所有素数。
本题出的目的是比较第1章4题两算法的运算速度,算法见上,就不再赘述了。
3.30位正整数的加法。
问题分析:
本题中,数据不可能存储在现有的数据结构里,但如果想到用数组将各位分开存储,本题就豁然开朗了。
程序代码:
#include
#defineN8//每个int装4位
voidmain()
{
inta[N],b[N],r[N];
inti,C;
for(i=0;i{
a[i]=0;
b[i]=0;
}
charc;
boolpf=false;
while((c=getchar())!
='\n')
{
if(!
pf&&'0'<=c&&c<='9')
{
for(i=0;ia[i]*=10;
a[0]+=c-'0';
for(i=0;i{
C=a[i]/10000;
a[i]%=10000;
a[i+1]+=C;
}
}
if(c=='+')
pf=true;
if(pf&&'0'<=c&&c<='9')
{
for(i=0;ib[i]*=10;
b[0]+=c-'0';
for(i=0;i{
C=b[i]/10000;
b[i]%=10000;
b[i+1]+=C;
}
}
}
C=0;
for(i=0;i{
r[i]=a[i]+b[i]+C;
C=r[i]/10000;
r[i]%=10000;
}
if(C)
printf("OVERFLOW!
\n");
else
{
for(i=N-1;r[i]==0&&i>0;i--);
printf("%d",r[i--]);
while(i>=0)
printf("%04d",r[i--]);
printf("\n");
}
}
运行结果:
4.完成函数strcpy(字符串拷贝)。
问题分析:
很典型的指针操作题目。
strcpy(Stringcopy)函数的用法为char*strcpy(char*待写字串,char*字串内容)。
其中返回值为待写字串的地址,字串内容遇到'\0'停止读取。
程序代码:
#include
char*strcpy(char*a,char*b)
{
inti=0;
while(*(b+i)!
='\0')
{
*(a+i)=*(b+i);
i++;
}
*(a+i)='\0';
//while((*(a+i)=*(b+i))!
='\0')i++;
//while((*(a+i)=*(b+i++))!
='\0');
returna;
}
voidmain()
{
chara[100];
char*pa;
pa=strcpy(a,"100");
printf("a:
%s\n",a);
printf("pa:
%s\n",pa);
a[0]='2';
printf("\npa:
%s\n",pa);
}
运行结果:
注:
1.本程序在将函数注释掉后导入“string.h”,运行主函数结果不变。
2.函数中注释掉的两个while是递进关系,每一个都比上一个要精简,而且都能完成函数功能。
课后习题:
1.以正三角形输出杨辉三角,行数为L(1~10)。
2.输入矩阵,输出该矩阵的对角式(用小数)。
3.输入n,输出n的因式分解。
4.*将10000以内的素数以一行一个的形式保存成“素数.txt”。
竞赛预演
5.在本课的第二个杨辉三角的例子中,对于储存量的要求是n平方级别的,即随着n的增大,储存量以n平方的级数增大。
现要求将储存量压缩到n级别,如2n。
不能用组合等临时数可能超过有效位数的计算方法。
6.求n的阶乘,n为1到100。
(参考二章第三题)