C语音文件与输入输出-62_第1页
C语音文件与输入输出-62_第2页
C语音文件与输入输出-62_第3页
C语音文件与输入输出-62_第4页
C语音文件与输入输出-62_第5页
已阅读5页,还剩59页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

从问题到程序裘宗燕北京大学数学学院2005年1第八章

文件与输入输出机试系统名单已导入031112031113031121用户名及密码均为学号访问地址为::8080/xdoj/2文件概念C程序中的文件使用结合介绍计算机及程序中一些相关问题和概念C语言标准库中的输入输出函数输入输出中的格式控制问题交互式程序的概念和相关问题38.1文件概念输入、输出和文件功能解决程序处理的数据来源和结果的保存问题。程序里的变量至多存在到程序结束。程序中积累的信息不能通过变量带到下次执行程序启动时OS为其分配存储。程序结束后该存储区可能分配给其他程序内存的特性:关机后存储其中的数据立刻消失为“持续”保存数据,必须借助外存,磁盘、磁带等语言需提供访问和使用外存的能力。文件是外存数据组织的基本单元。4外存数据组织在目录、文件结构中。目录是子目录和文件的集合,文件是封装的数据体。目录、文件通过名字使用。与外存交换信息主要就是访问文件。从程序向外送信息是输出,由外部取信息是输入。OS通常把所有IO对象都统一到文件的概念,包括外存文件和各种IO设备(键盘、显示器、打印机等)。键盘、显示器等设备也看作文件,给定“文件名”,有关操作都通过相应的文件名实施。IO工作繁杂琐碎,依赖于运行环境。语言需处理有关问题、屏蔽细节、以利编程,是程序语言设计的难点5C语言本身无支持IO的结构或者机制,通过库提供IO具体C系统可提供自己的IO功能。ANSIC把IO纳入标准库,以提高可移植性。标准库IO基于文件概念,定义了相关数据结构和一组IO函数。文件IO采用流概念。IO的对象是文件,为与文件交换信息就需要与之建立联系,流就是这种联系。要从文件输入,需要创建与文件关联的输入流。向文件输出要创建输出流。还可能创建输入输出流建立联系(创建流)的操作称作打开文件,文件打开后就可以对它操作。一个文件不再用时可以切断联系(撤消对应的流),称为关闭文件打开、关闭是文件处理的基本操作6流分为两类:正文流(字符流)和二进制流。正文流:文件是字符行的序列。一行包含0个或多个字符,每行最后有换行符'\n'正文流适合一般IO,包括与人交互的IO二进制流:把文件看成字节的序列。二进制流操作保证信息写入文件后按同样方式读回,内容不会改变主要用于程序内部数据的直接保存和装入,在保存装入大批数据时速度快,避免转换引起的信息损失通过二进制流保存的文件不适合人阅读7流通过特殊数据结构实现:标准库定义了一个类型FILE,其具体定义由实现确定。FILE中存储与流操作(IO文件)有关的信息文件打开操作返回一个指向FILE的指针(文件指针),代表所建流文件指针类型格式:FILE*对流的操作都通过这种FILE指针进行可以认为文件指针就是流的具体体现文件变量定义格式:FILE*fp;C标准库提供一套函数,包括流创建(打开文件)、撤消(关闭文件),文件读写(通过流对文件读写)等typedef

struct{shortlevel;/*缓冲区“满”或“空”的程度*/unsignedflags;/*文件状态标志*/charfd;/*文件描述符*/unsignedcharhold;/*如无缓冲区不读取字符*/shortbsize;/*缓冲区的大小*/unsignedchar*buffer;/*数据缓冲区的位置*/

unsignedchar*curp;/*当前工作指针*/unsignedistemp;/*临时文件,指示器*/shorttoken;/*用于有效性检查*/}FILE;8程序启动时自动建立三个文件指针:标准输入流stdin标准输出流stdout标准错误流stderrstdin与OS标准输入连接stdout与标准输出连接stderr通常直接与显示设备连接,不能重新定向标准输入输出操作getchar,scanf,putchar等都是对这些流(stdin,stdout)进行的9标准库采用缓冲式

