C语言文件操作

在程序设计中文件有两种,一种是程序文件(包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe))另外一种就是数据文件(文件的文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件, 或者输出内容的文件),也就是本篇文章所介绍的东东啦。

目录

文件名

文件类型

文件的打开和关闭

1.文件指针

2.fopen

3.fclose

文件的顺序读写

1.fputc

2.fgetc

3.fputs

4.fgets

5.fprintf

6.fscanf

7.fwrite

8.fread

对比printf,fprintf,,sprintf 

文件的随机读写 

1.fseek

2.ftell

3.rewind

文件读写结束的判断


文件名

一个文件要有一个唯一的文件标识,以便用户识别和引用,而我们通常称文件标识为文件名。文件名包含3 部分: 文件路径 + 文件名主干 + 文件后缀 例如: (c:\code\test.txt)
注意文件名不能包含一些特殊字符: \   /  :  *  ?  "  <  >  |

文件类型

数据文件根据数据的组织形式还可以分为文本文件和二进制文件。

二进制文件:数据在内存中以二进制的形式存储,如果不加转换的输出到外存就是二进制文件

文本文件:如果要求在外存上以ASCII码的形式存储,则需要在存储前转换。以ASCII字符的形式存储的文件就是文本文件

流是个抽象的概念,是对输入输出设备(文件,网络,内存等)的抽象,对于数据的输入/输出操作都是以“流”的方式进行。当程序需要从某个数据源读入数据的时候,就会开启一个输入流,数据源可以是文件、内存或网络等等。相反地,需要写出数据到某个数据源目的地的时候,也会开启一个输出流,这个数据源目的地也可以是文件、内存或网络等等。

任何一个C程序,只有运行起来就会默认打开3个流:FILE* stdin(标准输入流:对应键盘)

FILE* stdout(标准输出流:对应屏幕)FILE* stderr(标准错误流:应屏幕)

文件的打开和关闭

在对文件进行读写操作前后分别需要对文件进行打开和关闭的操作。其操作离不开文件指针。

1.文件指针

文件指针(如:FILE* pf)是用来维护文件的。每个文件被使用的时候都会在内存中开辟了一个相应的文件信息区,文件信息区存放有文件的相关信息(如文件的名字,文件状态及文件当前的位置等)。这些信息会被保存在一个名为FILE的结构体变量中,通过一个FILRE的指针可以维护该结构体,进而访问对应的文件。

2.fopen

fopen函数是用来打开文件的函数,

它的返回值FILE*,即打开文件的同时返回一个FILE*的指针变量指向该文件,也相当于建立了文件和指针的关系第一个参数(const char* filename)为字符串,包含要打开的文件的名称第二个参数(const char* mode)为模式说明符(文件的打开方式),代表文件的读写权限。

第二个参数文件打开方式最基本的有以下几种方式:

控制读写权限的字符串(必须指明)
打开方式 说明
"r" r(read),以“只读”方式打开文件。只允许读取,不允许写入。文件必须存在,否则打开失败。
"w" w(write),以“写入”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
"a" a(append),以“追加”方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。
"r+" 以“读写”方式打开文件。既可以读取也可以写入,也就是随意更新文件。文件必须存在,否则打开失败。
"w+" 以“写入/更新”方式打开文件,相当于wr+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
"a+" 以“追加/更新”方式打开文件,相当于a和r+叠加的效果。既可以读取也可以写入,也就是随意更新文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。
控制读写方式的字符串(可以不写)
打开方式 说明
"t" t(text),文本文件。如果不写,默认为"t"
"b" b(binary),二进制文件。
调用 fopen() 函数时必须指明读写权限,但是可以不指明读写方式(此时默认为"t")。

读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部(换句话说,不能将读写方式放在读写权限的开头)。例如:

1.将读写方式放在读写权限的末尾:"rb"、"wt"、"ab"、"r+b"、"w+t"、"a+t"

2.将读写方式放在读写权限的中间:"rb+"、"wt+"、"ab+“

注:使用fopen函数需要判断文件是否打开成功,若打开失败则返回空指针

