收藏 分享(赏)

计算机程序设计 (9).ppt

上传人:hskm5268 文档编号:6836309 上传时间:2019-04-23 格式:PPT 页数:54 大小:389.50KB
下载 相关 举报
计算机程序设计 (9).ppt_第1页
第1页 / 共54页
计算机程序设计 (9).ppt_第2页
第2页 / 共54页
计算机程序设计 (9).ppt_第3页
第3页 / 共54页
计算机程序设计 (9).ppt_第4页
第4页 / 共54页
计算机程序设计 (9).ppt_第5页
第5页 / 共54页
点击查看更多>>
资源描述

1、计算机程序设计基础,第八章 文件与数据存储,提 纲,8.1 文件的基本概念 8.2 文件的基本操作 8.3 文件的读写 8.4 数据存储 本章小结,8.1 文件的基本概念,文件定义 一组相关信息的集合;平时存储在外部介质上,需要时读入内存,操作完毕后再写回外部介质文件处理 数据流:数据从源到目的的流动,文件就是数据流动的源或目的 在 C 中,任何设备只要能够作为数据流动的源或目的都可以作为文件处理 文件操作:使用标准库函数,文件类型,二进制文件 信息以二进制格式存储 内容不分行,行尾无行结束标志 读写时不发生数据转换,文件中的内容与信息在内存中的存储布局相同 程序容易处理,程序员不容易理解 文

2、本文件 信息以文本(字符串)格式存储 文本分行,行尾有行结束标志 读写时发生数据转换(自动或人工),转换过程可逆 程序容易处理,程序员也容易理解,文件指针,文件指针类型 文件指针类型格式: FILE * 说明:所有对文件的操作都需要使用文件指针类型 文件变量 定义格式:FILE * fp; 说明:要访问某个文件,一定需要首先定义文件指针类型的变量,并将该变量与对应文件关联起来,此后才能进行文件操作,8.2 文件的基本操作,文件打开操作 文件关闭操作 文件结束检测操作 文件错误检测操作 文件缓冲区与流刷新操作 文件指针定位操作 文件指针位置查询操作 文件指针重定位操作,文件打开操作,文件打开函数

3、 fopen 函数原型:FILE * fopen( const char * filename, const char * mode ); 参数意义:filename 表示文件名(可带文件路径),mode 表示文件打开模式 返回值:成功执行,返回文件的对应指针,否则返回 NULL 注意:一定要检查 fopen 函数的返回值!,文件打开操作,文件打开模式 “r”:读模式,若文件不存在,则返回 NULL “w”:写模式,若文件不存在则创建它,若文件已存在则其内容被擦除 “a”:写模式,若文件不存在则创建它,若文件已存在则保留原内容,信息追加到文件尾部 “r+”:读写模式,文件必须已存在,否则返回

4、NULL “w+”:读写模式,若文件不存在则创建它,若文件已存在则其内容被擦除 “a+”:读写模式,若文件不存在则创建它,若文件已存在则保留原内容,信息追加到文件尾部 “b”:二进制格式打开文件,文件打开操作,文件打开模式说明 “b”模式 注意:不能单独使用,必须与其他模式合并使用 示例一:“rb”表示以二进制读模式打开文件 示例二:“w+b”表示以二进制读写模式打开文件 “a+”与“a”模式 相同点:追加信息均写入文件尾部 不同点:“a”模式不删除原始文件中的文件结束标志,部分程序不能读取新追加的信息 建议:使用“a+”而不是“a”,文件关闭操作,文件关闭函数 fclose 函数原型:int