IO。外存(磁盘、磁带等)速度慢,用成块方式传递数据。程序用数据的方式不同。为缓和两者差异,缓冲式IO用一块存储区(缓冲区)作为文件与程序的中介。10打开文件时为流建立缓冲区(一般通过动态存储分配),文件与程序间数据传递都通过缓冲区进行。文件关闭时释放流缓冲区。虽然程序与文件间有缓冲区,但操作中看不到它的影响(“透明性”),程序就像是直接在与外存打交道。“透明性”思想在计算机领域里非常重要,是许多设计的基础。在许多领域里都能够看到。例如网络传输,数字电话等等。118.2文件的使用通过重新定向可以将文件作为IO对象,把与标准设备(键盘、显示器)连接的标准流转连到指定文件这种做法有很大局限性:形成的定向在程序执行中不能改变无法使用多个文件(只有流stdin和stdout)为在程序中方便地使用文件,需要利用标准库的文件操作函数,通过建立输入输出流的方式使用文件。12文件的打开和关闭

fopen打开文件,返回FILE指针,通过这种指针可进行各种文件操作。原型:FILE*fopen(constchar*filename,constchar*mode)filename是文件名;mode指明文件打开方式。基本方式:"r"按读方式打开文件,找不到文件则失败"w"按写方式打开,文件已有则丢弃原内容"a"按添加方式打开或创建文件,在已有部分之后写"r+"读更新方式,可以对文件读或写"w+"写更新方式,可写读,如文件存在则丢弃原内容"a+"添加并可读方式,从文件尾接着写13用二进制方式时加b:"rb"、"wb+"、"a+b"以读写方式打开文件,使用时有特殊规定若文件打开出错,fopen返回空指针值关闭文件用函数fclose,原型:intfclose(FILE*stream)

关闭前将输出流缓冲区的数据送入文件;而后释放缓冲区。正常时返回0,否则返回EOF。能同时打开的文件数有限,文件用完后应关闭流程序退出时所有文件将自动关闭14使用文件的基本编程模式:FILE*fp;fp=fopen("myfile.abc","r");if(fp==NULL){/*处理文件打不开的情况,可能return*/}....../*对文件的操作*/fclose(fp);文件操作都是与外部打交道,能否正常完成,依赖于外部环境当时的情况。所以:fopen后应检查函数返回值,确保后续操作有效15若想通过交互输入文件名,可采用如下程序模式:intmain(){charfname[128];/*注意越界危险*/FILE*fp;

printf("Filename:");

scanf("%127s",fname);/*读入不超过127字符*/if((fp=fopen(fname,"r"))!=NULL){......}......}如果必须得到正确的文件名并正确打开,可用循环while(1){

printf("Filename:");scanf("%127s",fname);if((fp=fopen(fname,"r"))!=NULL)break;

printf("Filenameerror!\n");}16文件输入输出函数字符IO函数字符IO函数fgetc和fputc的原型:intfgetc(FILE*fp) 从流fp读字符intfputc(intc,FILE*fp)向fp写字符返回该字符。遇文件结束fgetc返回EOF,出错时都返回EOF。返回值是int类型,原因与getchar相同。getc和putc与上述函数类似,用宏实现标准流的字符读写函数通常如下定义:#definegetchar()getc(stdin)#defineputchar(c)putc(c,stdout)17程序cat,将命令行参数作为文件名,顺序复制到标准输出;无文件名时由stdin向stdout复制:#include<stdio.h>voidfilecopy(FILE*ifp,FILE*ofp){/*文件复制*/