3.fclose

fclose函数是用来关闭文件的,

它的返回值为int,如果成功关闭,则返回零值,关闭失败,返回 EOF。返回参数(FILE* stream)为要关闭的文件的 FILE 对象的指针。注意关闭文件后最好将文件指针置空。

下面是文件的打开和关闭的操作样例

#include
int main()
{
	FILE* pf = fopen("test.txt","w");//打开文件
	if (pf == NULL)//判断打开成功与否
	{
		perror("FILE:");
		return 1;
	}
//进行读写操作
//...
//结束读写操作
	fclose(pf);//关闭文件
	pf = NULL;
	return 0;
}

文件的顺序读写

1.fputc

fputc函数在文件的读写中用于把一个字符写入指定的文件中,

返回类型为int,写入成功则返回写入的字符,写入错误则返回 EOF 第一个参数(int character)为要写入的字符(写入时,该值在内部转换为无符号字符)。第二个参数(FILE* stream)为指向要访问的文件的文件指针。

下面是fgetc函数的使用样例:

#include
int main()
{
	FILE* pf = fopen("test.txt", "w");//以写方式打开文件
	if (pf == NULL)//判断打开成功与否
	{
		perror("FILE:");
		return 1;
	}
	fputc("a", pf)//将字符a写入pf所指向的文件中
	fclose(pf);//关闭文件
	pf = NULL;
	return 0;
}

2.fgetc

fgetc函数在文件的读写中用于从指定的文件中读一个字符

返回类型为int,成功后返回读取的字符(提升为 int 值),失败返回EOF函数参数(FILE* stream)为指向要访问的文件的文件指针。

下面是fgetc函数的使用样例:

#include
int main()
{
	FILE* pf = fopen("test.txt", "r");//以读方式打开文件
	if (pf == NULL)//判断打开成功与否
	{
		perror("FILE:");
		return 1;
	}
    while(ch=fgetc(pf)!=EOF)//读取pf所值向的文件内容
    {
	    printf("%c",ch);//打印读到的内容
    }
   	fclose(pf);//关闭文件
	pf = NULL;
	return 0;
}

3.fputs

fputs函数在文件的读写中用于向指定的文件写入一个字符串

返回类型为int,写入成功则返回非负值写入错误则返回 EOF 第一个参数(const char* str)为要写入的字符串的起始地址。第二个参数(FILE* stream)为指向要访问的文件的文件指针。

下面是fputs函数的使用

样例:

#include
int main()
{
	FILE* pf = fopen("test.txt", "w");//以写方式打开文件
	if (pf == NULL)//判断打开成功与否
	{
		perror("FILE:");
		return 1;
	}
    fputs("abc",pf);//将字符串abc写入pf所指向的文件中
	fclose(pf);//关闭文件
	pf = NULL;
	return 0;
}

4.fgets

fgets函数在文件的读写中用于从文件中读取字符并将其作为 C 字符串存储到 str 中,直到读取 (num-1) 字符或达到换行符或文件末尾(以先发生者为准)(注:1.换行符会使 fgets 停止读取,但它被函数视为有效字符,并包含在复制到 str 的字符串中 。   2.终止空字符也会自动追加到复制到 str 的字符之后。)
 返回类型为char*,读取成功返回指向在其中复制字符串读取的数组的指针,读取失败返回空指针第一个参数(char* str)为指向在其中复制字符串读取的数组的指针第二个参数(int num)为要复制到 str 中的最大字符数(包括终止空字符)第三个参数(FILE* stream)为指向要访问的文件的文件指针

下面是fgets函数的使用样例:

#include
int main()
{
	FILE* pf = fopen("test.txt", "r");//以读方式打开文件
	if (pf == NULL)//判断打开成功与否
	{
		perror("FILE:");
		return 1;
	}
	char arr[10] = { 0 };
	fgets(arr,5,pf);	//进行读操作
	printf("%s\n", arr);
	fclose(pf);//关闭文件
	pf = NULL;
	return 0;
}

5.fprintf

