文件概述
文件是指存储在外部存储器上的数据集合。更准确的来说,文件就是一组相关元素或数据的有序集合,而且每个集合都有一个符号化的指代,称这个符号化的指代为文件名。
文件类型
根据文件在外部存储器上的组织形式,文件可以分为ASCII文件和二进制文件。
- ASCII文件(文本文件):以字符的方式进行存储,一个字符对应一个ASCII码,而一个ASCII码占用1字节。例如:整数12在内存中占用4个字节;如果按照ASCII码的方式存储,则占用2个字节。ASCII文件可以阅读,可以打印,但是它与内存数据交换时需要转换;
- 二进制文件:将内存中的数据按照其在内存中的存储形式原样输出并保存在文件中。二进制文件占用空间小,内存数据与磁盘数据交换时无需转换,可以节省外存空间和转换时间。但是二进制文件不可阅读、打印。
在C语言中,扩展名为.c的源文件是由字符构成,而扩展名由.obj目标文件和.exe可执行文件是由二进制符号构成的。其实:txt文件也是一个文本文件。
C如何操作文件——文件指针
C语言是通过文件指针变量的操作来实现对文件的具体访问。
文件的指针不是指向一段内存空间,而是指向描述有关这个文件的相关信息的一个文件信息结构体,该结构体定义在studio.h头文件中。当然,也无需了解有关此结构体的详细细节,只需要知道如何使用文件指针就行了。
声明文件指针变量的一般形式为:
FILE* 文件型指针变量名;
其中,FILE应为大写;它实际上是由系统定义的一个结构体,该结构体中包含了文件名、文件使用方式、当前位置等信息。
在stdio.h文件中,FILE的结构体定义为:
typedof atruct
{
int _fd; /* 文件号 */
int _cleft; /* 缓冲区剩下的字符 */
int _mode; /* 文件操作模式 */
char* _nextc; /* 下一个字符的位置 */
char* _buff; /* 文件缓冲区位置 */
}FILE;
文件缓冲区
由于文件存储在外存储器上,外存的数据读/写速度相对较慢,所以在对文件进行写/读操作时,系统会在内存中为文件的输入或输出开辟缓冲区。
- 当对文件进行输出时,系统首先把输出的数据填入为该文件开辟的缓冲区内,每当缓冲区被填满时,就把缓冲区中的内容一次性输出到对应的文件中;
- 当从某个文件输入数据时,首先将从输入文件中输入一批数据放入到该文件的内存缓冲区中,输入语句将从该缓冲区中依次读取数据;当该缓冲区的数据被读完时,将在从输入文件中输入一批数据到缓冲区。
文件的打开与关闭
C语言规定,任何文件在使用之前必须打开,使用之后必须关闭。对文件的操作都是通过标准函数来实现的。
文件的打开——fopen()函数
C语言用fopen()函数打开一个文件,其调用的一般形式为:
文件指针名 = fopen(文件名,文件的使用方式);
该函数可以通过对文件指针名的判断来对文件打开进行判断,如果文件指针名为NULL,则文件打开失败;否则打开成功。
文件的使用方式和含义如下表所示:
打开方式 | 含义 | 指定文件不存在时 | 指定文件存在时 |
r | 只读方式打开文本文件 | 出错 | 正常打开 |
w | 只写方式打开文本文件 | 建立新文件 | 文件原有内容丢失 |
a | 追加方式打开文本文件 | 建立新文件 | 在原有内容末尾追加 |
r+ | 读/写方式打开文本文件 | 出错 | 正常打开 |
w+ | 读/写方式创建新的文本文件 | 建立新文件 | 文件原有内容丢失 |
a+ | 读/追加方式建立新的文本文件 | 建立新文件 | 在原有内容末尾追加 |
rb | 只读方式打开二进制文件 | 出错 | 正常打开 |
wb | 只写方式打开二进制文件 | 建立新文件 | 文件原有内容丢失 |
ab | 追加方式打开二进制文件 | 建立新文件 | 在原有内容末尾添加 |
rb+ | 读/写方式打开二进制文件 | 出错 | 正常打开 |
wb+ | 读/写方式创建新的二进制文件 | 建立新文件 | 文件原有内容丢失 |
ab+ | 读/追加方式创建新的二进制文件 | 建立新文件 | 在原有内容末尾追加 |
高版本的VS编译器可能会认为fopen()函数不安全,会强制要求使用fopen_s()函数来代替。
fopen_s()函数调用的一般形式为:
errno_t err;
err = fopen_s(指向文件指针的指针,文件名,文件的使用方式);
err = fopen_s(&fp,"d:\\1.txt","r");
这个函数的使用有两点注意:
- 该函数有返回值,如果打开文件成功,函数返回值为0;否则返回值非0;
- 该函数的第一个参数时指向文件指针的指针,也就是说,需要传递文件指针的地址。
文件的关闭——fclose()函数
在程序中,当对一个文件的操作使用完毕后,应将其关闭,断开文件指针与该文件之间的联系,防止文件遭到其他操作的破坏。C语言用fclose()函数打开一个文件,其调用的一般形式为:
fclose(文件指针);
该函数有返回值,如果关闭文件成功,函数返回值为0;否则返回值非0。
文件的顺序读/写
字符读/写函数fgetc()和fputc()
fgetc()函数的功能是从指定的文件中读取一个字符,其调用的形式为:
字符变量 = fgetc (文件指针);
如果在执行fgetc()函数时遇到文件结束符,函数会返回一个文件结束符标志EOF(-1)。
fputc()函数的功能是把一个字符写入指定的文件中,其一般调用的格式为:
fput(字符,文件指针);
例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE* fp1, *fp2;
errno_t err;
char c;
err = fopen_s(&fp1, "d:\\1.txt", "w");
if (err != 0) {
printf("文件打开失败!\n");
exit(0);
}
c = getchar();
while (c != '\n') {
fputc(c, fp1);
c = getchar();
}
fclose(fp1);
err = fopen_s(&fp2, "d:\\1.txt", "r");
if (err != 0) {
printf("文件打开失败!\n");
exit(0);
}
c = fgetc(fp2);
while (c != EOF) {
printf("%c", c);
c = fgetc(fp2);
}
printf("\n");
fclose(fp2);
return 0;
}
从上面的例程可以看出,文件的顺序读/写函数仅仅是沟通文件和程序之间的函数,如果想要从键盘上输入或者输出到命令行等,还是需要我们之前学到的输入输出的内容,不会的可以参考链接【编程语言】C语言基础(包括:关键字、数据类型、输入输出)。
字符串读/写函数fgets()和fputs()
fgets()函数的功能是从指定的文件中读取一个字符串,其调用的形式为:
fgets(字符数组名,n,文件指针);
其中,n是一个正整数,表示从文件中读出的字符串不超过n-1个字符。在读入一个字符串后加上字符串结束标志'\0'。
如果在执行fgets()函数时如果文件内的字符串读取完毕,函数会返回0。
fputs()函数的功能是把一个字符串写入指定的文件中,其一般调用的格式为:
fputs(字符串,文件指针);
其中,字符串可以是字符串常量、字符数组、字符指针变量。
例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE* fp1, *fp2;
errno_t err;
char s[30];
err = fopen_s(&fp1,"d:\\1.txt", "w");
if (err != 0) {
printf("文件打开失败!\n");
exit(0);
}
gets(s);
while (strlen(s) > 0) {
fputs(s, fp1);
gets(s);
}
fclose(fp1);
err = fopen_s(&fp2, "d:\\1.txt", "r");
if (err != 0) {
printf("文件打开失败!\n");
exit(0);
}
while (fgets(s, 11, fp2) != 0) {
printf("%s", s);
}
printf("\n");
fclose(fp2);
return 0;
}
数据块读/写函数fread()和fwrite()
读数据块函数fread(),其调用的一般形式为:
fread(buf,size,n,文件指针);
fread()函数的功能是从文件中读取字节长度为size的n个数据,并存放到buf指向的内存地址中去。
函数的返回值为实际读出的数据项个数。比如:
fread(fa,4,5,fp);
其意义是从fp所指向的文件中,每次读4个字节长度(int)送入到fa指向的内存地址中去,连续读5次。也就是说,读5个int类型的数据到fa指向的内存中。
写数据块函数fwrite(),其调用的一般形式为:
fwrite(buf,size,n,文件指针);
fread()函数的功能是将buf中存放的size*n个字节的数据输出到文件指针所指向的文件中去。
函数的返回值为实际写入的数据项个数。
fread()和fwrite()函数一般适用于二进制文件,它们是按数据块的大小来处理输入/输出的。
格式化读/写函数fscanf()和fprintf()
格式化读/写函数与标准的格式输入/输出函数功能相同,只不过它们的读/写对象不是键盘和显示器,而是文件。fscanf()和fprintf()函数只适用于ASCII码文件的读/写。
两个函数的格式如下:
fscanf(文件指针,格式字符串,输入列表);
fprintf(文件指针,格式字符串,输出列表);
fscanf()和fprintf()函数对文件进行读/写,使用方便,容易理解。但由于在输入时需要将ASCII码转换为二进制格式,在输出时又要将二进制格式转换为字符,花费时间较长,所以在内存与磁盘交换数据频繁的时候,最好不要用这两个函数。
文件定位与文件的随机读/写
在C语言中,打开文件时,文件指针指向文件头,即文件的起始位置。在读写文件时,需要从文件头开始,每次读写完一个数据后,文件指针会自动指向下一个数据的位置。但有时不想从文件头开始读取文件,而是读取文件中某个位置的数据。这时,系统提供了定位到某个数据存储位置的函数。
文件头定位函数rewind()
rewind()函数用于把文件指针移动到文件首部,其调用的一般形式为:
rewind(文件指针);
当前读/写位置函数ftell()
ftell()函数用于确定文件指针的当前读/写位置,其调用的一般形式为:
ftell(文件指针);
此函数有返回值,若成功定位,则返回当前位置;否则返回-1。
随机定位函数fseek()
fseek()函数用于将文件指针移动到某个确定的位置,其调用的一般形式为:
fseek(文件指针,位移量,起始点);
此函数有返回值,若成功移动,则返回当前位置;否则返回-1。
其中:位移量指从起始点向前移动的字节数,大多数C版本要求该位移量为long型数据;起始点有三种选择,具体的含义见下表:
起始点 | 表示符号 | 数字表示 |
文件首 | SEEK_SET | 0 |
当前位置 | SEEK_CUR | 1 |
文件尾 | SEEK_END | 2 |
例如,将指针位置移动到距离文件开头100字节处:
fseek(fp,100L,0)
注意:fseek()函数一般用于二进制文件,因为文本文件计算位置往往比较混乱,容易发生错误。
文件检测函数
C语言还提供了一些检测函数,用于在文件打开、关闭以及读/写操作过程中对有可能会发生的一些情况进行检测。
文件结束检测函数feof()
feof()函数用于判断文件是否处于文件结束为止,其调用的一般格式为:
feof(文件指针);
该函数有返回值,如果文件结束,函数的返回值为1;否则返回值为0。
读/写文件出错检测函数ferror()
ferror()函数用于检查文件在使用各种读/写函数时是否出错,其调用的一般格式为:
ferror(文件指针);
该函数有返回值,如果没有错误,函数的返回值为0;否则返回值非0。
文件出错标志清除函数clearerr()
clearerr()函数用于清除出错标志,其调用的一般格式为:
clearerr(文件指针);
在ferror()函数值为非0时,在调用此函数后,ferror()函数的值变为0。