intc;while((c=getc(ifp))!=EOF)putc(c,ofp);}intmain(intargc,char*argv[]){FILE*ifp;char*name=argv[0];if(argc==1){/*无参数情况*/

filecopy(stdin,stdout);return0;}while(*++argv!=NULL)if((ifp=fopen(*argv,"r"))==NULL)

printf("%s,can'topeninputfile:%s\n",name,*argv);else{

filecopy(ifp,stdout);fclose(ifp);}return0;}18例:从键盘输入一些字符,逐个把它们送到磁盘文件里,直到输入一个“#”为止。#include<stdio.h>#include<stdlib.h>main(){FILE*fp;charch,filename[10];

scanf("%s",filename);if((fp=fopen(filename,"w"))==NULL){printf("cannotopenfile\n");exit(0);}

ch=getchar();while(ch!='#'){fputc(ch,fp);

putchar(ch);

ch=getchar();}

fclose(fp);}19格式化IO函数文件格式化IO函数fscanf/fprintf的原型:intfscanf(FILE*stream,constchar*format,...);intfprintf(FILE*stream,constchar*format,...);

功能、使用与scanf、printf类似,增加了FILE*参数。按格式串做数据形式的转换,对指定流操作。三个圆点(...)是特殊参数描述形式,表示参数数目可变。定义这种函数用<stdargs.h>,第11章有介绍编译时不检查对应“...”的实参个数、不做类型转换、不检查错误/不产生错误信息。调用时,格式串后的参数在类型和个数上必须与格式串的转换描述一致!20例2:将一个磁盘文件中的信息复制到另一个磁盘文件中。#include<stdio.h>#include<stdlib.h>main(){FILE*in,*out;charinfile[10],outfile[10];printf("Entertheinfilename:\n");scanf("%s",infile);printf("Entertheoutfilename:\n");scanf("%s",outfile);

if((in=fopen(infile,"r"))==NULL){printf("cannotopeninfile\n");exit(0);}if((out=fopen(outfile,"w"))==NULL){printf("cannotopenoutfile\n");exit(0);}

while(!feof(in))fputc(fgetc(in),out);fclose(in);fclose(out);}21

行式IO函数:以行为单位进行输入或输出。行IO函数需要用字符数组存放输入或者输出字符序列。char*fgets(char*buf,intn,FILE*s);buf为字符数组。由流s读至多n-1个字符存入buf,遇换行符结束,换行符存入数组。最后放'\0'。正常结束时返回buf,文件结束或出错时返回空指针。buf应至少能容纳n个字符。intfputs(constchar*buf,FILE*s);将buf表示的字符串送到流s,输出的最后不向流中添换行字符(串里可以包含换行)。正常完成时返回非负值,出错时返回EOF值。22例:文件保存了一批单价和数量数据。写程序通过命令行参数提供文件名,最终输出货物的总货值设文件数据以单价、数量形式成对出现,用fscanf读入。定义函数nextentry完成一对数据的读入,它在发现所有数据处理完毕后返回0值。这里没处理数据出错情况,可补充修改。

doublenextentry(FILE*fp){doublepr,num;intn=fscanf(fp,"%lf%lf",&pr,&num);returnn==EOF?0.0:pr*num;}23intmain(intargc,char**argv){doubletotal=0.0,x;FILE*ifp;if(argc==1){/*缺参数,产生错误信息*/

printf("Missingfilename.Stop!\n");return1;}if((ifp=fopen(argv[1],"r"))==NULL){printf("Can'topen:%s.Stop!\n",argv[1]);return2;}while((x=nextentry(ifp))!=0.0)total+=x;printf("Totalprice:%f\n",total);

fclose(ifp);return0;}函数形参char*argv[]和char**argv可以通用,因为它们发生在数组调用过程中,而在数组定义过程中,必须按照规定格式进行,如:

char*name={“aa”,“bb”};//定义在以后调用过程中,完全可以写成:

char**pp=name;char*p[]=name;等形式。24二进制流和直接IO函数格式化函数fscanf、fprintf工作时,要做内外部形式之间的转换。如int内部是二进制序列,实际输出的是一串数字字符。格式化输入函数也要做转换数据转换要耗费时间。如IO对象是人,就必须进行转换。若是要输出到文件,以便后来取回程序使用,转换就没必要了转换还可能丢失信息,尤其是对浮点(double等)数据,输出后再输入可能产生误差。为解决这些问题,标准库提供二进制流和直接IO函数25如果程序里需要把保存起一些内部信息,以便随后使用或下次执行程序时再用,合适方式是用二进制流,直接将内部数据保存到文件里。直接输入输出函数:size_tfread(void*p,size_tsize,size_tnum,FILE*s)size_tfwrite(constvoid*p,size_tsize,size_tnum,FILE*s)fwrite向s输出num个大小为size的数组元素,p指定数据的起始位置,各数据项顺序写入指定文件。fread由s读num个大小size的元素。p应指数组,数组元素类型应与size一致,数组大小至少为num。26直接存入文件后以同样方式读回,得到的数据不变。fwrite返回输出的元素个数,小于num说明出错fread返回实际读入元素的个数对二进制流,应该用feof检查是否遇到文件结束27直接输入输出的例:设datatable是所用数组类型:

enum{TLEN=100};typedefdoubledatatable[TLEN];datatablem,n;设计算出的值存在m,要存入文件。设二进制输出流为msave。保存m的全部信息:fwrite(m,sizeof(double),TLEN,

msave);如果需要把以前保存的数据装入数组n(数据的形式符合要求)。已为此建立了流msaved。下面调用完成输入并把函数返回值存入变量num:num=fread(n,sizeof(double),TLEN,msaved);

288.3标准输入输出与格式控制标准IO的行式输入输出对标准输入/输出流也有行式IO函数。原型:char*gets(char*s)intputs(constchar*s)gets读一行到s,用'\0'代行尾'\n'并返回s。s应是字符数组。出错或遇文件结束返回NULLputs将字符串s以及一个换行符输出。正常完成返回非负值,出错返回EOF用gets时应注意数组越界(无法限制写入长度),应选用足够大的数组29一般使用形式:chars[256];...if(gets(s)!=NULL)......if(puts(s)!=EOF)...应检查和处理操作未正常完成的情况。gets函数无长度控制,成为许多系统的安全漏洞。建议用fgets(buf,len,stdin)或者scanf(格式串里写%ns,其中的n为长度限制)代替gets30输入格式控制以scanf为例介绍标准输入函数的格式控制scanf的原型:intscanf(constchar*fmt,...);scanf将输入流看成空白字符分隔的字段,读入过程就是对这些字段的顺序处理fmt指挥scanf进行转换,成功转换得到的值赋给对应变量(地址来自参数)scanf处理完格式串或遇转换失败时结束,返回所完成的转换项数31fmt里各种字符的意义与作用:空白字符(空格/制表符):要求scanf跳过输入中遇到的空白字符(空格、换行符、制表符)普通字符:非转换描述的非空白字符。要求scanf将它与输入流下一个非空白字符匹配,字符相同时匹配成功,否则转换失败转换描述:一个转换描述刻画对流中下一字段的转换方式。以字符%开始,到转换字符结束32转换描述:%之后可有:*表示只匹配不赋值;长度描述(整数)指定输入字符个数;赋值目标长度指示h、l或L;最后是转换字符。转换顺利完成时结果赋给参数所指变量(无*时)。若有长度描述就以指定个数的字符为字段。有星号(如%*s、%*6d等)时所匹配的东西不赋值,直接丢掉。转换字符的意义,所要求实际输入,对应参数类型:d 十进制形式的整数。 (int*)i 整数,可为十、八或十六进制表示。 (int*)o 八进制整数,可无先导字符0。 (int*)u 无符号十进制整数。 (unsigned*)x 十六进制整数,可无先导0x或0X。 (int*)33c 字符,指定输入宽度可输入多字符到字符数组。不跳过空白,读入多字符后不加'\0'。 (char*)s 非空白字符序列,可有长度限制。在字符数组最后附加'\0'(做成字符串)。 (char*)e,f,g