fprintf在文件的读写中用于将格式化的数据写入文件

fprint的用法可以类比printf的用法

对比一下可以发现fprintf比printf就多加了一个参数 (FILE* stream),这样我们很容易可以得到下面fprintf的使用样例:

#include
struct a
{
	int m;
	char p;
}a1={2,‘a’};
int main()
{
	FILE* pf = fopen("test.txt", "w");
	if (pf == NULL)
	{
		perror("FILE:");
		return 1;
	}
	fprintf(pf, "%d", a1.m);//比printf多一个参数pf
	fclose(pf);
	pf = NULL;
	return 0;
}

结合流的内容,printf("%d",a);也可以写成fprintf(stdout,"%d",a);

6.fscanf

fscanf在文件的读写中用于从文件中读取数据,并根据参数格式将其存储到附加参数所指向的位置

 fscanf的用法也可以类比scanf的用法

 对比一下同样发现fscanf比scanf就多加了一个参数 (FILE* stream),这样我们很容易可以得到下面fprintf的使用样例:

#include
struct a
{
	int m;
	char p;
}a1;
int main()
{
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("FILE:");
		return 1;
	}
	fscanf(pf, "%d", &a1.m);//比scanf多一个参数pf
    printf("%d\n",a1.m);
	fclose(pf);
	pf = NULL;
	return 0;
}

7.fwrite

fwritez函数在文件的读写中用于将数据块以二进制的形式写到文件中

 返回值为成功写入的元素总数,如果该数字与 count 参数不同,则会出现写入错误第一个参数(const void* ptr)为指向要写入的元素数组的指针第二个参数(size_t size)为要写入的每个元素的大小(以字节为单位)第三个参数(size_t count)为要写入的元素数,每个元素的大小为size字节第四个参数(FILE* stream)为指向要访问的文件的文件指针。

下面是fwrite函数的使用样例:

#include
struct S
{
	char arr[10];
	int a;
};
int main()
{
	struct S s = { "abc",21 };
	FILE* pf = fopen("text.txt", "wb");
	if (pf == NULL)
	{
		perror("FILE:");
		return 1;
	}
	fwrite(&s, sizeof(struct S), 1, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}

8.fread

fread函数在文件的读写中用于以二进制的形式读取数据。

返回值为成功读取的元素总数,如果该数字与 count 参数不同,或者读到文件末尾,则会出现写入错误第一个参数(void* ptr)为值向大小至少为(大小*计数)字节的内存块的指针,转换为void*第二个参数(size_t size)为要读取的每个元素的大小(以字节为单位)第三个参数(size_t count)为要读取的元素数,每个元素的大小为size字节第四个参数(FILE* stream)为指向要访问的文件的文件指针。

下面是fwrite函数的使用样例:

#include
struct S
{
	char arr[10];
	int a;
};
int main()
{
	struct S s = { "abc",21 };
	FILE* pf = fopen("text.txt", "wb");
	if (pf == NULL)
	{
		perror("FILE:");
		return 1;
	}
	fread(&s, sizeof(struct S), 1, pf);
	printf("%s %d\n", s.arr, s.a);
	fclose(pf);
	pf = NULL;
	return 0;
}

对比printf,fprintf,,sprintf 

​​​​​​printf,scanf和fscanf,fprintf我们都见识过了,那么这里就再介绍两个函数:sprintf和sscanf

sprintf

sprintf函数用于把一个格式化的数据写到字符串中,

用法和fprintf差不多,只是把文件指针换成字符指针,废话就不多说了,直接上样例:

#include
int main()
{
	struct S s = { "abc",3 };
	char buf[100];
	sprintf(buf,"%s %d", s.arr, s.n);
	printf("%s", buf);//打印结果为abc 3
	return 0;
}

sscanf
sscanf函数是用于从一个字符串中转化出一个格式化的数据,

 看样用法样例:

struct S
{
	char arr[10];
	int n;
};
#include
int main()
{
	struct S s = { 0 };
	char buf[10] = "abc 3";
	sscanf(buf, "%s %d", s.arr, &s.n);
	printf("%s %d", s.arr, s.n);//打印结果为abc 3
	return 0;
}

总结

scanf:是针对标准输入的格式化输入语句

printf:是针对标准输出的格式化输出语句

fscanf:是针对所有输入流的格式化输入语句

fprintf:是针对所有输出流的格式化输出语句

sscanf:是从一个字符串中转化出一个格式化的数据

sprintf:是把一个格式化的数据转化成字符串

文件的随机读写 

1.fseek

fseek函数用于根据文件指针的位置和偏移量来定位文件指针。

返回类型为int,成功返回零,否则返回非零值第一个参数(FILE* stream)为指向要访问的文件的文件指针第二个参数(long int offset)若为二进制文件则是要从偏移的字节数,若为文本文件则是零或 ftell 返回的值第三个参数(int origin)用作偏移参考的位置,它由常量(SEEK_SET(文件开头),SEEK_CUR(文件指针的当前位置),SEEK_END(文件结尾))之一指定,这些常量在 专门用作此函数的参数

 

2.ftell

ftell函数用于返回文件指针相对于起始位置的偏移量

返回类型为long int,成功则返回仓位指标的当前值,失败返回 -1L 。参数FILE* stream)为指向要访问的文件的文件指针

