文件操作(上)
文章目录
前言
我们在编写程序时肯定会遇到要将数据写入一个文件保存或者导入一个文件数据的情况,这个时候我们就要掌握文件的相关操作才可以达到目的,本篇博客就将针对文件的一些操作进行讲解,希望对大家有所帮助。
一、文件的概述
1.什么是文件?
磁盘上的文件是文件。
但是在程序设计中,我们一般谈的文件有两种:程序文件、数据文件(从文件功能的角度来分类的)。
1.1程序文件
包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)
1.2数据文件
文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。
我们之前处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上,其实我们很多时候需要将数据存储到磁盘,当需要使用这些数据时,将磁盘的数据读取到内存中使用,这里处理的就是磁盘上的文件。
1.3文件名
一个文件要有一个唯一的文件标识,以便用户识别和引用。
文件名包含3部分:文件路径+文件名主干+文件后缀
例如: c:\code\test.txt
为了方便起见,文件标识常被称为文件名
二、文件的打开和关闭
2.1文件指针
缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息是保存在一个结构体变量中的。该结构体类型是有系统声明的,取名FILE。
例如,VS2013编译环境提供的 stdio.h 头文件中有以下的文件类型申明:
struct _iobuf {
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
这个结构体就是FILE型的结构体。
我们现在使用的编译器多种多样,不同的编译器可能FILE类型包含的内容不完全相同但是大同小异,我们可以不用去关心这点。
同时我们使用的FILE的指针来维护FILE结构的变量,这样使用会非常的得心应手。
2.1.1创建一个FILE*指针变量
FILE* pf;//文件指针变量
定义pf就是一个文件指针变量,pf就会指向一个文件的文件信息区,通过该文件信息区中的信息就能够访问该文件。也就是说,通过文件指针变量能够找到与它关联的文件,并且对文件进行操作。
ps:这个文件信息区本质就是一个结构体变量。
2.2文件的打开
我们在使用文件之前肯定第一步要打开文件,在打开文件的同时会返回一个FILE*型的指针变量指向这个文件,相当于建立了指针和文件之间的关系。
ANSIC 规定使用fopen函数来打开文件。
代码如下(示例):
//打开文件
FILE * fopen ( const char * filename, const char * mode )
如果打开文件成功会返回一个FILE*型的指针,如果打开失败就会返回一个NULL。
ANSIC 规定使用fclose函数来打开文件。
int fclose ( FILE * stream );
范例:
#include <stdio.h>
int main ()
{
//打开文件
FILE* pf = fopen ("myfile.txt","w");//只写模式,如果原文件有内容则会清零。
//文件操作
if (pf==NULL)
{
perrof("fopen");
//关闭文件
fclose (pf);
}
return 0;
}
最后介绍一个很抽象的概念:流。
流是一个高度抽象的概念,我们程序需要操作电脑的很多硬件,比如屏幕,硬盘,U盘等,然而这些不同硬件的读取方式是不同的,我们就在程序和硬件之间抽象出一个流的层次,程序作用在流中,然后流将数据写入到对应的地方。
其实我们相当于直接向流里写数据。
三、文件的顺序读写
3.1.fputc函数
fputc函数的作用是写一个字符到一个流里面去或者说写到一个标准输出上去,这个流指的就是文件流。
代码如下(示例):
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* pf = fopen("myFILE.dat", "w");
if (pf == NULL)
{
perror("fopen");
}
fputc('a', pf);
fclose(pf);
pf = NULL;
return 0;
}
这里是对文件进行"w"(只写)操作,如果原来就存在这个文件就会将这个文件清零然后写入,如果没有找到这个文件就会自动创建一个文件然后进行写入,我们用fputc函数写入了一个字符放在myFILE.dat文件中。然后同时利用了perror函数进行检查,如果fopen文件失败的话就会报错,同时指出是在fopen环节出现了问题。
3.2.fgetc函数
fgetc函数的作用是读一个字符从文件流或者标准输入流中。
fgetc函数读取到字符后返回的是该字符的ASCII码值作为一个整数,如果读取失败或者遇到文件结束的时候就会返回一个EOF。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* pf = fopen("myFILE.dat", "r");
if (pf == NULL)
{
perror("fopen");
}
int ret=fgetc(pf);
printf("%c\n",ret);
fclose(pf);
pf = NULL;
return 0;
}
ps:我们得到的是一个字符为什么ret要用int类型的呢?
因为如果失败的话会返回一个EOF,EOF是-1,所以需要用一个有符号整型来接收。
3.3fputs函数
fputs函数是文本输出函数,适用于所有的输出流,
fputc是一个字符一个字符的读,fputs可以一行一行的读。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE* file = fopen("a.txt", "w");
if (file == NULL) {
printf("open file failed\n");
exit(-1);
}
char buf[] = "abcd";
while (1) {
fputs(buf, file);
}
fclose(file);
return 0;
}
3.3fgets函数
文本输入函数,适用于所有的输入流。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* pf = fopen("myFILE.dat", "r");
if (pf == NULL)
{
perror("fopen");
}
int ret=fgets(pf);
printf("%s\n",ret);
fclose(pf);
pf = NULL;
return 0;
}
同时fgets函数还可以指定读取多少个字符。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
char arr[] = {
0 };
FILE* pf = fopen("a.txt", "r");
if (pf == NULL)
{
perror("fopen");
}
fgets(arr, 6, pf);
printf("%s\n", arr);
fclose(pf);
pf = NULL;
return 0;
}
虽然我们写的是读取6个字符,但是实际上读取不了6个字符,最多读取5个,因为还有留一个位置给\0,然后如果还需要读取接着第一次继续读取。
3.4比较scanf,fscanf,sscanf函数
scanf函数和fscanf函数相比:
fscanf比scanf多了一个参数,也就是在scanf使用的基础上多传入了一个流。
fscanf是格式化输入函数。
代码示例如下:
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct S
{
char arr[10];
int num;
float sc;
};
int main()
{
struct S s = {
0};
FILE* pf = fopen("a.txt", "r");
if (pf == NULL)
{
perror("fopen");
}
fscanf(pf, "%s,%d,%f", s.arr, &(s.num),&( s.sc));
fclose(pf);
pf = NULL;
return 0;
}
3.5比较printf,fprintf,sprintf函数
形式参数中的…表示可变参数,
printf函数和fprintf函数相比:
fprintf函数比printf函数多了一个参数,也就是在使用printf函数的基础上多传入了一个流。fprintf是格式化输出函数。
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct S
{
char arr[10];
int num;
float sc;
};
int main()
{
struct S s = {
"abcdef",10,5.5f };
FILE* pf = fopen("a.txt", "w");
if (pf == NULL)
{
perror("fopen");
}
//写文件
fprintf(pf, "%s,%d,%f", s.arr, s.num, s.sc);
fclose(pf);
pf = NULL;
return 0;
}
3.6fwrite函数
fwrite函数是二进制输出函数。
代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct S
{
char arr[10];
int num;
float sc;
};
int main()
{
struct S s = {
"abcdef",10,5.5f };
FILE* pf = fopen("a.dat", "w");
if (pf == NULL)
{
perror("fopen");
return;
}
fwrite(&s,sizeof(struct S),1,pf);
fclose(pf);
pf = NULL;
return 0;
}
这个函数是以二进制的形式写进文件的,所以我们是看不懂的,但是计算机看得懂,如果我们想看到数据也有其他办法。
3.7fread函数
fread函数是二进制输入函数。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct S
{
char arr[10];
int num;
float sc;
};
int main()
{
struct S s = {
0 };
FILE* pf = fopen("a.dat", "r");
if (pf == NULL)
{
perror("fopen");
return;
}
fread(&s, sizeof(struct S), 1, pf);
printf("%s %d %f", s.arr, s.num, s.sc);
fclose(pf);
pf = NULL;
return 0;
}
总结
本篇博客主要讲了C语言文件的一些基础操作,包括什么是文件,文件的分类,文件指针的讲解,流的解释,文件的顺序读写等知识,还有一些文件的操作例如文件的随机读写将会在下一篇博客和大家分享,我们明天再见啦~