符合C语言规定形式的浮点数。 (float*)p 指针值,形式与printf("%p")输出一致。用于把由printf输出的指针值读回。 (void*)n 向对应参数中写入本次函数调用执行到此已读的字符个数。不实际读入也不计转换项数。 (int*)[...]

与所列字符的最长序列匹配,写入数组并附'\0'。用[]...]表示匹配串可含']'。 (char*)[^...]

与不含所列字符的最长序列匹配,写入数组并附'\0'。[^]...]表示不含']'。 (char*)% 与输入流中的字符%匹配,不赋值。34转换字符d、i、o、u、x前面可以标明目标长度,h表示赋值目标为short;l表示赋值目标为long转换字符e、f、g前加l表示目标是double,L表示是longdouble加了这些字符,scanf将按指定类型构造和赋值未做转换前出错或者遇到文件结束时返回EOF其他情况下返回执行中完成转换的项数,返回0表示第一个转换失败35例:设scanf当时要处理转换描述串%ld:scanf读入并丢掉空白符号(可无),然后做匹配若遇到的非空白字符不能看作数,则匹配失败否则就逐个读入字符,直至遇到第一个非数字字符将读入的正负号及数字字符序列做成一个长整数,赋给指定的变量从这个转换描述串之后继续处理格式串如果转换失败,引起失败的字符仍在流中,下次调用输入函数将先读到这个字符36写好输入需要考虑许多细节,较麻烦。原因:输入描述与外部打交道的方式,应根据外部提供的信息决定工作方式。外部情况不受写程序的人控制。写好处理输入的程序片段,需要考虑外部的各种可能,适当处理有关情况(难说“正确”,只能说“适当”)外部输入不满足程序要求时,应提供一些“信息”写输入时总要做一些假设,考虑一些问题。书上有一些例子和说明37输出格式控制以printf为例(其他格式化输出函数同)原型:intprintf(constchar*fmt,...);fmt里的非转换描述直接输出根据转换描述顺序转换各实参,结果插入在fmt里相应位置,形成整个输出出错时返回负值正常完成返回实际输出的字符个数38转换描述从%开始到转换字符止,中间可有若干字符,顺序地可以有如下成分(都可缺):1)标志字符:下面字符可以以任意顺序出现:-转换结果在字段范围内左对齐+数值前面总输出正负号 空格 转换得到的首字符不是正负号时输出一个空格0若数值输出不能填满字段,前面全填0#指定另一种形式。对转换符o的数值前加0;x和X的非0结果前加0x或0X;e、E、f、g、G输出时总写小数点;g和G,不去掉最后的0。392)一整数。最小宽度,转换结果至少占这么宽,可更宽。若得到的序列不够,在左边(或右边,有+时)填空格。数值在有0标志时在数字序列左边填0。3)圆点及另一数。对字符串参数表示最大输出字符数;对e、E、f表示小数点后数位;对g、G表示有效数位;对整数表示最小输出数字个数,不够时左边添0。4)目标长度字符h、l或L。h和l用于整参数,h指short或unsignedshort;l指long或unsignedlong。L指longdouble。字段宽度/精度可写*,表示值由参数取得。提供值的参数必须是int。40转换字符,所要求参数类型和实际输出形式:d,i 带符号的十进制形式整数(int)o 无符号八进制表示的整数,无先导0(int)x,X 无符号十六进制整数,无先导0x或0X。用x时十以上数字用abcdef;对X用ABCDEF(int)u 无符号十进制整数(int)c 字符,转为unsignedchar输出(int)s 字符序列,从参数所指位置直到字符'\0',或者达到字段的指定宽度为止(char*)f 一般实数形式[-]mmm.ddd,小数点后位数由精度描述定,默认6。精度为0不输出小数点(double)41e,E 科学记数法[-]m.dde±xx或[-]m.ddE±xx,小数点后位数由精度描述确定,默认6。精度为0时不输出小数点(double)g,G 灵活形式。指数小于-4或大于等于精度描述时用%e或%E形式输出,否则用%f形式输出。末尾的0或小数点不输出(double)p 指针,用具体实现确定的形式(void*)n 把本次函数执行到此已输出的字符数写进参数。处理这个“转换描述”进行不进行任何转换(int*)% 输出字符%,无转换42通过字段宽度和精度控制、对齐等,灵活运用转换描述可形成所需输出形式。几个实例:%16.8lf%-10.6f%20.12e%010ld%.7s格式串里可用星号表示字段宽度和精度,实际宽度精度由参数取得。这个功能使我们可以在程序里通过程序机制控制输出格式。例:printf("%s%*d\n",”Num:",width,num);先输出一个字符串,然后输出num的值(假定num是整型变量),字段宽度由(int变量)width值确定。43字符串作为格式化IO的对象文件是字节序列,正文流是字符序列,从文本流输入就是从中读取一段。输出的情况相反。字符串也是字符序列,可作为IO对象标准库有两个以字符串为对象的格式化IO函数。输入函数从字符串读并转换,结果赋给指定变量。输出函数实现相反过程,把输出存入字符数组并做成字符串函数原型是:intsscanf(char*s,constchar*fmt,...)intsprintf(char*s,constchar*fmt,...)与对应格式化IO函数相同,只是对象是字符串44出错处理输出出错信息是希望显示到屏幕供人读。通过stdout不合适:输出定向到文件也使出错信息送到文件。送到标准错误流stderr的信息不受定向影响。改造前面程序cat,只需要改动一个语句:fprintf(stderr,"%s,can'topeninfile:%s\n",name,*argv);文件错误检查:intferror(FILE*stream)IO出错时设置相关流的出错标志变量。ferror检查流的出错标志。当stream的状态变量设置时返回非0。错误标志复位(清除)函数

