C语言文件操作

C语言文件操作

文件基础

在程序设计中,一般涉及的文件分为两种,程序文件和数据文件,其中程序文件包括源程序文件(.c),目标文件(.obj)、可执行程序(.exe)等;数据文件指代的文件内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,或者输出内容的文件。根据数据文件的组织形式,进而可将数据文件划分为文本文件和二进制文件两种。

文本文件:以ASCII码编写的可视化的字符型文件,不同的文字以特殊编码存在,如GBK、utf-8等。
二进制文件:直接以原生二进制码存放的文件,如BMP等图片文件,MP3等音频文件,exe等可执行文件。

数据流:一组有序、有起点和终点的字节的数据序列,程序和数据的交互是以流的形式进行的。

标准 IO 及缓冲区
在这里插入图片描述
ANSIC标准采用“缓冲文件系统”处理数据文件,所谓缓冲文件系统是指系统自动的在内存中为程序中每一个正在使用的文件开辟一块“文件缓冲区”。从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘中,如果磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区,然后再从缓冲区逐个的将数据送到程序数据区,其中缓冲区的大小由编译系统决定。

文件指针

缓冲文件系统中,关键的概念是“文件类型指针”,简称“文件指针”。
每个被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件的名字,文件状态及当前位置等),这些信息是保存在一个结构体变量中的,该结构体类型是系统声明的,取名FILE

stdio.h中对文件类型的声明为:

#ifndef _FILE_DEFINED
  struct _iobuf {
    char *_ptr;
    int _cnt;
    char *_base;
    int _flag;
    int _file;
    int _charbuf;
    int _bufsiz;
    char *_tmpfname;
  };
  typedef struct _iobuf FILE;
#define _FILE_DEFINED
#endif

不同的C编译器的FILE类型包含的内容不完全相同,每当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构的变量,并填充其中的信息,使用者不必关心细节。一般都是通过一个FILE的指针来维护这个FILE结构的变量,这样使用起来更加方便。

FILE *pf;//文件指针变量

pf指向某个文件的文件信息区,通过文件指针变量能够找到与它关联的文件。

基础文件操作

在命令行上显示一段内容

printf("hello!\n");

上面的这行程序,相当于是在标准输出文件中进行输出,也就是相当于下面这行程序,二者的输出结果相同的。

fprintf(stdout, "hello!\n");
// stdin 标准输入
// stdout 标准输出
// stderr 标准错误
// printf 是 fprintf 对 stdout 的特化
// scanf 是 fscanf 对 stdin 的特化

如果不是在标准输出当中处理文字,而是写入到特定的文件当中,那么就需要按照以下步骤进行文件的操作:

1、打开文件

FILE *fopen(const char* filename, const char* mode);
// 参数1: 文件的路径(相对于可执行文件的相对路径或绝对路径)
// 参数2: 文件的模式,接收一个字符串,用双引号
// r只读模式(如果文件不存在,出错),w只写模式(如果文件不存在,新建文件),a追加模式,向文件尾追加数据(文件不存在,出错);
// b二进制模式,rb二进制只读(若文件不存在,出错),wb二进制只写(若文件不存在,新建文件),ab二进制追加(若文件不存在,出错);
// +读写都包括, r+打开文件读写(若文件不存在,出错),w+新建文件读写(若文件不存在,新建文件),a+文件尾读写(若文件不存在,新建文件);
// rb+打开二进制文件读写(若文件不存在,出错),wb+新建二进制文件读写(若文件不存在,新建文件),ab+二进制文件尾读写(若文件不存在,新建文件);
// 文件存在和文件不存在两种情况
// 当文件存在时,r读模式从文件的开头开始读取文件内容,当文件不存在时,r读模式失败
// fopen()操作成功会返回一个指向文件的指针,若返回失败则返回一个 NULL
// 当文件存在时,w写模式会从文件的开始部分写入内容,当文件不存在的时候,会在对应的目录下创建该文件
// 当模式中不带有+号时,文件不存在读和追加模式会出错,当带有+号时,文件不存在只有读模式下会出错
FILE *fp = fopen("1.txt", "r");
if(fp != NULL)
{
	fprintf(stdout, "success\n");
}
else
{
	fprintf(stdout, "failed\n");
	perror("error:");
	// 可以使用 perror 输出完整的错误信息
	fp = fopen("1.txt", "w");
}

fprintf(fp, "hello\n");
// 如果执行出错,将会返回一个复数。

2、处理文件

// 所有输入输出流
fprintf fscanf fgets fputs fgetc fputc

