一、C 文件基本知识
1. 文件与文件分类
文件 (flie) 个别是指 存储在内部介质 (如磁盘) 上的数据的汇合。操作系统是以文件为单位对数据进行治理的。文件有不同类型,C 语言中次要用到两种文件:
- 程序文件:包含源程序文件
.c
、指标文件.obj
、可执行文件.exe
。文件的内容是程序代码。 - 数据文件:文件的内容是供程序运行时读写的数据。
咱们次要探讨的是数据文件。
C 语言把文件看作一个字符 (或字节) 的序列,即由一个一个字符 (或字节) 的数据程序组成。一个输入输出流就是一个字符流或字节 (内容为二进制数据) 流。C 的数据文件由一连串的字符 (或字节) 组成,对文件的存取是以字符 (字节) 为单位的。输入输出数据流的开始和完结仅受程序控制而不受物理符号 (如回车换行符) 管制,这就减少了解决的灵活性。这种文件称为 流式文件。
一个文件要有惟一的文件标识,以便辨别和援用,这就是 文件名。文件标识包含 3 局部:①文件门路;②文件名骨干;③文件后缀。如:
D:\cProgram\temp\file1.dat
工具数据的组织模式,将数据文件分为 ASCII 文件 和二进制文件 。数据在内存中是以二进制模式存储的,如果不加转换地输入到外存,就是二进制文件,能够认为它就是存储在内存的数据的映像,所以也称之为 映像文件(image file)。如果要求在外存上以 ASCII 代码模式存储,则须要在存储前进行转换。ASCII 文件又称文本文件(text file),每一个字节寄存一个字符的 ASCII 代码。
用 ASCII 码模式输入时字节与字符一一对应,一个字节代表一个字符,因此便于对字符进行一一解决,也便于输入字符。但个别占存储空间较多,而且要花费转换工夫 (二进制模式与 ASCII 码间的转换)。用二进制模式输入数值,能够节俭外存空间和转换工夫,把内存中的存储单元中的内容一成不变地输入到磁盘(或其余内部介质) 上,此时每一个字节并不一定代表一个字符。如果程序运行过程中有的两头数据须要保留在内部介质上,以便在须要时再输人到内存,个别用二进制文件比拟不便。在事务管理中,常有少量数据寄存在磁盘上,随时调人十算机进行查问或解决,而后又把批改过的信息再存回磁盘,这时也罕用二进制文件。
2. 文件缓冲区
ANSI C 规范采纳“缓冲文件系统”解决数据文件,所谓缓冲文件系统是指零碎主动地在内存区为程序中每一个正在应用的文件开拓一个文件缓冲区。从内存向磁盘输入数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。如果从磁盘向计算机读入数据,则一次从磁盘文件将一批数据输人到内存缓冲区(充斥缓冲区),而后再从缓冲区一一地将数据送到程序数据区(给程序变量)。这样做是为了节俭存取时间,提高效率,缓冲区的大小由各个具体的 C 编译系统确定。
3. 文件类型指针
缓冲文件系统中,要害的概念是“文件类型指针 ”,简称 文件指针。每个被应用的文件都会在内存中开拓一个相应的文件信息区,用来寄存文件的无关信息,这些信息是保留在一个零碎申明的构造体变量 FILE
中。例如,一种 C 编译环境提供的 stdio.h
头文件中有以下文件类型申明:
typedef struct {
short level; // 缓冲区“满”或“空”的长度
unsigned flags; // 文件状态标记
char fd; // 文件描述符
unsigned char hold; // 如缓冲区无内容不读取字符
short bsize; // 缓冲区大小
unsigned char* buffer; // 数据缓冲区的地位
unsigned char* curp; // 文件地位标记指针以后的指向
unsigned istemp; // 临时文件指示器
short token; // 用于无效查看
} FILE;
以上申明 FILE 构造体类型的信息蕴含在头文件 stdio.h
中。在程序中能够间接用 FILE
类型名定义变量。每一个 FILE
类型变量对应一个文件的信息区,在其中寄存该文件的无关信息。例如:
FILE fl;
以上定义了一个构造体变量 f1
,用它来寄存一个文件的无关信息。这些信息是在关上一个文件时由零碎依据文件的状况主动放人的,在读写文件时须要用到这些信息,也会批改某些信息。例如在读一个字符后,文件信息区中的地位标记指针的指向就要扭转。个别不定义 FILE
类型的变量命名,也就是不通过变量的名字来援用这些变量,而是设置一个指向 FILE
类型变量的指针变量,而后通过它来援用这些 FILE
类型变量。这样应用起来不便。如:
FILE* fp;
定义 fp
是一个指向 FILE
类型数据的指针变量。能够使 fp
指向某一个文件的文件信息区(是一个构造体变量),通过该文件信息区中的信息就可能拜访该文件。
留神:指向文件的指针变量并不是指向内部介质上的数据文件的结尾,而是指向内存中文件信息区的结尾。
二、关上和敞开文件
文件读写之前要先关上,应用完结后要及时敞开。所谓“关上”是指为文件建设相应的休息区和文件缓冲区。编写程序时,关上文件的同时个别都指定一个指针变量指向该文件,通过指针变量对文件进行读写。所谓“敞开”就是撤销文件休息区和文件缓冲区,是指针变量不在指向该文件。
1. 用 fopen
函数关上数据文件
ANSI C 规定用规范输入输出函数 fopen
来实现关上文件,其调用形式为:
fopen(文件名, 应用文件形式);
fopen
函数的返回值是一个指向该文件的指针变量。如果关上出错,则返回空指针 NULL
。如:
FILE* fp;
fp = fopen("afile", "r");
应用文件形式包含:
文件应用形式 | 含意 | 如果指定文件不存在 |
---|---|---|
r 只读 |
为了输出数据关上一个已存在的文本文件 | 出错 |
w 只写 |
为了输入数据关上一个文本文件 | 建设新文件 |
a 追加 |
向文本文件尾增加数据 | 出错 |
rb 只读 |
为了输出数据关上一个二进制文件 | 出错 |
wb 只写 |
为了输入数据关上一个二进制文件 | 建设新文件 |
ab 追加 |
向二进制文件尾增加数据 | 出错 |
r+ 读写 |
为读写关上一个文本文件 | 出错 |
w+ 读写 |
为读写建设一个新的文本文件 | 建设新文件 |
a+ 读写 |
为读写关上一个文本文件 | 出错 |
rb+ 读写 |
为读写关上一个二进制文件 | 出错 |
wb+ 读写 |
为读写建设一个新的二进制文件 | 建设新文件 |
ab+ 读写 |
为读写关上一个二进制文件 | 存储 |
2. 用 fclose
函数敞开数据文件
flose
函数敞开文件的个别模式为:
fclose(文件指针);
当胜利执行了敞开操作,该函数返回值为 0,否则为 EOF(-1)。
三、程序读写数据文件
1. 向文件读写字符
C 语言提供两个函数对文本文件读写单个字符:
函数名 | 调用模式 | 性能 | 返回值 |
---|---|---|---|
fgetc |
fgetc(fp) |
从 fp 指向的文件读入有一个字符 |
读胜利则返回所读字符,失败则返回文件完结标记 EOF (即 -1) |
fputc |
fputc(ch, fp) |
把字符 ch 写到 fp 指向的文件中 |
输入胜利则返回输入字符,失败则返回文件完结标记 EOF (即 -1) |
上面的例子实现了从键盘一一输出字符并写入到磁盘文件中,晓得输出一个 #
:
#include <bits/types/FILE.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE* fp; // 定义文件指针
char ch, filename[10];
scanf("%s", filename); // 输出文件名
getchar(); // 消化回车符
if ((fp = fopen(filename, "w")) == NULL) { // 关上文件
printf("Can not open file!");
exit(0);
}
ch = getchar(); // 输出第一个字符
while (ch != '#') {fputc(ch, fp); // 向磁盘文件按输入字符
putchar(ch);
ch = getchar();}
fclose(fp);
putchar(10);
return 0;
}
在拜访磁盘文件时,是一一字符 (字节) 进行的,为了晓得以后拜访到第几个字节,零碎用“文件读写地位标记”来示意以后所拜访的地位。开始时“文件读写地位标记”指向第 1 个字节,每拜访完一个字节后,以后读写地位就指向下一个字节,即以后读写地位主动后移。为了晓得对文件的读写是否实现,只须看文件读写地位是否移到文件的开端。文件尾示意用标识符 EOF
(end of file)示意,EOF
在 stdio.h
中被定义为 -1。
用 feof
函数能够检测文件尾标记是否已被读取过。如果文件尾标记已被读出,则示意文件已完结,此时 feof
函数值为真(以 1 示意),否则 feof
函数值为假(以 0 示意)。
2. 向文件读写字符串
C 语言容许应用两个函数读写一个字符串:
函数名 | 调用模式 | 性能 | 返回值 |
---|---|---|---|
fgets |
fgets(str, n, fp) |
从 fp 指向的文件读入有一个长度为 n-1 的字符串,寄存到字符数组 str 中 |
读胜利则返回地址 str ,失败则返回 NULL |
fputs |
fputs(str, fp) |
把 str 所指向的字符串写到 fp 指向的文件中 |
输入胜利则返回 0,失败则返回非 0 值 |
上面的例子实现了从键盘读入若干个字符串,依照字母大小排序后保留到磁盘文件中:
#include <bits/types/FILE.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
FILE* fp;
char str[3][10], temp[10];
int i, j, k, n = 3;
for (i = 0; i < n; i++)
scanf("%s", str[i]);
for (i = 0; i < n - 1; i++) {
k = i;
for (j = i + 1; j < n; j++) {if (strcmp(str[k], str[j]) > 0)
k = j;
}
if (k != i) {strcpy(temp, str[i]);
strcpy(str[i], str[k]);
strcpy(str[k], temp);
}
}
if((fp=fopen("file.txt", "w"))==NULL) {printf("Can not open file!");
exit(0);
}
for(i=0;i<n;i++){fputs(str[i], fp);
fputs("\n", fp);
printf("%s\n", str[i]);
}
return 0;
}
3. 格式化读取文本文件
应用 fprintf
和 fscanf
函数能够实现对文件的格式化输入输出:
fprintf(文件指针, 格式化字符串, 输出表列);
fscanf(文件指针, 格式化字符串, 输出表列);
如:
fprintf(fp, "%d, %6.2f", i, f);
fscanf(fp, "%d, %f", &i, &f);
用这两个函数对磁盘文件读写,使用方便,易于了解,但因为在输出时要将文件中的 ASCII 码转换为二进制模式再保留在内存变量中,在输入时又要将内存中的二进制模式转换成字符,要花费较多工夫。因而,在内存与磁盘频繁替换数据的状况下,最好不必 fprintf
和 fscanf
函数,而用上面的 fread
和 fwrite
函数进行二进制的读写。
4. 二进制形式读写数据
在程序中常须要一次输入输出一组数据(如数组或构造体变量的值),C 语言应用 fread
函数从文件中读取一个数据块,用 fwrite
函数向文件写一个数据块。在读写时是以二进制模式进行的,因而操作的文件需以二进制模式关上。它们的个别模式为:
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
其中:
buffer
:地址。对fread
来说是用来寄存从文件读入的数据的存储地址;对fwrite
来说是把该地址开始的存储区中的数据向文件输入。size
:要读写的字节数。count
:要读写的数据项的个数,每个数据项长度为size
。fp
:FILE
类型的指针。
上面的例子实现了从键盘上输出 10 组数组并转存到磁盘文件上:
#include <stdio.h>
#define SIZE 10
struct Student {char name[10];
int num;
int age;
} stud[SIZE];
void save() {
FILE* fp;
int i;
if ((fp = fopen("stu.dat", "wb")) == NULL) {printf("Can not open file!\n");
return;
}
for (i = 0; i < SIZE; i++) {if (fwrite(&stud[i], sizeof(struct Student), 1, fp) != 1)
printf("File write error!\n");
}
fclose(fp);
}
int main() {
int i;
for (i = 0; i < SIZE; i++)
scanf("%s %d %d", stud[i].name, &stud[i].num, &stud[i].age);
save();
return 0;
}
四、随机读写数据文件
随机拜访不是按数据在文件中的物理地位秩序进行读写,而是能够对任何地位上的数据进行拜访,显然这种办法比程序拜访效率高得多。
零碎为每一个文件设置了一个文件读写地位标记,用来批示接下来要读写的字符的地位。如果是程序写文件,则每写完一个数据后,文件地位标记程序向后移一个地位,而后在下一次执行写操作时把数据写人地位标记所指的地位。直到把全副数据写完,此时文件地位标记在最初一个数据之后。
能够依据读写的须要,人为地挪动文件地位标记的地位。文件地位标记能够向前移、向后移,而后对该地位进行读写。从而实现在任何地位写人数据,在任何地位读取数据。
强制文件地位标记指向特定地位,能够应用以下函数实现:
- 用
rewind
函数使文件地位标记指向文件结尾,此函数无返回值。 -
用
fseek
函数扭转文件地位标记,其调用模式为:fseek(文件类型指针, 位移量, 起始点);
“起始点”用 0,1 或 2 代替,0 代表文件开始地位,1 为以后地位,2 为文件开端地位。C 规范指定的名字如下表:
起始点 名字 用数字代表 文件开始地位 SEEK_SET 0 文件以后地位 SEEK_CUR 1 文件开端地位 SEEK_END 2 “位移量”是指以“起始点”为基点,向前挪动的字节数,应是一个
long
型数据。该函数个别用于二进制文件。 - 用
ftell
函数测定文件地位标记以后的地位,用绝对于文件结尾的位移量来示意。如果调用函数时出错,则返回-1L
。
五、文件读写的出错检测
C 提供一些函数来查看输入输出函数调用时可能呈现的谬误:
-
ferror
函数:在调用各种输入输出函数 (如putc
,getc
,fread
和fwrite
等) 时,如果呈现谬误,除了函数返回值有所反映外,还能够用ferror
函数查看。它的个别调用模式为:ferror(fp);
如果返回值为 0(假),示意未出错;如果返回一个非零值,示意出错。对同一个文件每一次调用输入输出函数,都会产生一个新的
ferror
函数值,因而,该当在调用一个输入输出函数后立刻查看ferror
函数的值,否则信息会失落。在执行fopen
函数时,ferror
函数的初始值主动置为 0。 clearerr
函数:该函数作用是使文件出错标记和文件完结标记置为 0。假如在调用一个输入输出函数时产生谬误,ferror
函数的值为一个非零值。应该立刻调用clearerr(fp)
使clearerr(fp)
值变为 0,以便再进行下一次的检测。只有呈现文件读写出错标记,它就始终保留,直到对同一文件调用clearerr
函数或rewind
函数,或任何其余一个输入输出函数。