3.rewind

rewind函数用于让文件指针的位置回到文件的起始位置。

就喜欢这种参数返回类型简单明了的函数,哈哈

再看看随机读写用法样例子:

#include
struct S
{
	char arr[10];
	int m;
}s1[10], s2;
int main()
{
	struct S s1[10] = { {"abc",1},{"def",2},{"gew",3} };
	FILE* pf = fopen("text.txt", "wb+");
	if (pf == NULL)
	{
		perror("FILE");
        return 1;
	}
	fwrite(s1, sizeof(struct S), 3, pf);//写入3条信息
	fseek(pf, sizeof(struct S), SEEK_SET);//移动文件指针
	fread(&s2, sizeof(struct S), 1, pf);//读取一条信息
	printf("%s %d", s2.arr, s2.m);//打印结果为:def 2
	fclose(pf);
	pf = NULL;
	return 0;
}

文件读写结束的判断

1. 文本文件读取是否结束,判断返回值是否为 EOF fgetc ),或者 NULL fgets
例如:
fgetc 判断是否为 EOF .
fgets 判断返回值是否为 NULL .
int main()
{
	FILE* pf = fopen("text.txt", "r");
	if (pf == NULL)
	{
		perror("FILE:");
		return 1;
	}
	char ch = 0;
	while ((ch = fgetc(pf)) != EOF)/*fgetc 当读取失败的时候或者遇到文件结束的时候,都会返回EOF*/
	{
		putchar(ch);
	}
//判断是什么原因结束的
	if (ferror(pf))
	{
		puts("I/O error when reading");
	}
	else if(feof(pf))
	{
		puts("End of file reached successfully");
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

2. 二进制文件的读取结束判断,判断返回值是否小于实际要读的个数。
例如:
fread 判断返回值是否小于实际要读的个数。
#include 
enum { SIZE = 5 };
int main(void) {
	double a[SIZE] = { 1.,2.,3.,4.,5. };
	FILE* fp = fopen("test.bin", "wb"); // 必须用二进制模式
	fwrite(a, sizeof * a, SIZE, fp); // 写 double 的数组
	fclose(fp);
	double b[SIZE];
	fp = fopen("test.bin", "rb");
	size_t ret_code = fread(b, sizeof * b, SIZE, fp); // 读 double 的数组
	if (ret_code == SIZE) {
		puts("Array read successfully, contents: ");
		for (int n = 0; n < SIZE; ++n) printf("%f ", b[n]);
		putchar('\n');
	}
	else { // error handling
		if (feof(fp))
			printf("Error reading test.bin: unexpected end of file\n");
		else if (ferror(fp)) {
			perror("Error reading test.bin");
		}
	}
	fclose(fp);
}

你可能感兴趣的:(c语言)