第13章 文 件 六、字符和字符串读写函数 七、无格式转换的读写函数fread和fwrite 八、文件的定位 九、一个简单的读写存盘程序步骤
六、字符和字符串读写函数 1. 读取单个字符的fgetc函数 fgetc函数的原型为: int fgetc (FILE * pSrcFile); FILE*型的入口形参指明读取操作的来源, 实参匹配 fopen打开的磁盘文件或stdin。 该函数从磁盘文件的当前位置读取一个字符, 定位源 磁盘文件的位置指针向后移动一个字节。如果该指针到达文 件末尾,fgetc函数返回EOF。 注意例程getc与函数fgetc作用相同但可以作为函数和 宏出现。getchar()例程与getc(stdin)一致,亦可以作为函数 和宏出现。
[例]fgetc.cpp # include <stdio.h> void main(void) { FILE* fpGet =fopen ("fgetc.cpp","r"); if (fpGet==NULL) { printf ("fopen failed\n"); return; } int i=0; char buffer[512]; while (feof (fpGet)==0 && i<512) buffer [i++]= fgetc (fpGet); buffer[i]='\0'; printf ("%s\n",buffer); //fclose (fpGet); } //程序运行的结果显示fgetc.cpp在屏幕上
2. 存写单个字符的fputc函数 fputc函数的原型为:int fputc (int ch, FILE * pDstFile); fputc函数的第一个形参ch正是要存写到第二个形参 pDstFile关联的磁盘文件中去的字符,该函数返回第一个入 口形参的实参值。 pDstFile匹配一个fopen返回的流文件指针或stdout,调 用失败返回EOF。
[例] fputc.cpp # include <stdio.h> void main (void) { FILE* fpGet = fopen ("fputc.cpp", "r"); if ( fpGet == NULL) { printf ("fopen failed\n"); return; } FILE* fpPut = fopen ("fputc.xpp","w"); if ( fpPut == NULL) { printf ("fopen failed\n"); return; } while (!feof ( fpGet )) //while (feof (fpGet)==0) fputc (fgetc (fpGet), fpPut); fclose ( fpGet ); fclose ( fpPut ); }
程序运行之后即将上面的fputc.cpp源程序转存到fputc.xpp中。 上面单个输入输出函数其返回类型是int型的数值,虽然处理的是仅一个字节长的字符。 因此接受这些函数的结果的变量最好定义成int型变 量即: int ch1= fgetc ( pSrcFile ); 函数一般地会自动进行截断整型高位数的处理而只存 写低位。 而能不失包容性地应付占两个字节的重要结尾信息 EOF(-1)。
3. 按行读文本串fgets函数 函数fgets的原型为: char * fgets(char *pDstBuffer, int num, FILE * pSrcFile); FILE *型的入口形参pSrcFile定位数据的来源,匹配实 参stdin或fopen函数读模式返回的流文件的指针,从中读取 num-1个字符到char*的入口形参pDstBuffer标明的字符缓 冲区,pDstBuffer也就是该函数的返回地址。 如果在读取num-1个字符之前遇到换行符\n则读入结 束;然后添补一个\0字符,以表示字符串的规则结束。 暗地里定位源磁盘文件的位置指针向后移动字符缓冲区 实际拥有的字节数.pDstBuffer 匹配一个足够大的字符数组.
[例] fgets.cpp # include <stdio.h> void main (void) { FILE* fp=fopen ( "fgets.cpp","r" ); if ( fp==NULL ) { printf ("fopen failed\n"); return; } char sBuffer[128]; for ( int k=0;k<9;k++ ) if ( fgets ( sBuffer,128,fp )! = NULL) printf ( "%s", sBuffer ); fclose ( fp ); } //程序运行的结果显示 fgets.cpp在屏幕上。
4. 按行写文本串fputs函数 函数fputs的原型为: int fputs (const char *pSrcBuffer, FILE * pDstFile); 文本文件的操作一般按行进行,一行一行地读一行一行 地写,fgets函数执行按行读的操作,函数fputs则按行写。 fputs函数将第一个形参定位的只读字符串送到送到第 二个形参指明的目的文件中的当前位置。 如果调用成功函数返回正数,在出现错误时返回EOF。
[例] fputs.cpp # include <stdio.h> void main(void) { FILE* fpRead = fopen ("fputs.cpp","r"); if ( fpRead == NULL ) { printf ( "fopen r failed\n" ); return; } FILE* fpWrite = fopen ( "fputsw.cpp", "w") ; if ( fpWrite == NULL ) { printf ( "fopen w failed\n" );
char s [81]; while ( !feof ( fpRead )) //while ( feof ( fpRead ) = =0) if ( fgets ( s, 81, fpRead )! = NULL ) { fputs (s, fpWrite ); printf ( "%s", s ); } fclose ( fpWrite ); //程序运行显示fputs.cpp在屏幕上同时拷贝了一个备份 在磁盘文件fputsw.cpp中
七、无格式转换的读写函数fread和fwrite 函数直接读写磁盘文件的二进制数据,读写过程数据的 信息未发生转换。因而空间效率和时间效率都比较高。 1. 函数fread的原型为: size_t fread (void * pDst, size_t size, size_t items , FILE * pSrc); 第一个参数pDst定位读取数据的存放位置,匹配任意 集合类型的起始地址。 第二个参数size指出数据的类型大小; size = sizeof ( type )。
第三个参数items指出数据的最大项数,常对应数组的 维数,单一变量或对象常取1。 第四个参数是FILE * 型的入口形参pSrc,指明读入数 据的来源。 fread返回实际读取的项数,函数从输入文件中最多读 取items项数,每一项含有size字节,并将它们放入pDst起 始的内存中。 如果错误发生则所读的结果是游移的。 与pSrc关联的 位置指针向后移动实际读取的字节数。
2. fwrite存写函数 函数fwrite的原型为: size_t fwrite(const void * pSrc, size_t size, size_t items, FILE * pDst ); 一般地fread总是读取原先由frwite存写的二进制数据; 第一个参数入口形参pSrc定位所写数据的源位置,匹 配任意集合类型的起始地址。 第四个参数入口形参 pDst指向待存写的目的地即与 pDst相关联的磁盘文件。 其余两个参数同fread函数。
函数fwrite 返回实际所存写的数据项的个数,如出现错 误返回值可少于items。 fwrite函数从pSrc定位地址起尽量转送items个数据项 到输出流文件中,位置指针递增实际所写的字节数。 上面两个函数都存在void*的指针用于宽泛地定位内存 的地址,但由于其含糊性必伴随具有size_t 类型的具体参数 严格划定数据的边界,这是void*型形参函数的共性。 一般地fread总是读取原先由frwite 存写的二进制数 据。
[例]直接随机方式处理结构变量和数组 #include<stdio.h> typedef struct SData { int nLineset; float fAdjust; int nPoint; float fXa; float fYa; float fZa; } CData; void WriteData ( CData pSrc[ ], int n, FILE *fpDst ) { switch (n) { case 1: fwrite ( pSrc, sizeof (CData), 1 ,fpDst); break; default: fwrite ( pSrc, sizeof (CData), n, fpDst ); } } inline void ReadData (CData* pDst, int n, FILE *fpSrc ) { fread ( pDst, sizeof ( CData ),n,fpSrc ); }
void PrintData ( const CData &d ) { printf (“%4d,%4.1f,%4d,%4.1f,%4.1f,%4.1f\n”, d.nLineset, d.fAdjust, d.nPoint, d.fXa, d.fYa, d.fZa); } void main (void) { CData s = {0,1,2,3,4,5}; CData d[ ]= {1,2,3,4,5,6, 2,3,4,5,6,7, 3,4,5,6,7,8}; FILE* fpWrite = fopen ( "c:\\cdata.dat", "w"); if ( fpWrite == NULL) { printf ( "fopen w failed\n" ); return;} WriteData ( &s, 1, fpWrite ); WriteData ( d, 3, fpWrite ); fclose ( fpWrite ); FILE* fpRead = fopen ( "c:\\cdata.dat", " r" );
if ( fpRead = = NULL ) { printf ( "fopen r failed\n" ); return;} CData b [4]; ReadData ( b, 3, fpRead ); ReadData ( b+3, 1, fpRead ); for ( int k=0; k<4; k++ ) PrintData ( b[k] ); } c:\cdata.dat文件的长度为4*6*4=96个字节,屏幕显示 结果为: 0, 1.0, 2, 3.0, 4.0, 5.0 1, 2.0, 3, 4.0, 5.0, 6.0 2, 3.0, 4, 5.0, 6.0, 7.0 3, 4.0, 5, 6.0, 7.0, 8.0
fread和fwrite函数因其void*入口参数而可以匹配任意 类型的变量地址,而称为随机读写函数。 但实际上数据的类型属性转移到size_t参数上去了,这 个size_t参数n=sizeof (type)协同函数在定位好的内存空间 中读写数据。 因此数据类型属性的匹配依然是环环相扣不可轻易错位 的。即此类型的函数操作此类型的数据。随机读写应根据数 据类型的匹配关系为: void WriteData ( CData*pSrc , int n, FILE * fpDst) { fwrite (pSrc,sizeof (CData), n, fpDst);} void ReadData (CData* pDst ,int n,FILE * fpSrc) {fread (pDst,sizeof(CData), n, fpSrc);}
八、文件的定位 1. ftell函数告知当前位置 FILE结构的声明中有一个char *型指针成员 _ptr即索 引数据流内容的位置指针,该指针潜在地指向当前文件的位 置。 当用户首次打开流文件进行读写操作时,操作系统把该 位置指针设置在文件的开头。 但特别地以追加方式a打开文件时,位置指针定位于文 件的结尾处。每次读写一个字符时,位置指针向前移动一个 字符。 如果从文件中读写一行文本,位置指针一般移到该行末 尾或下一行开始处。
ftell函数告知当前位置函数ftell的原型为: long ftell (FILE * stream); 函数返回的long型值为指定文件中的当前位置相对于流 文件起始位置的字节偏移量,stream一般匹配fopen函数打 开的文件。 [例]ftell.cpp #include<stdio.h> void main (void) { FILE * stream= fopen ( "ftell.cpp", "rb") ; double a[20]; fread ( a, sizeof (double), 20, stream); printf ( "%d\n", ftell (stream)); } //输出160
2. fseek函数探寻文件的位置 函数fseek用于在一个打开的文件中重新定位文件的位 置指针,如果成功返回结果0,否则返回非0值,在不能探寻 的设备上返回的结果是不定的。其函数原型为: int fseek (FILE *stream, long offset, int origin); // fseek(文件指针,偏移量,起始位置); 起始位置参量origin的取值范围必须是如下的宏名或随 后具体的常数,含义为: #define SEEK_CUR 1 //文件当前位置 #define SEEK_END 2 //文件结束位置 #define SEEK_SET 0 //文件开始位置
fseek函数在stream形参所对应的文件中运作,将其中 的位置指针变动到新的位置,新的位置由offset算定。 偏移量参量offset指出距离origin起始位置的字节偏移 数,正的long型实参表示以起始位置为基准前移offset个字 节作为新的位置,负的实参则后移。 在流上的下一回合操作以新的位置为基准,一般可安全 地用于二进制文件。 fseek函数操作文本文件的结果漂移于回车换行次数的 不确定性。
3. rewind函数反绕到文件开头位置 rewind函数原型为: void rewind ( FILE *stream ); 函数rewind重新定位与stream关联的位置指针到文件 的开头。 函数调用[ rewind (stream ); ]近似于: (void) fseek ( stream, 0L, SEEK_SET ); 但不象 fseek, 函数rewind清除文件结束符和stream的 出错标记。
九、一个简单的读写存盘程序步骤 读写存盘操作是程序设计中的重要工作,程序的读写操 作主要包含如下步骤和特点: 1. 构筑待存盘操作的数据结构,不同的数据结构对应不 同的函数; 2. 明晰数据的来源和走向,屏幕操作利用格式转换函数 以进行字符处理; 3. 格式化输出一般用于显示数据,格式化磁盘输入常严 格呼应格式化输出函数; 4. 非格式化函数直接在内存和磁盘之间输送数据无需信 息转换.
请打开 “第14章(1).ppt”