voidclearerr(FILE*stream)45标准库错误处理:为检查程序执行中出错,库提供了一些机制。每个C系统定义了一组错误编号,0表示无错,其余值表示各种错误。一些函数出错时自动给状态变量errno设错误编号,还定义了相应的信息串。见<errno.h>。打印当时错误信息:voidperror(char*s)检查当时错误编号(perror调用之前的最近错误),把对应信息串送到stderr。输出形式: 字符串s

冒号错误信息串换行46例:设有一组文件,每个文件存一批实数。写程序由用户取得文件名,对各文件中的数值求平均值输出。8.4程序实例程序功能分解:从文件读数值求平均值并输出是一项独立工作,定义为函数,原型设计为:voidaverage(FILE*fp,char*fname);它要求的参数一个打开的输入文件指针和文件名。由主函数处理取得文件名并打开文件的工作。综合上面考虑,average可定义为:47voidaverage(FILE*fp,char*fname){doublex,sum=0.0;

intl=0,n=0,m,c;printf("\nFile%s:\n",fname);whlie((m=fscanf(fp,"%lf",&x))!=EOF){++l;if(m==1){sum+=x;n++;continue;}

printf("Error,line%d:",l);while(!isspace(c=getc(fp)))

fputc(c,stderr);/*丢掉数据并通知用户*/

fputc('\n',stderr);}

printf("Average:%16.8f\n",sum/n);}48主函数确定循环终止方法。下面用文件结束符,给文件结束信号(组合键Ctrl-Z)程序结束:intmain(void){

charname[128];FILE*fp;while(1){printf("Filename(Ctrl-Ztoend):");if(scanf("%127s",name)==EOF)break;

if((fp=fopen(name,"r"))==NULL)printf("Can'topen:%s\n",name);

else{

average(fp,name);fclose(fp);

}}

printf("Bye!\n");return0;}49主函数的另一版本,要求用户通过命令行参数提供文件名。命令行没有文件名就什么也不做。intmain(intargc,char**argv){FILE*fp;while(*++argv!=NULL){if((fp=fopen(*argv,"r"))==NULL)printf("Can'topen:%s\n",*argv);

else{

average(fp,*argv);fclose(fp);}}printf("Bye!\n");return0;}50例:考虑一个背英语单词程序,基本循环是显示一个中文词,要求用户输入对应的英文单词并给以评判。

