第5章 文件
文件 文件(file)是存储在一个共同名称下的数据的集合,通常存在磁盘或者光盘上。例如,你存储在磁盘上的一个C语言程序就是一个文件的例子。每个文件都有一个文件名。 有两种基本的文件类型:文本文件(text file)和二进制文件(binary file)。两种文件类型都使用二进制代码存储数据,差别是这个代码用什么代表。
文本文件 文本文件使用单独的字符编码(ASCII编码)存储每个单独的字符。字符代码的使用允许这样的文件通过文字处理程序或者文本编辑显示,以使得每个人都能够不受任何C语言程序的约束地阅读它们。 例如,整型的十进制数5678,采用文本文件存放则需要占用4个字节,因为文本文件将5,6,7,8分别当做4个字符来存储,因此需要占用4个字节,它们的ASCII编码为:
所以文本文件占用的字节数大,读写操作需要转化,但是在屏幕上显示非常直观。 ASCII码:00110101 00110110 00110111 00111000 十进制码: 5 6 7 8 所以文本文件占用的字节数大,读写操作需要转化,但是在屏幕上显示非常直观。
二进制文件 二进制文件使用的代码计算机处理器在内部为C语言的原始数据类型使用的代码相同。这意味着数字以真正的二进制代码形式存储,然而,只有字符串保留它们ASCII字符码形式。 若采用二进制文件存放5678,将它转换为二进制为 (5678)=(1011000101110) 因此只需2个字节即可存储该数字了。
而实际上在内存中所存放的数据5678就是以二进制形式存放的。所以二进制文件的典型优点是速度快和紧凑性,因为不需要转换,读写操作效率高,但是在屏幕上显示会出现乱码,不直观。
文件的存取 文件流(file stream)是用于连接存储在物理设备(如磁盘,光盘等)上的文件到一个程序的单向传输路径。 磁盘 程序文件 #include <stdio.h> void main( ) { …… } 程序文件 将文件存(写)入磁盘 输出流文件 将文件从磁盘取(读)出 输入流文件
例5-1:从键盘输入一段文字,保存到磁盘的文本文件中。 分析: A)数据的结构(字符串) B)确定输入项(键盘输入字符串) C)确定期望的输出(将该字符串输出到磁盘中) D)列出算法:
文件指针 在C语言中,对文件进行操作时,都会有一个指向文件的FILE结构指针,即所谓的文件指针。 FILE *fp; 此时,指针变量fp是一个指向FILE类型(文件类型)的指针,当没有对fp赋值时,它不指向任何文件。对fp的赋值通常是借助文件的打开操作来完成的。
文件的操作 对文件的操作一般通过打开文件、读或写文件、关闭文件三个步骤完成。这三个步骤的实施都离不开一个称为文件指针的变量。 文件指针 在C语言中,对文件进行操作时,都会有一个指向文件的FILE结构指针,即所谓的文件指针。 FILE *fp; 此时,指针变量fp是一个指向FILE类型(文件类型)的指针,当没有对fp赋值时,它不指向任何文件。对fp的赋值通常是借助文件的打开操作来完成的。
文件的打开使用函数fopen(),函数原型为 FILE *fopen(char *filename, char *type); 需要打开的文件名 文件打开的类型P108表5-2 例如: FILE *fp; fp=fopen(“test.txt”, “r”); 上述的这两条语句实现了这样的操作:打开存放在当前目录下的文本文件“test.txt”;文件的打开方式为“只读”方式;文件指针fp指向这个文件。
如果要同时打开多个文件,就要定义多个文件指针分别指向这些文件,例: FILE *fp1 , *fp2 ; fp1=fopen(“aaa.txt”,”r”); fp2=fopen(“bbb.dat”,”rb”);
int fclose(FILE *stream); 文件的关闭 在使用完一个文件后应该关闭它! 关闭一个指定的文件常使用函数fclose()。 函数原型为: int fclose(FILE *stream); 例:fclose(fp);//关闭指针fp指向的文件 还可以使用函数fcloseall()来关闭所有被打开的文件。 例:fcloseall(); 文件指针
文件的读和写操作 与数据处理一样,对于文件的读写也有不同的方式: 1)字符读写; 2)字符串读写; 3)格式化读写 4)数据块读写;
1.文件的读字符函数和写字符函数 (1)读字符函数fgetc。 fgetc函数的功能是从指定的文件读一个字符,函数原型为: int fgetc(FILE *stream); 当读取正确时,返回所读取的字符;当读取错误时返回-1(EOF:文本文件的结束标志); 注意:在fgetc函数调用中,读取的文件必须是以“r”(读)或者“r+”(读写)方式打开。
例5-0:将磁盘D上的文本文件“test.txt”中的字符一一读出,并且输出到显示屏上。 分析: A 确定的输入项:由文件“test.txt”提供。 B 期望的输出:将文件“test.txt”输出到显示屏上。 C 算法: 打开文件:以只读的方式打开文本文件“test.txt” 读文件:使用字符读函数fgetc完成。 输出到显示屏上:可以使用字符输出函数putchar完成。 关闭文件。用函数fclose完成。
(2)写字符函数 fputc。 fputc函数的功能是把一个字符写入到指定的文件中。函数原型为: int fputc(char ch, FILE *stream); 若写入正常,则返回字符ch的ASCII码值,否则返回-1(EOF)。 注意:在fputc函数调用中,被写入的文件可以是“w”(写)或者“w+”(读写)或者“a”(追加)方式打开。
例5-1从键盘输入一串字符,存入到磁盘的文本文件中。 分析: A 确定输入项:由键盘输入 B 期望的输出:写入到磁盘的文本文件中。 C 算法: 打开文件:以写方式打开; 键盘输入字符:用字符数组存储所输入的字符串; 写字符到文件:使用字符写函数fputc完成; 关闭文件。
2.文件的读字符串和写字符串函数 (1)读字符串函数fgets。 函数原型为 char *fgets(char *string , int n , FILE * fp); 该函数的功能是从fp所指向的文件中读n-1个字符,放到以string为起始的存储空间(string 可以是数组名)中。若在读出n-1个字符前遇到换行符,或者文件结束符,则读操作结束。在读入的最多n-1个字符后面加上一个字符串结束标志’\0’。
【用字符串读函数做例5-0】 将磁盘D上的文本文件“test.txt”中的字符一一读出,并且输出到显示屏上。 分析: A 确定的输入项:由文件“test.txt”提供。 B 期望的输出:将文件“test.txt”输出到显示屏上。 C 算法: 打开文件:以只读的方式打开文本文件“test.txt” 读文件:使用字符串读函数fgets完成。 输出到显示屏上:可以使用字符输出函数puts完成。 关闭文件。用函数fclose完成。
(2)写字符串函数fputs; 函数原型 int fputs(char *string , FILE *fp); 功能:将string所表示的字符串内容(不含最后的’\0’)输出到fp所指向的文件。若操作成功,返回一个非负数,否则返回-1(EOF)。
【用字符串写函数做例5-1】 例5-1从键盘输入一串字符,存入到磁盘的文本文件中。 分析: A 确定输入项:由键盘输入 B 期望的输出:写入到磁盘的文本文件中。 C 算法: 打开文件:以写方式打开; 键盘输入字符:用字符数组存储所输入的字符串; 写字符到文件:使用字符串写函数fputs完成; 关闭文件。
3.文件的格式化读写函数 (1)格式化读函数fscanf。 函数原型: int fscanf(FILE *fp, char *fomat , &arg1, ……,&argn); 功能:按照format给出的控制符格式,把从fp所指向的文件中读取的内容分别赋予变量arg1,……,argn. 若读取成功,则返回以读取的数据的个数;如遇文件结束,则返回-1(EOF);若出错,则返回0. 例如:fscanf (fp,”%d,%d”,&x,&y);
例: fprintf (fp,”%d,%d”,x,y); 函数原型: int fprintf(FILE *fp , char *fomat, arg1,……,&argn) ; 功能:按照format给出的控制符格式,将变元arg1,……,argn的值写入到fp所指向的文件中去。若写入成功,则返回实际写入的数据的个数;若出错,则返回负数。 例: fprintf (fp,”%d,%d”,x,y);
例5-2 把2~1000之内的素数存入文本文件中。 A:输入项:2~1000 B:期望的输出:2~1000之内的素数 C:算法: 打开文件; 判断一个正整数是否为素数,若是则fprint()函数写入到文件中用。 关闭文件。
习题5.2:求两个数的最大者。 A:输入项:两个整数(实数) B:期望的输出:将最大者写入文件 C:算法: 打开文件; 判断大小 将较大者写入到文件中。 关闭文件。
4.数据块读写函数 (1)数据块读函数fread。 函数原型: int fread(void *buff,int size,int count,FILE fp); 其中:buff是一个指针,表示要存放读入数据的地址; size表示要读入的数据块的字节数; count表示要读入的数据块的块数; fp表示文件指针。 功能:从fp所指向的文件中读取count个size字节的数据块,放到由buff所指向的内存地址中。读取成功时,返回count的值,即读取数据块的完整个数,否则出错,返回EOF。读取成功时,实际读取的数据块可能小于代入的count的值。
(1)数据块写函数fwrite。 函数原型: int fwrite(void *buff,int size,int count,FILE fp); 其中:buff是一个指针,表示存放写入数据的地址; size表示要写入的数据块的字节数; count表示要写入的数据块的块数; fp表示文件指针。 功能:将buff所指的内存地址中的count个size字节数据块写入fp所指向的文件中。若写入成功,返回count的值,否则出错。写入成功时,实际写入的数据块可能小于代入的count的值。
例5-3 实现文件的复制。 A:输入项:源文件名和目标文件名 B:期望的输出:将源文件中的所有内容写入 目标文件中 C:算法: 键盘输入源文件名和目标文件名; 打开两个文件:一个是读的方式打开源文件,一个是以写的方式打开(目标文件) 读文件、写文件(用数据块读写函数实现); 关闭文件。
数据块的读写函数fread()和fwrite()常用于二进制文件的读写。对于二进制文件,其文件的结束是通过函数feof()来判断。
文件定位 (1)rewind函数。 (2)ftell函数。 函数原型: long ftell(FILE *fp); 函数原型: void rewind(FILE *fp); 功能:将fp所指向的文件的内部位置指针置于文件开头,并清除文件结束标志和错误标志。 (2)ftell函数。 函数原型: long ftell(FILE *fp); 功能:得到fp所指向的文件中的当前位置,该位置用相对于文件头的位移量来表示。成功时返回当前读写的位置;失败时返回-1.
(2)fseek函数。 函数原型: int fseek(FILE *fp,long offset , int base); 其中:fp为文件指针; base 为起始点,可以为0、1和2。 0:(SEEK_SET)表示文件开始 1:(SEEK_CUR)表示文件的当前位置 2:(SEEK_END)表示文件末尾。 offset为位移量,是指以起始点为出发点的移动字节数。当位移量为负数时,表示向文件头方向移动;当位移量为正数时,表示向文件尾方向移动。
#include <stdio.h> int max(int a,int b) { if(a>=b)return a; else return b; } void main() int a,b,z; FILE *infile; if((infile=fopen("d:\\max.txt","r+"))==NULL) puts("文件打不开"); return; fscanf(infile,"%d,%d",&a,&b); z=max(a,b); fseek(infile,0,SEEK_END); fprintf(infile,"\r\n最大数为%d",z); fclose(infile);
习题5.2:求两个数的最大者。 A:输入项:两个整数(实数) B:期望的输出:将最大者写入原来文件的数据之后 C:算法: 打开文件; 判断大小; 文件内部指针定位,将较大者写入到文件该位置中。 关闭文件。
显示16点阵汉字 问题:将所输入的汉字以16点阵的形式放大输出到显示器上。16点阵汉字是指有16行、每行16个点的图形。 具体事例请看:16点阵汉字.jpg
问题分析: 4 读数据:读16*2个字节的数据,显示汉字图形 5 关闭文件 输入:键盘输入汉字;在图形字库中查找位置 期望的输出:放大的16点阵汉字; 算法: 1 打开图形字库(HZK16); 2 根据所输入的汉字找到该汉字的区位码; 3 根据区位码获得该汉字的图形数据开始位置; 4 读数据:读16*2个字节的数据,显示汉字图形 5 关闭文件
GB2312又称国标码,由国家标准总局发布,1981年5月1日实施,通行于大陆。新加坡等地也使用此编码。它是一个简化字的编码规范,当然也包括其他的符号、字母、日文假名等,共7445个图形字符,其中汉字占6763个。我们平时说6768个汉字,实际上里边有5个编码为空白,所以总共有6763个汉字。 GB2312规定“对任意一个图形字符都采用两个字节表示,每个字节均采用七位编码表示”,习惯上称第一个字节为“高字节”,第二个字节为“低字节”。GB2312中汉字的编码范围为,第一字节0xB0-0xF7(对应十进制为176-247),第二个字节0xA0-0xFE(对应十进制为160-254)。 GB2312将代码表分为94个区,对应第一字节(0xa1-0xfe);每个区94个位(0xa1-0xfe),对应第二字节,两个字节的值分别为区号值和位号值加32(2OH),因此也称为区位码。01-09区为符号、数字区,16-87区为汉字区(0xb0-0xf7),10-15区、88-94区是有待进一步标准化的空白区。
获取汉字的区位码: 由于每个汉字都是由两个字节存储的,第一个字节存储区码,第二个字节存储位码因此我们用字符数组的形式存储汉字。 例如:char chinese[3]=“啊”. 其中,chinese[0]存储该汉字的区码编码,chinese[1]存储该汉字的位码编码。都是16进制编码。chinese[2]则用来存储字符串的结束标志’\0’。 那么,如何根据一个汉字的区位码的编码来获取该汉字的区码和位码编号呢?比如汉字“啊”,它的区码和位码的16进制编码分别是:0xB0和0xA1. 那么,我们很容易知道该汉字的区位编号分别为: 区号i:chinese[0]-0xA1+1=chinese[0]-0xA0 位号j:chinese[1]-0xA1+1=chinese[1]-0xA0
当前位置(i,j)离字库的首元的距离(所存储的汉子数) 在字库文件HZK16中定位该汉字的位置: fseek(文件指针,(94*(i-1)+(j-1))*32L,SEEK_SET); 每个汉字在文件显示中占用32个字节 当前位置(i,j)离字库的首元的距离(所存储的汉子数)
读数据:读16*2个字节的数据,显示汉字图形 显示汉字图形函数: 循环16行 每行2字节 每字节8个位 如果位为1则显示“■”,否则显示“ ” 取位:一个字节ch从最高位开始,取最高位用ch & 0x80(ch&10000000) 然后向左移一位ch<<1,下一个位又到了最高位,如上炮制(重复8次)