磁盘上的文件就是文件,在程序设计中,从文件功能角度来看,一般谈到的文件有两种:程序文件、数据文件
比如源程序文件、目标文件、可执行文件
单纯存储/输出数据的文件,比如:存放数字、存放字符等,下面要介绍的函数多是处理数据文件的,数据文件又可以分为文本文件和二进制文件
一个文件要有一个唯一的文件标识符,便于计算机/用户识别和引用,文件名包含三个部分:文件路径+文件名主干+文件后缀,比如:C:\limou\test\文档.txt(注意windows的文件分隔符是“\”,而Linux是“/”)
但是不同编译器、系统的文件实现机制可能不太相同,以下是windows环境中VS2013的定义FILE
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
FILE* fopen(const char* filename, const char* mode);
int fclose( FILE* stream );
//测试代码,我在我的桌面放置了一个文件,文件名为:"C:\Users\DELL\Desktop\limou_cache_file_1.txt"
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
int main()
{
FILE* pf = fopen("C:\\Users\\DELL\\Desktop\\limou_cache_file_1.txt", "r");
if (pf == NULL)
{
perror("fopen失败!\n");
return 1;
}
fclose(pf);
pf = NULL;
return 0;
}
int fgetc( FILE* stream );
//如果读取成功返回ASCII码值(字符)并且提升为int,以容纳特殊值EOF(其值为-1)
//如果位置指示符位于文件末尾,该函数返回EOF并设置流的EOF指示符(feof)。
//如果发生其他读取错误,该函数也返回EOF,但设置其错误指示符(ferror)。
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
int main()
{
//打开文件
FILE* pf = fopen("C:\\Users\\DELL\\Desktop\\limou_cache_file_1.txt", "r+");
if (pf == NULL)
{
perror("fopen失败!\n");
return 1;
}
//读取文件
int i = 0;
for (i = 0; i < 26; i++)
{
char ch = fgetc(pf);
printf("%c", ch);
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
int fputc( int character, FILE* stream );
//如果读取成功返回ASCII码值,并且提升为int,以容纳特殊值
//如果发生写入错误,则返回EOF,并设置错误指示器(ferror)
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
int main()
{
//打开文件
FILE* pf = fopen("C:\\Users\\DELL\\Desktop\\limou_cache_file_1.txt", "r+");
if (pf == NULL)
{
perror("fopen失败!\n");
return 1;
}
//写入文件
int i = 0;
for (i = 0; i < 26; i++)
{
fputc('a' + i, pf);
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
char* fgets ( char* str, int num, FILE* stream );//(字符串数组名, 最大的个数字符, 文件指针)
//如果读取成功,返回指向字符串的指针
//如果在读取字符时遇到文件结束符,则设置eof指示符(feof)。如果这种情况发生在读取任何字符之前,则返回的指针是空指针(str的内容保持不变)
//如果发生读错误,则设置错误指示符(ferror)并返回NULL(但str所指向的内容可能已更改)。
#include
#include
int main()
{
//打开文件
FILE* pf = fopen("C:\\Users\\DELL\\Desktop\\limou_cache_file_1.txt", "r+");//这个文件里面我又重新放入一段文本了
if (pf == NULL)
{
perror("fopen失败!\n");
return 1;
}
//读取文件
char arr[150];
for (int i = 0; i < 10; i++)
{
fgets(arr, 150, pf);
printf("%s\n", arr);
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
注意实际上读取num-1个字符,因为保存多一个’\n’
int fputs ( const char* str, FILE* stream );
//如果成功,则返回一个非负值
//在错误时,函数返回EOF并设置错误指示符(ferror)
#include
#include
int main()
{
//打开文件
FILE* pf = fopen("C:\\Users\\DELL\\Desktop\\limou_cache_file_1.txt", "r+");
if (pf == NULL)
{
perror("fopen失败!\n");
return 1;
}
//写入文件
fputs("hello_limou_welcome_file!", pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
同时可以看到之前写的数据被替换了,只是没有"w"那么彻底,因此如果多次使用fputs函数会“覆盖显示”在同一行,除非用户输入的字符串本身带有’\n’字符
int fscanf ( FILE* stream, const char* format, ... );
#include
int main()
{
typedef struct S
{
int a;
double b;
char c[10];
}S;
S s = { 0 };
//打开文件
FILE* pf = fopen("C:\\Users\\DELL\\Desktop\\limou_cache_file_1.txt", "r+");
if (pf == NULL)
{
perror("fopen失败!\n");
return 1;
}
//读文件
fscanf(pf, "%d %f %s", &(s.a), &(s.b), &(s.c));//这里最好加上括号,清晰一点
//打印到屏幕上
printf("%d %lf %s", s.a, s.b, s.c);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
int fprintf ( FILE* stream, const char* format, ... );
#include
int main()
{
//存储输入文件的数据结构体
typedef struct S
{
int a;
double b;
char c[10];
}S;
S s = { 100, 3.1415926, "limou3434" };
//打开文件
FILE* pf = fopen("C:\\Users\\DELL\\Desktop\\limou_cache_file_1.txt", "r+");
if (pf == NULL)
{
perror("fopen失败!\n");
return 1;
}
//写文件
fprintf(pf, "%d %f %s", s.a, s.b, s.c);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
size_t fread ( void* ptr, size_t size, size_t count, FILE* stream );
//返回成功读取的元素总数
//如果这个数字与count参数不同,则要么发生了读取错误,要么在读取时到达了文件末尾。在这两种情况下,都设置了正确的指示器,可以分别使用ferror和feof进行检查。
//如果size或count中有一个为零,函数返回零,流状态和ptr指向的内容都保持不变。
//注意size_t是无符号整型
#include
typedef struct S
{
int a;
double b;
char arr[10];
}S;
int main()
{
//输入数据
S s1 = { 3, 3.14, "abcde" };
FILE* pf = fopen("limou_txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fwrite(&s1, sizeof(S), 1, pf);
//关闭文件
fclose(pf);
pf = NULL;
//输出数据
S s2 = { 0 };
pf = fopen("limou_txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fread(&s2, sizeof(S), 1, pf);
printf("%d %f %s\n", s2.a, s2.b, s2.arr);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
size_t fwrite ( const void* ptr, size_t size, size_t count, FILE* stream );
//返回成功写入的元素总数
//如果此数字与count参数不同,则写入错误阻止函数完成。在这种情况下,将为流设置错误指示器(ferror)
//如果size或count中有一个为零,则函数返回零,错误指示符保持不变
#include
typedef struct S
{
int a;
double b;
char arr[10];
}S;
int main()
{
//输入数据
S s1 = { 3, 3.14, "abcde" };
FILE* pf = fopen("limou_txt", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fwrite(&s1, sizeof(S), 1, pf);
//关闭文件
fclose(pf);
pf = NULL;
//输出数据
S s2 = { 0 };
pf = fopen("limou_txt", "rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
fread(&s2, sizeof(S), 1, pf);
printf("%d %f %s\n", s2.a, s2.b, s2.arr);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
int sscanf ( const char* s, const char* format, ...);
#include
typedef struct S
{
int a;
double b;
char c[10];
}S;
int main()
{
S s1 = { 200,3.14f,"limou3434" };
//转化为字符串数据
char arr[200] = { 0 };
sprintf(arr, "%d %lf %s\n", s1.a, s1.b, s1.c);
printf("%s", arr);
//转化为格式化数据
S s2 = { 0 };
sscanf(arr, "%d %lf %s", &(s2.a), &(s2.b), s2.c);
printf("%d %lf %s\n", s2.a, s2.b, s2.c);
return 0;
}
int sprintf ( char* str, const char* format, ... );
#include
typedef struct S
{
int a;
double b;
char c[10];
}S;
int main()
{
S s1 = { 200,3.14f,"limou3434" };
//转化为字符串数据
char arr[200] = { 0 };
sprintf(arr, "%d %lf %s\n", s1.a, s1.b, s1.c);
printf("%s", arr);
//转化为格式化数据
S s2 = { 0 };
sscanf(arr, "%d %lf %s", &(s2.a), &(s2.b), s2.c);
printf("%d %lf %s\n", s2.a, s2.b, s2.c);
return 0;
}
int fseek( FILE* stream, long int offset, int origin );
//(文件指针, 偏移量, 起始位置)根据文件指针的位置和偏移量来定位文件指针
//其中第三个参数的选择有三种:
//①SEEK_SET,即文件开始位置
//②SEEK_CUR,即文件指针的当前位置
//③SEEK_END,即文件末尾位置
#include
int main()
{
//输入数据
FILE* pf = fopen("limou.txt", "r");//文件指针指向a的地址
if (pf == NULL)
{
perror("fopen");
return 1;
}
//顺序读写
int ch = fgetc(pf);
printf("%c", ch);//打印a,读完后文件指针指向b
ch = fgetc(pf);
printf("%c", ch);//打印b,读完后文件指针指向c
ch = fgetc(pf);
printf("%c", ch);//打印c,读完后文件指针指向d
ch = fgetc(pf);
printf("%c", ch);//打印d,读完后文件指针指向e
//改变偏移量读写
fseek(pf, -3, SEEK_CUR);
ch = fgetc(pf);
printf("%c", ch);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
long int ftell ( FILE* stream );//返回文件指针相对于起始位置的偏移量
#include
int main()
{
//输入数据
FILE* pf = fopen("limou.txt", "r");//文件指针指向a的地址
if (pf == NULL)
{
perror("fopen");
return 1;
}
//顺序读写
int ch = fgetc(pf);
printf("%c", ch);//打印a,读完后文件指针指向b
ch = fgetc(pf);
printf("%c", ch);//打印b,读完后文件指针指向c
ch = fgetc(pf);
printf("%c", ch);//打印c,读完后文件指针指向d
ch = fgetc(pf);
printf("%c", ch);//打印d,读完后文件指针指向e
//改变偏移量读写
fseek(pf, -3, SEEK_CUR);
ch = fgetc(pf);
printf("%c", ch);
//继续顺序读写
ch = fgetc(pf);
printf("%c\n", ch);//打印d,读完后文件指针指向e
//返回偏移量
long int a = ftell(pf);
printf("%d\n", a);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
void rewind ( FILE* stream );//修改当前的文件指针,指向开头位置
#include
int main()
{
//输入数据
FILE* pf = fopen("limou.txt", "r");//文件指针指向a的地址
if (pf == NULL)
{
perror("fopen");
return 1;
}
//顺序读写
int ch = fgetc(pf);
printf("%c", ch);//打印a,读完后文件指针指向b
ch = fgetc(pf);
printf("%c", ch);//打印b,读完后文件指针指向c
ch = fgetc(pf);
printf("%c", ch);//打印c,读完后文件指针指向d
ch = fgetc(pf);
printf("%c", ch);//打印d,读完后文件指针指向e
//改变偏移量读写
fseek(pf, -3, SEEK_CUR);
ch = fgetc(pf);
printf("%c", ch);
//继续顺序读写
ch = fgetc(pf);
printf("%c\n", ch);//打印d,读完后文件指针指向e
//返回偏移量
long int a = ftell(pf);
printf("%d\n", a);
//修改文件指针为开头
rewind(pf);
//再次返回偏移量
a = ftell(pf);
printf("%d\n", a);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
数据在内存中以二进制的形式存储,如果不加转化的输出到外存就是二进制文件
如果对二进制数据加以转换,以ASCII字符的形式存储的文件就是文本文件
两种文件只是看待数据的视角不一样,实际的访问速度和存储空间大小不能简单进行对比
int feof ( FILE* stream );
int ferror ( FILE* stream );
(4)具体代码
#include
int main()
{
//输入数据
FILE* pf = fopen("limou.txt", "r");//文件指针指向a的地址
if (pf == NULL)
{
perror("fopen");
return 1;
}
int ch = 0;
while ((ch = fgetc(pf)) != EOF)
{
printf("%c", ch);
}
//通过feof函数来判断是不是遇到文件末尾结束的
if (feof(pf))
{
printf("到文件结尾\n");
}
else if (ferror(pf))
{
printf("文件读取错误\n");
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
#include
enum
{
SIZE = 5
};
int main()
{
//创建数据
double a[SIZE] = {1.0, 2.0, 3.0, 4.0, 5.0};
FILE *fp = fopen("test.bin", "wb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写入文档
fwrite(a, sizeof *a, SIZE, fp);
//关闭文件
fclose(fp);
//创建存储数据的数组
double b[SIZE];
//打开文件
fp = fopen("test.bin","rb");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读取文件到b数组
size_t ret_code = fread(b, sizeof* b, SIZE, fp);
if(ret_code == SIZE)
{
printf("完全读取成功\n");
for(int n = 0; n < SIZE; ++n)
printf("%f ", b[n]);
putchar('\n');
}
else
{
//如果程序到这里,说明读取异常了
if (feof(fp))//查看是否遇到末尾
{
printf("文件意外结束\n");
}
else if (ferror(fp))//查看是否读取错误,这里还需要判断,所以写出else if
{
perror("读取错误\n");
}
}
//关闭文件
fclose(fp);
}
ANSIC标准采用“缓冲文件系统”处理的数据文件
#include
#include
int main()
{
//打开文件
FILE* pf = fopen("limou.txt", "w");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//输入数据
fputs("hello limou!", pf);//先将代码放在输出缓冲区
//查看文档这个时候是否存储数据
Sleep(15000);//程序停止运行15秒,这个时候可以打开limou.txt文件来看看是否将数据存入了。可以看到,文件并没有存储数据,因为这段数据还在缓存区域中
//刷新缓冲
fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)注意,fflush在高版本的VS上不能使用了,除了这个函数还有其他的方法可以清空缓存区
//查看文档这个时候是否存储数据
Sleep(10000);
//关闭文件
fclose(pf);//注意,fclose在关闭文件的时候,也会刷新缓冲区
pf = NULL;
return 0;
}
在上面的某一段代码中出现了两个宏