// 二进制文件
// 如果保存的数据是数组、结构体等非字符型的复杂数据,可以使用该操作
fread fwrite

3、关闭文件

fclose(fp);
fp = NULL;
// 关闭文件

文件顺序读写

1、字符输入函数fgetc
函数原型int fgetc(FILE *stream);

int ch = 0;
ch = fgetc(pf);

2、字符输出函数fputc
函数原型int fputc(int c, FILE *stream);

fputc('b', pf);

3、文本行输入函数fgets
函数原型char* fgets(char* string, int n, FILE *stream);

char arr[20] = {0};
fgets(arr, 20, pf);
// 当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,会停止

4、文本行输出函数fputs
函数原型int fputs(const char* string, FILE *stream);

fputs("hehe", pf);

5、格式化输入函数fscanf
函数原型int fscanf(FILE *stream, const char *format, ...)

int a = 0;
char arr[10];
fscanf(pf, "%s %d", arr, &a);
fscanf(stdin, "%s %d", arr, &a);

6、格式化输出函数fprintf
函数原型int fprintf(FILE *stream, const char *format, ...)

int a = 0;
char arr[10] = "hehe";
fprintf(pf, "%s %d", arr, a);
fprintf(stdout, "%s %d", arr, a);

7、二进制输入fread
函数原型size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

fread(buffer, strlen(c)+1, 1, fp);
// 读取的数据存放在哪,元素大小,元素个数,从哪里读

8、二进制输出fwrite
函数原型size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)

char str[] = "This is runoob.com";
fwrite(str, sizeof(str) , 1, fp);
// 要被写入的数据,元素大小,元素个数,输出流

scanf、fscanf、sscanf与printf、fprintf、sprintf对比

1、scanf与printf
printf函数原型int printf(const char *format, ...)
scanf函数原型int scanf(const char *format, ...)
格式化的输入输出函数,针对的是标准输入输出

2、fscanf与fprintf
fprintf函数原型int fprintf(FILE *stream, const char *format, ...)
fscanf函数原型int fscanf(FILE *stream, const char *format, ...)
格式化的输入输出函数,文件或标准输入输出都可

3、sscanf与sprintf
sprintf函数原型int sprintf(char *buffer, const char *format, ...)
sscanf函数原型int sscanf(const char *buffer, const char *format, ...)
格式化的输入输出函数,针对字符串

struct S
{
	char name[20];
	int age;
};

struct S s = {"zhangsan", 20};
// 在真实开发环境中,存在结构体与字符串之间的相互转换

char buf[20] = {0};
sprintf(buf, "%s %d", s.name, s.age); // 把一个结构体变量的数据以格式化的形式转换为字符串存储

// 字符串转换为结构体
struct S tmp = {0};
sscanf(buf, "%s %d", tmp.name, &(tmp.age));

文件随机读写

对文件的读取方式分为三种:
1、文件开头开始读
2、文件末尾开始读
3、文件当前位置开始读

// 当需要从文件的末尾开始读取时,需要用到 fseek 文件操作光标移动函数
fseek(fp, 10 ,SEEK_SET);
// 参数1 打开的文件指针
// 参数2 需要移动的位移量
// 参数3 从哪里开始计算位移量:
// SEEK_SET 文件头
// SEEK_CUR 当前位置
// SEEK_END 文件尾,需要配合负数的位移量
// 注意,以 a 形式打开文件,任何写入的操作为了避免内容丢失,都会在写入的时候,定位到文件末尾
// 大部分时间,使用 SEEK 处理读取的操作。

此外还有其他的一些操作

rewind  // 重定位到文件头
ftell  // 返回当前文件操作光标距离文件头的位置
fseek(fp, 0, SEEK_END);
printf("%ld", ftell(fp));

其他操作

删除文件

remove("1.txt");

重命名文件

rename("1.txt", "2.txt");
// 两个参数,前者为旧名字,后者为新名字

读取其中内容

FILE *fp = fopen("2.txt", "r+");
while(1)
{
	char c = fgetc(fp);
	putchar(c);
}
// 按照上述程序,该程序将会无限循环执行

//文件终止符 EOF,是否是文件终止符
FILE *fp = fopen("2.txt", "r+");
char c = '0';
while(c != EOF)
{
	c = fgetc(fp);
	putchar(c);
}

//文件终止符 feof,检测当前的文件操作光标是否在文件末尾
FILE *fp = fopen("2.txt", "r+");
char c = '0';
while(!feof(fp))
{
	c = fgetc(fp);
	putchar(c);
}
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页