5、 fclose( FILE * fp ); 参数意义:fp 表示待关闭的文件指针 返回值:成功执行时返回 0,失败时返回 -1 注意:测试函数时,要特别注意成功时为假(0),失败时为真(非 0),不同于标准库中其他函数! 程序结束时,未关闭的文件自动关闭 多个文件的关闭操作 函数原型:int fcloseall(); 关闭除标准文件流之外的所有文件,标准文件流,什么是标准文件流 C 语言为用户预定义的三个全局文件流数据对象 定义于“stdio.h”中 标准输入流 stdin:代表输入设备(一般为键盘) 用户输入均由系统自动放入该变量所代表的存储空间 标准输出流 stdout:代表输出设备(一般

6、为屏幕) 程序输出均由系统自动放入该变量所代表的存储空间 标准错误流 stderr:代表错误输出设备(一般为屏幕) 程序错误输出均由系统自动放入该变量所代表的存储空间,文件结束检测操作,文件结束检测函数 feof 函数原型:int feof( FILE * fp ); 参数意义:fp 表示待检测的文件指针 返回值:文件结束时返回真,否则返回假 feof 使用示例 FILE * fp; fp = fopen( “filename“, “w+“ ); if( !fp ) PrintErrorMessage( FALSE, “Failed in opening file %s.“, “filenam

7、e“ ); while( !feof( fp ) )/ 文件的具体操作在此 fclose( fp );,文件错误检测操作,文件错误检测函数 ferror 函数原型:int ferror( FILE * fp ); 参数意义:fp 表示待检测的文件指针 返回值:读写文件发生错误时返回真,否则返回假 ferror 使用示例 FILE * fp; fp = fopen( “filename“, “w+“ ); if( !fp ) PrintErrorMessage( FALSE, “Failed in opening file %s.“, “filename“ ); while( !feof( fp

8、 ) )if( ferror( fp ) ) / 发生文件读写错误,处理该错误 fclose( fp );,文件缓冲区与流刷新操作,缓冲区 系统为文件读写操作专门建立的数据中转站 文件缓冲区与流刷新函数 fflush 函数原型:int fflush( FILE * fp ); 参数意义:fp 表示待刷新的文件指针 强制清空文件缓冲区中的所有内容 多个文件流的刷新 函数原型:int fflushall(); 意义:刷新程序使用到的所有文件流,文件指针定位操作,文件指针定位函数 fseek 函数原型:int fseek( FILE * fp, long int offset, int origin

9、 ); 参数意义:fp 表示待定位的文件指针,offset 表示偏移量,origin 表示从什么位置开始计算偏移量 返回值:成功执行返回 0,否则返回非 0 origin 的取值 SEEK_SET:从文件开头偏移 offset 字节,此时 offset 总为非负数 SEEK_CUR:从文件当前位置偏移 offset 字节 SEEK_END:从文件尾部偏移 offset 字节,此时 offset 总为非正数,文件指针位置查询操作,文件指针位置查询函数 ftell 函数原型:long int ftell( FILE * fp ); 参数意义:fp 表示待查询的文件指针 返回值:文件指针的位置 ft

10、ell 的典型使用场合 调用此函数获得文件指针位置,然后调用 fseek 函数定位到该位置制定的偏移处进行数据读写 此函数很少使用,文件指针重定位操作,文件指针重定位函数 rewind 函数原型:void rewind( FILE * fp ); 参数意义:fp 表示待重定位的文件指针 rewind 的功能 在使用文件一段时间后将文件指针重新移动到文件开头 rewind:“倒带” 此函数使用场合较少,8.3 文件的读写,面向字符的文件读写操作 面向文本行的文件读写操作 面向格式化输入输出的文件读写操作 面向信息块的文件读写操作,字符读写操作,字符读取函数 函数原型:int getc( FILE

11、 * fp ); 意义:从文件 fp 中读取单个字符,以整型返回 字符写入函数 函数原型:int putc( int c, FILE * fp ); 意义:将字符 c 写入文件 fp,返回值为写入的字符 适用性 适用文本文件,逐一字符操作,字符读写操作,编写程序,完成文件复制,#include #ifndef _ZYLIB_ #include “zylib.h“ #endif FILE * OpenFile( const char * filename, const char * mode ); void CopyFile( FILE * in_file, FILE * out_file );

