1、C文件概述C语言把文件看作一个字符的序列,即由一个一个字符(字节)的数据顺序组成。根据数据的组织形式,可分为ASCII文件和二进制文件。ASCII文件有称为文本(text)文件,它的每个字节存放一个ASCII代码,代表一个字符。二进制文件是把内存中的数据按其在内存中的存储形式原样输出。如果一个数10000,在内存中占2个字节。如果按照ASCII存储则占5个字节,如按二进制存储则占2字节。如下:二进制形式内存中样式ASCII形式
00100111 000100000100111 000100000110001 ----> 1
00110000 ----> 0
00110000 ----> 0
00110000 ----> 0
00110000 ----> 0
由前述,一个C文件是一个字符流或二进制流。在C语言中文件的存取以字符(字节)为单位。输入输出的数据流的开始和结束仅受程序控制而不受物理符号(如回车换行符)控制。也就是说,在输出时不会增加回车换行符作为记录结束的标志,输入时不以回车换行符作为间隔。这种文件称为流式文件。
在过去使用C版本有两种对文件的处理方式:a、缓冲文件系统 b、非缓冲文件系统。缓冲文件系统在每次向文件读写时都进过缓冲区。
2、文件类型指针缓冲文件系统中,关键的概念是“文件指针”。每个被使用的文件在内存中开辟一个区,用来存放文件的有关信息。这些信息保存在一个结构体变量中。该结构体有系统定义的,取名为FILE。在Turbo C中stdio.h对文件定义如下:typedef struct
{
short level; /* 缓冲区“满”或“空”的程度 */
unsigned flags; /* 文件状态标志 */
char fd; /* 文件描述符 */
unsigned char hold; /* 如无缓冲区不读去字符 */
short bsize; /* 缓冲区的大小 */
unsigned char *buffer; /* 数据缓冲区的位置 */
unsigned ar *curp; /* 指针,当前的指向 */
unsigned istemp; /* 临时文件,指示器 */
short token; /* 用于有效性检查 */
} FILE;
有了文件FILE结构体类型后,就可以定义文件类型的变量和指针变量。如:
FILE f[5] 定义一个文件数组
FILE *fp 定义一个文件指针
3、文件的打开和关闭ANSI C规定了标准输入输出函数库,用fopen()函数来实现打开文件。fopen函数调用方式通常为:FILE * fp;
fp = fopen(文件名, 使用文件方式);文件使用方式含义
“r”(只读)为输入打开一个文本文件
“w”(只写)为输出打开一个文本文件
“a”(追加)向文本文件尾添加数据
“rb”(只读)为输入打开一个二进制文件
“wb”(只写)为输出打开一个二进制文件
“ab”(追加)向二进制文件末尾添加数据
“r+”(读写)为读写打开一个文本文件
“w+”(读写)为读写建立一个新的文本文件
“a+”(读写)为读写打开一个文本文件
“rb+”(读写)为读写打开一个二进制文件
“wb+”(读写)为读写建立一个新的二进制文件
“ab+”(读写)为读写打开一个二进制文件
说明:
(1)用"r"打开的文件只能用于向计算机输入文件,而且该文件已经存在。不能打开不存在的文件,否则会报错。
(2)用"w"打开的文件只能用于输出文件。如果不存在文件,则在打开是新建一个以指定名称的文件。如果存在,则删除存在的文件,然后重新建立一个新文件。
(3)如果使用"a"方式打开则可以向文件末尾追加数据,但是该文件必须已经存在。打开时,位置指针移动到文件末尾。
(4)使用"r+"、"w+"、"a+"方式打开的文件仅可以读也可以写。用“r+”打开的文件必须存在,用"w+"删除旧文件,重新建立新文件。用"a+"方式打开不会删除文件。
(5)如果打开文件失败,fopen()函数返回一个NULL(NULL在stdio.h文件中定义为0)。如:if((fp = fopen("file1", "r")) == NULL)
{
printf("不能打开这个文件\n");
exit(0);
}
(6)在向计算机输入文本文件时,回车换行符被替换成一个换行符,输出时转换成回车换行符。二进制文件,则原样保存。
(7)在程序开始运行时,系统自动打开3个标准输入输出。stdin标准输入、stdout标准输出、stderr标准出错输出。
常用函数简介:
1、fclose(文件指针)函数用于关闭文件,关闭成功返回0。否则返回EOF(-1)。如:fclose(fp);
2、fputc(输出字符,文件指针)函数把一个字符写到磁盘上去。如:fputc(ch,fp)表示将ch字符输出到fp指向的文件中去。输出成功返回输出的字符,否则返回EOF
3、putchar函数是从fputc函数派生而来。#define putchar(c) fputc(c, stdout)
4、fgetc(文件指针)函数从指定的文件中读取一个字符,该文件必须以读或读写方式打开的。如:ch = fgetc(fp);从fp指定的文件中取出一个字符,赋给ch变量。ch = fgetc(fp);
while( ch != EOF)
{
putchar( ch );
ch = fgetc( fp);
}
以上是将一个文件以顺序显示在屏幕上。
注意:
EOF不是可输出字符,因为ASCII码不可能出现-1。当读取文件等于-1(EOF)时,表示读入的已不是正常字符而是文件结束符。但是值适合于文本文件。当读取二进制文件时,读取的数据可能为-1,因此ANSI C提供了feof(文件指针)函数来判断文件是否已经结束。如果结束,feof(文件指针)返回值为1(真),否则为0(假);
如:while( !feof(fp) )
{
c = fgetc( fp );
}
可以顺利的读取一个二进制文件
【实例】从键盘输入一个字符串,逐个把他们写到磁盘文件上,知道输入‘#’为止。#include
#include
void main()
{
FILE * fp;
char ch, filename[10];
scanf("%s", filename); // 提示用户输入文件名
if((fp = fopen(filename, "w")) == NULL)
{
printf("不能打开文件\n");
exit(0); // 终止程序
}
ch = getchar(); // 用来接收在执行scanf语句时最后输入的回车符
ch = getchar(); // 接受输入的第一个字符
while(ch != '#')
{
fputc(ch, fp); // 写入到文件中去
putchar(ch);
ch = getchar();
}
putchar(10); // 向屏幕输出一个换行符
fclose(fp);
}
【实例】将文件中的数据输出到屏幕上#include
#include
void main()
{
FILE *in;
char ch, infile[10];
printf("请输入文件名:\n");
scanf("%s", infile);
if( (in = fopen(infile, "r")) == NULL)
{
printf("打开输入文件失败\n");
exit(0);
}
// 将文件中的数据输出到屏幕上
while( !feof(in))
{
putchar(fgetc(in));
}
// 关闭文件
close(in);
}
【实例】将一个文件的数据写入到另一个文件中去。#include
#include
void main()
{
FILE *in, *out;
char ch, infile[10], outfile[10];
printf("请输入被拷贝文件名:\n");
scanf("%s", infile);
printf("请输入拷贝文件名:\n");
scanf("%s", outfile);
// 打开输入文件
if( (in = fopen(infile, "r")) == NULL)
{
printf("打开输入文件失败\n");
exit(0);
}
// 打开输出文件
if( (out = fopen(outfile, "w")) == NULL)
{
printf("打开输出文件失败\n");
exit(0);
}
// 将输入文件的数据写入到输出文件中去
while( !feof(in))
{
fputc(fgetc(in), out);
}
// 关闭文件
close(in);
close(out);
}
对文件之间拷贝的改进:#include
#include
void main(int argc, char * argv[]) // argc-表示参数argv数组的长度 argv-表示参数
{
FILE *in, *out;
char ch;
if(argc != 3) // argv[0]-可执行文件名 argv[0]-输入文件名 argv[0]-输出文件名
{
printf("你忘记输入文件名");
exit(0);
}
if( (in = fopen(argv[1], "r")) == NULL)
{
printf("打开输入文件失败\n");
exit(0);
}
if( (out = fopen(argv[2], "w")) == NULL)
{
printf("打开输出文件失败\n");
exit(0);
}
// 将文件中的数据输出到屏幕上
while( !feof(in))
{
fputc(fgetc(in), out);
}
// 关闭文件
close(in);
}
运行方式:
C:\> fileExe text.txt new.txt
5、fread(buffer, size, count, fp) 和 fwrite(buffer、size、count、fp)
参数介绍:
buffer 要读入/写出存放数据的地址(起始地址)
size 要去读的字节数
count 要读写多少个size字节的数据
fp 文件指针
如:fread(f,4,2,fp)表示从fp中读取2个4字节的数据到数组f中
如果存在一下学生信息结构体:struct student_type
(
char name[20]; -- 学生姓名
int num; -- 学号
int age; -- 年龄
char addr[30]; -- 居住地址
)stud[40];
我们可以使用一下语句读取和写出数据。for(i = 0; i
{
fread(&stud[i], sizeof(struct student_type), 1, fp);
}
for(i = 0; i
{
fwrite(&stud[i], sizeof(struct student_type), 1, fp);
}
如果调用成功放回count的值。
【实例】从键盘输入4个学生的有关数据,然后将他们存到磁盘文件上。#include
#include
#define SIZE 4
struct student_type
{
char name[20]; // 姓名
int num; // 学号
int age; // 年龄
char addr[15]; // 地址
} stud[SIZE];
void save()
{
FILE *fp;
int i;
if((fp = fopen("student_list","wb")) == NULL)
{
printf("打开student_list文件失败\n");
return;
}
for(i=0; i
{
if(fwrite(&stud[i], sizeof(struct student_type), 1 , fp) != 1)
{
printf("写入错误\n");
}
}
fclose(fp);
}
void main( )
{
int i;
for(i = 0; i
{
scanf("%s%d%d%s", stud[i].name, &stud[i].num, &stud[i].age, stud[i].addr);
}
save();
}
【实例】将上面写入的数据读取到屏幕上面#include
#include
#define SIZE 4
struct student_type
{
char name[20];
int num;
int age;
char addr[15];
} stud[SIZE];
void main()
{
int i;
FILE *fp;
fp = fopen("student_list", "rb");
for(i = 0; i
{
fread(&stud[i], sizeof(struct student_type), 1, fp);
printf("%-10s%4d%4d%-15s\n", stud[i].name, stud[i].num, stud[i].age, stud[i].addr);
}
fclose(fp);
}
注意:fread()和fwrite()一般用于二进制文件的输入输出。因为他们是按照数据块的长度来处理输入输出的,在字符串发生转换的情况下很有可能出现与原设想不同的情况。
【实例】将一个已经存在学生基本信息的文件加载到内存,然后写入到新的文件中去。#include
#include
#define SIZE 4
struct student_type
{
char name[20];
int num;
int age;
char addr[15];
} stud[SIZE];
/**
* 将student_list文件中保存的学生信息加载到内存中
*/
void load()
{
FILE *fp;
int i;
if((fp = fopen("student_list","rb")) == NULL)
{
printf("打开student_list文件失败\n");
exit(0);
}
for(i=0; i
{
if(fread(&stud[i], sizeof(struct student_type), 1, fp) != 1)
{
if(feof(fp))
{
fclose(fp);
return;
}
printf("\n");
}
}
}
/**
* 将加载到内存的数据保存到new_student_list文件中
*/
void save()
{
FILE *fp;
int i;
if((fp=fopen("new_student_list", "wb")) == NULL)
{
printf("打开new_student_list文件失败\n");
exit(0);
}
for(i=0; i
{
if(fwrite(&stud[i], sizeof(struct student_type), 1, fp) != 1)
{
printf("向new_student_list文件写入数据失败\n");
}
}
fclose(fp);
}
void main()
{
load();
save();
}
6、fprintf( )和fscanf( )函数
这两个函数与printf和scanf函数作用相仿,都是格式化读写函数。只有一点不同:fprintf和fscanf函数的读写对象不是终端而是磁盘文件。它们的一般格式为:
fprintf(文件指针、格式化字符串、输出列表)
fscanf(文件指针、格式化字符串、输入列表)
如:fprintf(fp, "%d,%6.2f", i, c) 写到磁盘文件
fscanf(fp, "%d,%f", &i, &t) 磁盘文件上存在:3,4.5
【实例】将0~9十个数字写入到number.txt文件中#include
#include
void main()
{
FILE *fp;
int i;
if((fp = fopen("number.txt", "w")) == NULL)
{
printf("不能打开文件\n");
exit(0);
}
// 向number.txt文件循环写入0~9数字
for(i=0; i<10; i++){
fprintf(fp, "%d", i);
}
fclose(fp);
}
【实例】从磁盘文件读取数据,存到变量中,然后将变量输出到屏幕上。#include
#include
void main()
{
FILE *fp;
int a;
float b;
if((fp = fopen("number.txt", "r")) == NULL)
{
printf("不能打开文件\n");
exit(0);
}
fscanf(fp, "%d,%f", &a, &b); // 20,25.4
printf("a=%d, b=%f\n", a, b); // a=20, b=25.4
fclose(fp);
}
用fprintf或fscanf对磁盘文件进行读写,使用方便,容易理解。但由于在输入时要将ASCII码转换成二进制格式,在输出时要将二进制转换成字符,花费时间较多。因而,在内存与磁盘交换频繁时,最好不要用fprintf和fscanf函数,而用fread和fwrite函数。