程序运行中应保存一对对中文/英文词。最好将单词存在文件里,启动程序后将单词装入。需要用文件IO。

为简化输入,应该先设计好单词文件的格式。例如每行里放一对英文词和中文词,英文在前中文在后,空格分隔。(文件格式设计很重要)单词文件可能不止一个,用户通过交互提供文件名,要求程序装入文件内容,而后交互练习。中文和英文词用字符串形式存入一个两维字符数组。

51可以有下面定义:/*WDNUM:中英文词数;WDLEN:单词存储数组长度,ROUND:一轮练习的次数*/enum{WDNUM=1000,WDLEN=32,ROUND=20};charwds[WDNUM*2][WDLEN];#defineENGLISH(i)wds[2*(i)]#defineCHINESE(i)wds[2*(i)+1]定义宏是为写程序方便。已经可以定义main:52intmain(void){charfn[256];FILE*fp;intterms;do{getnstr("Wordfilename:",256,fn);if((fp=fopen(fn,"r"))==NULL)printf("Wrongfilename.");else{terms=readfile(fp,WDNUM,wds);

fclose(fp);if(terms==0)continue;wordgame(terms,wds,ROUND);}}while(next("wordfile"));return0;}53getnstr从标准输入读一段字符,遇空白字符结束:voidgetnstr(charprompt[],intlim,charbf[]){

intc,i=0;printf("%s",prompt);while(i<lim-1&&(c=getchar())!=EOF

&&!isspace(c))bf[i++]=c;if(c!='\n')while(getchar()!='\n');/*吃掉本行剩余字符*/

bf[i]='\0';}本函数认为输入行里有用信息就是一个字段(由空白界定),其他信息都应抛弃。读入的字符存入数组bf,做成字符串形式。注意检查越界问题。

54next是前面简单函数的推广,它输出提示串参数,通过读入的y或者n确定返回真假值。本函数只在遇到y时返回1,其余情况都返回0:intnext(chars[]){intc;printf("Next%s?(y/n):",s);

do{c=getchar();

}while(isspace(c));/*读到一个非空白字符*/

while(getchar()!='\n')

;/*吃掉本行剩余字符*/

if(c=='y'||c=='Y')return1;elsereturn0;}这两个函数都可能用到许多程序里。55wordgame采用随机选择策略(有许多改造可能):voidwordgame(intterms,intrd,charwds[][WDLEN]){

intn,i;charwd[WDLEN];do{for(i=0;i<rd;++i){n=rand()%terms;printf("%s",CHINESE(n));getnstr(">",wd,WDLEN);if(strcmp(wd,ENGLISH(n))==0)printf("Ok!\n");elseprintf("Wrong!Itis:%s\n",ENGLISH(n));}}while(next("Round?"));}56readfile采用按行输入方式,读入一行后分析输入并复制到指定数组,以便必要时生成错误信息:intreadfile(FILE*fp,

intlim,charwds[][WDLEN]){charline[256],*p;intl=0,n=0;while(n<lim&&fgets(line,256,fp)!=NULL){++l;p=line;p=charscopy(WDLEN-1,ENGLISH(n),p,'');p=charscopy(WDLEN-1,CHINESE(n),p,'');if(*ENGLISH(n)=='\0'||*CHINESE(n)=='\0')printf("Wrongline#%d:%s",l,line);else++n;}returnn;}57charscopy把由s开始不超过lim个非空白字符复制到单词数组t。delim表示单词分隔符,前面用''。也支持用其他分隔符,这种设计使函数更通用。

char*charscopy(intlim,chart[],chars[],chardelim){inti;while(isspace(*s))++s;/*跳过s中的空白*/for(i

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论