12、 int main() FILE * in, * out;in = OpenFile( “main.c“, “r“ );out = OpenFile( “main.bak“, “w“ );CopyFile( in, out );fcloseall();return 0; ,字符读写操作,FILE * OpenFile( const char * filename, const char * mode ) FILE * fp;fp = fopen( filename, mode );if( !fp )PrintErrorMessage( FALSE, “OpenFile: Failed in o

13、pening file %s.“, filename );return fp; void CopyFile( FILE * in_file, FILE * out_file ) int c;while( (c=getc(in_file) != EOF ) putc( c, out_file ); ,文本行读写操作,文本行读取函数 函数原型:char * fgets( char * s, int n, FILE * fp ); 意义:从文件 fp 中读取最多 n 个字符,写入 s 所指向的存储空间,函数在遇到换行符或读取了 n 1 个字符之后结束 返回值:成功执行时,返回 s,否则返回 NULL

14、 特别说明 确保 s 指向的存储空间在调用此函数时已分配 确保空间足够,字符串尾部0由系统自动添加;建议使用标准库中的 BUFSIZ 宏(512 字节) 读取过程中如遇到 EOF,函数同样返回 NULL,文本行读写操作,文本行写入函数 函数原型:int fputs( const char * s, FILE * fp ); 意义:将字符串 s 写入文件 fp 返回值:成功时为写入的字符个数;否则为 EOF 特别说明 fputs 函数并不复制字符串尾部的0 fputs 函数的返回值一般不需要测试 适用性 适用文本文件,按文本行模式操作,文本行读写操作,修改上例,使用文本行读写函数实现之,void

15、 CopyFile( FILE * in_file, FILE * out_file ) char bufferBUFSIZ;while( fgets( buffer, BUFSIZ, in_file ) ) fputs( buffer, out_file ); ,格式化读写操作,文件格式化输出函数 函数原型:int printf( const char * fmt, ); 函数原型:int fprintf( FILE * fp, const char * fmt, ); 函数原型:int sprintf( char * buffer, const char * fmt, ); 意义:fpri

16、ntf、sprintf 函数意义与 printf 基本相同,fprintf 写入文件 fp,sprintf 写入字符缓冲区 buffer 返回值:实际输出的字符个数,一般不需测试 特别说明 printf( fmt, ) 等价于 fprintf( stdout, fmt, ),文本行读写操作,编写程序,将随机生成的 5 个 0.01.0 之间的浮点数按照下述格式写入文本文件:a0=0.780090a1=0.809692a2=0.738953a3=0.724457a4=0.465240,字符读写操作,#include #ifndef _ZYLIB_ #include “zylib.h“ #endi

17、f #ifndef _ZYRANDOM_ #include “zyrandom.h“ #endif#define NUM_OF_ELEMENTS 5void GenerateReals( double a, unsigned int n ); FILE * OpenFile( const char * filename, const char * mode ); void WriteRealsToFile( double a, unsigned int n, FILE * fp );,字符读写操作,int main() double aNUM_OF_ELEMENTS;FILE * fp;Gen

18、erateReals( a, NUM_OF_ELEMENTS );fp = OpenFile( “data.txt“, “w“ );WriteRealsToFile( a, NUM_OF_ELEMENTS, fp );fclose( fp );return 0; void GenerateReals( double a, unsigned int n ) unsigned int i;Randomize();for( i=0; in; i+ ) ai = GenerateRandomReal( 0.0, 1.0 ); ,字符读写操作,FILE * OpenFile( const char *

19、filename, const char * mode ) FILE * fp;fp = fopen( filename, mode );if( !fp )PrintErrorMessage( FALSE, “OpenFile: Failed in opening file %s.“, filename );return fp; void WriteRealsToFile( double a, unsigned int n, FILE * fp ) unsigned int i;for( i=0; in; i+ )fprintf( fp, “a%d=%8.6lfn“, i, ai ); ,格式

20、化读写操作,文件格式化输入函数 函数原型:int scanf( const char * fmt, ); 函数原型:int fscanf( FILE * fp, const char * fmt, ); 函数原型:int sscanf(const char *buffer, const char *fmt, ); 意义:fscanf、sscanf 函数意义与 scanf 基本相同,fscanf 从文件 fp,sscanf 从字符缓冲区 buffer 读取字符串,写入后续参数中 返回值:实际输入的数据对象个数,一般不需测试 特别说明 scanf( fmt, ) 等价于 fscanf( stdin

21、, fmt, ) 注意写入空间是否足够,格式化输入函数的格式描述符,存储空间问题,整数、浮点数 一般不需要考虑空间是否足够问题 字符串:要特别注意 #define NUM_OF_ELEMENTS 10 char nameNUM_OF_ELEMENTS; fscanf( fp, “%9s“, name ); 替代魔数 9 char formatBUFSIZ; sprintf( format, “%ds“, NUM_OF_ELEMENTS 1 ); fscanf( fp, format, name );,格式化读写操作,编写程序,从上例生成的文本文件中读取最多 5 个浮点数并保存到数组中,#inc

22、lude #ifndef _ZYLIB_ #include “zylib.h“ #endif #ifndef _ZYRANDOM_ #include “zyrandom.h“ #endif#define NUM_OF_ELEMENTS 5FILE * OpenFile( const char * filename, const char * mode ); int ReadRealsFromFile( double a, unsigned int n, FILE * fp ); void PrintReals( double a, unsigned int n );,格式化读写操作,int m

23、ain() double aNUM_OF_ELEMENTS;int count;FILE * fp;fp = OpenFile( “data.txt“, “r“ );count = ReadRealsFromFile( a, NUM_OF_ELEMENTS, fp );PrintReals( a, count );fclose( fp );return 0; ,格式化读写操作,FILE * OpenFile( const char * filename, const char * mode ) FILE * fp;fp = fopen( filename, mode );if( !fp )Pr

24、intErrorMessage( FALSE, “OpenFile: Failed in opening file %s.“, filename );return fp; void PrintReals( double a, unsigned int n ) unsigned int i;for( i=0; in; i+ )printf(“a%d=%8.6lfn“, i, ai ); ,格式化读写操作,int ReadRealsFromFile( double a, unsigned int n, FILE * fp ) unsigned int i = 0;char junkBUFSIZ,

25、junk2;int t;while( i n )t = fscanf( fp, “%=%lf%c“, junk, ,数据块读写操作,数据块读取函数:二进制格式读取 原型:int fread( void * buffer, int size, int count, FILE * fp ); 意义:从文件指针 fp 指向的文件中读取 count 个大小为 size 字节的数据块,结果写入到 buffer 所指向的存储空间 返回值:读取的数据块数,需要特别注意! 数据块写入函数:二进制格式写入 原型:int fwrite( void * buffer, int size, int count, FI

26、LE * fp ); 意义:向 fp 指向的文件中写入 count 个大小为 size 字节的数据块,数据来自 buffer 所指向的存储空间 返回值:写入的数据块数,一般不需要测试,数据块读写操作示例,数据块写入示例 要求:将整数数组 a 中前 10 个整数写入文件 fp 每块包含一个整数,共 10 块if( fwrite( a, sizeof(int), 10, fp ) != 10 )PrintErrorMessage( FALSE, “Error in writing data.“ ); 每块包含 10 个整数,共一块if( fwrite( a, 10 * sizeof(int), 1

27、, fp ) != 1 )PrintErrorMessage( FALSE, “Error in writing data.“ );,数据块读写操作示例,数据块读取示例一 要求:从文件 fp 中读取 10 个整数,保存到数组 a 中,已知文件中至少包含 10 个整数if( fwrite( a, sizeof(int), 10, fp ) != 10 )PrintErrorMessage( FALSE, “Error in reading data.“ ); 数据块读取示例二 要求:从文件 fp 中最多读取 10 个整数,保存到数组 a 中,文件中包含多少整数未知(可能不够 10 个) 如何实现

28、?,数据块读写操作示例,数据块读取示例二:实现一 数据块数未知,需要边读边判断文件是否结束 错误代码:while( !feof( fp ) ) int e; if( fread( 错误结果:程序异常退出 错误原因:fread 函数在读取了 10 个整数后,文件结束标志 EOF 还未读取,文件没有结束,while 循环在试图获得第 11 个整数时;在 feof 函数返回前,fread 就已读取 EOF 标志,返回 0,此时必然导致 if 测试成功,数据块读写操作示例,数据块读取示例二:实现二 不检测 fread 函数返回值也有问题 错误代码:while( !feof( fp ) ) int e;

29、 fread( 错误结果:获取了 11 个整数,最后两个相同,并且可能会写入错误位置 错误原因:在 feof 函数返回前,fread 就已读取 EOF 标志,返回 0,此时 e 未设置,维持第 10 个整数值不变,并写入第 10 个整数的后续位置,数据块读写操作示例,数据块读取示例二:实现三 简单颠倒分支流程也有问题 错误代码:while( !feof( fp ) ) int e; if( fread( 错误结果:程序异常退出 错误原因:与实现一相同,数据块读写操作示例,数据块读取示例二:实现四 不仅要判断 fread 函数返回值是否正确,还需要判断文件操作是否发生错误 正确代码:while(

30、 !feof( fp ) ) int e; if( fread( 只有在出现错误时才需要输出错误信息,避免 fread 读取到 EOF 标志时流程出现错误,8.4 数据存储,数据持久化 定义:构造程序数据对象与外部磁盘文件的关联;在程序退出前将必要的数据写入文件,在程序再此执行时从文件中获取上次数据 持久化规则必须精心设计 不能简单将数据写入磁盘就完事! 一般原则:信息存储应按照使用性质(访问文件的时机)而不是数据性质来组织 示例:设置程序基本环境的数据,在程序启动时读取一次,不管这些数据性质有多大差别,都应组织在一个文件中,提高程序读取效率,动态数组的持久化,数据持久化接口 void DiW

31、riteToFile( DYNAMIC_INTEGERS a, const char * filename ); void DiReadFromFile( DYNAMIC_INTEGERS a, const char * filename ); 修改动态数组类型:添加持久化文件名与文件是否修改信息(以确定是否需要持久化) struct _DYNAMIC_INTEGERSunsigned int capacity; unsigned int count; int * elements;BOOL modified; char * filename; ; 定义默认文件名:如果用户没有提供文件名,则使

32、用缺省文件名存储持久化数据 static const char * const default_filename = “di.dat“; 函数实现,动态数组的持久化,void DiWriteToFile( DYNAMIC_INTEGERS a, const char * filename ) if( !a )PrintErrorMessage( FALSE, “DiWriteToFile: Parameter illegal.“ );if( a-modified )FILE * fp;_DiSetFileName( a, filename );fp = OpenFile( a-filename

33、, “wb“ );_DoDiWriteToFile( a, fp );fclose( fp );a-modified = FALSE; ,动态数组的持久化,void DiReadFromFile( DYNAMIC_INTEGERS a, const char * filename ) FILE * fp;if( !a )PrintErrorMessage( FALSE, “DiReadFromFile: Parameter illegal.“ );_DiSetFileName( a, filename );fp = OpenFile( a-filename, “rb“ );_DiClearEl

34、ements( a );_DoDiReadFromFile( a, fp );fclose( fp );a-modified = FALSE; ,动态数组的持久化,static void _DiClearElements( DYNAMIC_INTEGERS a ) a-count = 0;a-modified = TRUE; static void _DiSetFileName( DYNAMIC_INTEGERS a, const char * filename ) if( a-filename )DestroyObject( a-filename );if( filename )a-file

35、name = DuplicateString( filename );elsea-filename = DuplicateString( default_filename ); ,动态数组的持久化,static void _DoDiWriteToFile( DYNAMIC_INTEGERS a, FILE * fp ) if( fwrite( a-elements, sizeof(int), a-count, fp ) count )PrintErrorMessage( FALSE, “DiWriteToFile: Failed in writing file %s.“, a-filename

36、 ); static void _DoDiReadFromFile( DYNAMIC_INTEGERS a, FILE * fp ) while( !feof(fp) )int e;if( fread( ,应用程序的持久化策略,动态数组的持久化策略 简单持久化动态数组所有元素并不是最佳策略:不能保证程序下次执行时重构一模一样的动态数组 解决方法:将动态数组的容量、元素数目与元素一起持久化 解决步骤:首先持久化动态数组的容量、其次元素数目,最后才是所有元素值 持久化结果不再是数据的简单堆砌,而是带有格式信息的特殊文件结构 注意事项 动态数组的其他成员不需要持久化它们与程序的执行特性有关,而与实际

37、数据无关 elements 成员本身的值不需要持久化它指向某片动态分配的存储区,其值会随着动态数组操作不断变化,程序下次执行时也几乎不可能分配到同样的位置,动态数组的持久化策略,static void _DoDiWriteToFile( DYNAMIC_INTEGERS a, FILE * fp ) if( fwrite( ,动态数组的持久化策略,static void _DoDiReadFromFile( DYNAMIC_INTEGERS a, FILE * fp ) unsigned int capacity, count;if( fread( ,本章小结,文件的定义、性质与分类 文件的基本操作 文件的读写 面向字符的文件读写 面向字符串(文本行)的文件读写 面向格式化输入输出的文件读写 面向数据块的文件读写 数据的持久化 数据持久化的意义 动态数组的持久化,作 业,第 307 页:习题二 第 1、4 小题,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 网络科技 > 计算机原理

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报