C语言之文件操作篇(2)

目录

文件状态指针

文件流

文件的顺序读写

fgetc

fputc

fgets

fputs

fscanf

fprintf

fread

fwrite

sscanf

sprintf 

文件的随机读写

fseek 

ftell

rewind

文件结束的判定

feof

ferror


今天接下来我们讲解文件读写函数。

文件状态指针

  • 文件状态指针也就是文件指示器。(可以理解为光标) 
  •  文件在不断被操作读取的过程中,文件状态指针是一直在移动的。

C语言之文件操作篇(2)_第1张图片

文件流

  • 流是怎样一个概念呢?我们简单通俗的介绍下。本篇我们把所有输入输出流分为两个标准输入出流文件流

流是一个高度抽象的概念!!在我们写程序的时候,我们需要将数据传到屏幕,存在硬盘上,传到网络上,U盘光盘等外部设备。不同的外部设备的读写方式不同,传输方式也不同。 有人觉得程序员也太难了,于是抽象化了流的概念。我们先将数据统一传输到流里面(文件流等),然后再传输到各个外部设备。

 C语言之文件操作篇(2)_第2张图片

我们在写文件的时候,需要打开文件,关闭文件。我们用scanf从键盘上读取数据,printf向屏幕上打印数据,直接操作了为什么没有打开标准输入输出流呢? 

那是因为C语言程序运行起来,就默认打开了3个流。

  • stdin : 标准输入流(键盘)
  • stdout : 标准输出流(屏幕)
  • stderr : 标准错误流
  • 特别提醒:所有的流都是FILE*的指针类型

用打开标准输出流的方式打印26个字母

#include
int main()
{
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		if (ch % 5 == 0)
			fputc('\n', stdout);//标准输出流
		fputc(ch, stdout);
	}
	//a-97
	//b-98
	//c-99
	//100换行了
	return 0;
}

C语言之文件操作篇(2)_第3张图片

用打开文件流的方式打印26个字母。------>下面fputc

#include
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		if (ch % 5 == 0)
			fputc('\n', pf);//文件流
		fputc(ch, pf);
	}
	//a-97
	//b-98
	//c-99
	//100换行了
	fclose(pf);
	pf = NULL;
	return 0;
}

文件的顺序读写

记住:站在内存数据的角度去理解!! 其次文件要用输入输出函数去输入输出操作,也可以动键盘修改文本文件!!

功能                 函数名        适用于

字符输入函数          fgetc         所有输入流
字符输出函数          fputc         所有输出流
文本行输入函数        fgets         所有输入流
文本行输出函数        fputs         所有输出流
格式化输入函数        fscanf        所有输入流
格式化输出函数        fprintf       所有输出流
二进制输入            fread         文件
二进制输出            fwrite        文件

C语言之文件操作篇(2)_第4张图片

C语言之文件操作篇(2)_第5张图片

我们接下来详细的介绍以上各个函数。函数头文件 参数 返回值 使用去介绍


fgetc

 fgetc - C++ Reference (cplusplus.com)

 

  • fgetc是以读的形式字符输出文件,内存读取数据。
  • 头文件 #include
  • 参数FILE *stream 文件指针类型的指针变量 指向了一个文件信息区
  • 返回值是int类型(字符发生了整型提升)
  1. 成功后,将返回字符读取(提升为 int 值)ASCII码。
  2. 返回类型为 int 以适应特殊值 EOF,该值表示失败-1
  3. 如果位置指示器位于文件末尾,则该函数返回 EOF 并设置的 eof 指示器 (feof)。
  4. 如果发生其他读取错误,该函数还会返回 EOF,但改为设置其错误指示器 (ferror)。
#include
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	ch=fclose(pf);
	pf = NULL;
	return 0;
}

C语言之文件操作篇(2)_第6张图片

fputc

fputc - C++ Reference (cplusplus.com)

  

  • fputc以写的形式输入字符到文件中。
  •  头文件#include
  • 参数int character 是整型 也就是字符整型提升 直接写字符即可
  • 参数FILE*stream 是文件指针类型的指针变量 指向了一个文件信息区
  • 返回值
  1. 成功后,将返回所写字符(整型提升)
  2. 如果发生写入错误,则返回 EOF 并设置错误指示器(ferror)。
#include
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	fputc('a', pf);//文件流
	fputc('b', pf);
	fputc('c', pf);
	fputc('d', pf);

	fclose(pf);
	pf = NULL;
	return 0;
}

C语言之文件操作篇(2)_第7张图片

请在文件中输入26个英文字母。 

#include
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ch = 0;
	for (ch='a'; ch <= 'z'; ch++)
	fputc(ch, pf);
	fclose(pf);
	pf = NULL;
	return 0;
}
//换行
#include
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char ch = 0;
	for (ch = 'a'; ch <= 'z'; ch++)
	{
		if (ch % 5 == 0)
			fputc('\n', pf);//文件流
		fputc(ch, pf);
	}
	//a-97
	//b-98
	//c-99
	//100换行了
	fclose(pf);
	pf = NULL;
	return 0;
}

 C语言之文件操作篇(2)_第8张图片


fgets

 fgets - C++ Reference (cplusplus.com)

从流中获取字符串

中读取字符并将其作为 C 字符串存储到 str 中,直到读取 num-1 个字符或到达换行符或文件末尾,以先发生者为准。
换行符使 fgets 停止读取,但它被函数视为有效字符,并包含在复制到 str 的字符串中。
终止空字符会自动附加到复制到 str 的字符之后

  • fgets是从文本文件stream读取输出字符串num str的函数 
  • 头文件#include
  • 参数str是字符指针,指向一个字符数组的指针
  • 参数FILE*stream 是文件指针类型的指针变量 指向了一个文件信息区
  • 参数num是要复制到 str 的最大字符数(包括终止空字符)(整型提升)
  • 返回值是char*类型
  1. 成功后,函数返回 str
  2. 如果在尝试读取字符时遇到文件末尾,则设置 eof 指示器 (feof)。
  3. 如果在读取任何字符之前发生这种情况,则返回的指针为空指针(str 的内容保持不变)。
  4. 如果发生读取错误,则设置错误指示器(ferror),并返回空指针(但str指向的内容可能已更改)。
  •  读取停止情况
  1. 读取到了num-1个字符
  2. 到达换行符\n
  3. 到达文件末尾

只会读num-1个字符 

#include
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	char str[10] = { 0 };
	int ret=fgets(str,7,pf);//实际上只会读6个字符
	if (ret == EOF)
	{
		perror("fgets");
	}
	else
	printf("%s", str);
	fclose(pf);
	pf = NULL;
	return 0;
}

C语言之文件操作篇(2)_第9张图片

读到\n停止不读了 

#include
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	char str[100] = { 0 };
	int ret=fgets(str,12,pf);//实际上只会读6个字符
	if (ret == EOF)
	{
		perror("fgets");
	}
	else
	printf("%s", str);
	fclose(pf);
	pf = NULL;
	return 0;
}

C语言之文件操作篇(2)_第10张图片

fputs

fputs - C++ Reference (cplusplus.com)  

  • fputs是将str里的字符输出到文本文件stream 
  • 头文件#include
  • 参数FILE*stream 是文件指针类型的指针变量 指向了一个文件信息区
  • 参数str 是字符指针 指向存放字符串的数组(由const修饰,防止被修改)
  • 返回值是int类型
  1. 成功时,将返回非负值。
  2. 出错时,该函数返回 EOF 并设置错误指示器(ferror)
#include
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	char str[] = "abcdef";
	fputs(str,pf);
	fputs("abcdef", pf);
//两种写法
	fclose(pf);
	pf = NULL;
	return 0;
}

C语言之文件操作篇(2)_第11张图片


下面两个函数用结构体去举例子 fscanf是输入 fpirntf是输出,先看下对比。

C语言之文件操作篇(2)_第12张图片你发现了什么?? 

  • scanf------->键盘-------->读取到程序中   fscanf-------->文件-------->读取到程序中 
  • printf-------->程序中的数据-------->输入到屏幕上   fprintf-------->程序中的数据------>输入到文件

fscanf

fscanf - C++ Reference (cplusplus.com)

  • fscanf是从文件读取数据
  • 头文件#include
  • 参数FILE*stream 是文件指针类型的指针变量 指向了一个文件信息区
  • 其他和scanf是一样的,不用关心
  • 返回值是int类型
#include
struct S
{
	char c;
	int i;
	float a;
};
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	//输入
	//struct S s = { 't',7,3.14 };
	//fprintf(pf,"%c  %d  %f",s.c,s.i,s.a);
	//格式必须一摸一样
	//输出
	struct S s = {0};
	fscanf(pf, "%c %d %f", &(s.c), &(s.i), &(s.a));
	printf("%c %d %f", s.c, s.i, s.a);

	fclose(pf);
	pf = NULL;
	return 0;
}

 C语言之文件操作篇(2)_第13张图片

fprintf

fprintf - C++ Reference (cplusplus.com)

  • fprintf是输入数据到文本文件
  • 头文件#include
  • 参数FILE*stream 是文件指针类型的指针变量 指向了一个文件信息区
  • 其他和printf是一样的,不用关心
  • 返回值是int类型
  1. 成功后,将返回写入的字符总数。
  2. 如果发生写入错误,则设置错误指示器(ferror)并返回负数。
  3. 如果在写入宽字符时发生多字节字符编码错误,errno 将设置为 EILSEQ 并返回负数。
#include
struct S
{
	char c;
	int i;
	float a;
};
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}

	struct S s = { 't',7,3.14 };
	fprintf(pf,"%c  %d  %f",s.c,s.i,s.a);

	fclose(pf);
	pf = NULL;
	return 0;
}

C语言之文件操作篇(2)_第14张图片


前面的函数都是针对于文本文件的,下面这组函数针对的是二进制文件。 

fread

 fread - C++ Reference (cplusplus.com)

从流中读取数据块

中读取计数元素数组,每个元素的大小为字节并将它们存储在 ptr 指定的内存块中。
流的位置指示器按读取的总字节数前进。
如果成功,则读取的总字节数为(大小*计数)。

  • fread是向二进制文件读取数据
  • 头文件#include
  • 参数FILE*stream 是文件指针类型的指针变量 指向了一个文件信息区
  • size_t size是每个元素的大小(以字节为单位)
  • size_t count是数组元素的个数
  • 参数void *ptr指向大小至少为 (size*count) 字节的内存块的指针(const修饰防止修改,void*类型的指针是因为不知道读取的是哪种类型的指针,void*不能被解引用操作,可以强制转换)
  • 返回值是size_t类型
  1. 返回成功读取的元素总数。
  2. 如果此数字与 count 参数不同,则表示读取时发生读取错误或到达文件末尾。在这两种情况下,都会设置正确的指标,可以分别用 ferror 和 feof 进行检查。
  3. 如果大小计数为零,则该函数返回零,并且流状态和 ptr 指向的内容保持不变。
    size_t 是无符号整数类型。
  • fread要求读取count个大小位size字节的数据
  1. 如果真的读取到count个数据,函数就返回count
  2. 如果没有读取到count个数据,函数就返回的是真实的读取到的完整的数据个数
#include
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int arr[20] = { 0};//10个整型40个字节
	fread(arr, 4, 10, pf);
	int i = 0;
	for (i = 0; i < 10;i++)
	{
		printf("%d ", arr[i]);
	}
	fclose(pf);
	pf = NULL;
	return 0;
}

C语言之文件操作篇(2)_第15张图片

fwrite

fwrite - C++ Reference (cplusplus.com)

写入要流式传输的数据块

计数元素数组(每个元素的大小为字节从 ptr 指向的内存块写入中的当前位置。
流的位置指示器按写入的总字节数前进。
在内部,该函数解释所指向的块,就好像它是一个类型的元素数组,并按顺序写入它们,就好像为每个字节调用一样。

  • fwrite是向二进制文件写输入数据的
  • 头文件#include
  • 参数FILE*stream 是文件指针类型的指针变量 指向了一个文件信息区
  • 参数void *ptr是指向写入元素数组的的指针变量(const修饰防止修改,void*类型的指针是因为不知道输入的输入是哪种类型的指针,void*不能被解引用操作,可以强制转换)
  • size_t size是每个元素的大小(以字节为单位)
  • size_t count是数组元素的个数
  • 返回值size_t类型
  1. 返回成功写入的元素总数。
  2. 如果此数字与 count 参数不同,则写入错误阻止函数完成。在这种情况下,将为设置错误指示器(ferror)。
  3. 如果大小计数为零,则该函数返回零,错误指示器保持不变。
    size_t 是无符号整数类型。
#include
int main()
{
	FILE* pf = fopen("data.txt", "w");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };//10个整型40个字节
	fwrite(arr, 4, 10, pf);

	fclose(pf);
	pf = NULL;
	return 0;
}

 C语言之文件操作篇(2)_第16张图片


 前面我们已经学习过了scanf&printf 和 fscanf&fprintf 我们再来学习一下sscanf&sprintf

sscanf

sscanf - C++ Reference (cplusplus.com) 

  • 从s(字符串数组)拿出格式化的数据(字符串),放到每个数据当中去。
  • 头文件#include
  • 参数s是指向一块字符串数组的字符指针
  • 后面参数和scanf一样
  • 格式要和sprintf一模一样,或者和字符串内字符串格式一摸一样
  • 返回值是int类型
  1. 成功后,该函数返回参数列表中成功填充的项数。此计数可以与预期的项目数匹配,或者在匹配失败的情况下更少(甚至为零)。
  2. 如果在成功解释任何数据之前输入失败,则返回 EOF。
//需要结构体
//sprintf和sscanf配合使用
//两个才能达到效果
#include
struct S
{
	int a;
	float b ;
	char c;
};
int main()
{
	struct S s = { 7,3.14,'t' };
	char str[100] = { 0 };
	sprintf(str, "%d_%f_%c", s.a, s.b, s.c);
	printf("%s\n", str);

	
	struct S tmp = { 0 };
	sscanf(str, "%d_%f_%c", &(tmp.a), &(tmp.b), &(tmp.c));
	printf("%d\n", tmp.a);
	printf("%f\n", tmp.b);
	printf("%c\n", tmp.c);
	return 0;
}

C语言之文件操作篇(2)_第17张图片

sprintf 

 sprintf - C++ Reference (cplusplus.com)

  • sprintf是把格式化的数据,转化成字符串(写格式化的数据到字符串str里)
  • 头文件#include
  • 参数str 是指向一块字符数组的字符指针
  • 后面参数和printf一样
  • 返回值是int类型
  1. 成功后,将返回写入的字符总数。此计数不包括自动追加在字符串末尾的其他 null 字符。
  2. 失败时,返回负数
#include
int main()
{
	int a = 7;
	float b = 3.14;
	char c = 't';
	char str[100] = { 0 };
	sprintf(str, "%d_%f_%c", a, b, c);
	printf("%s", str);
	return 0;
}

C语言之文件操作篇(2)_第18张图片


最后我们来对比一下底下几组函数

C语言之文件操作篇(2)_第19张图片

  • scanf 是格式化的输入函数,针对是标准输入流(键盘)
  • printf 是格式化的输出函数,针对的是标准输出流(屏幕)
  • scanf和printf是针对标准输入和输出流的格式化输入和输出函数
  • fscanf 是针对所有输入流(文件流、标准输入流)的格式化输入函数
  • fprintf 是针对所有输出流(文件流、标准输出流)的格式化输出函数
  • sscanf 将字符串转化成格式化的数据
  • sprintf 将格式化的数据转化成字符串

文件的随机读写

fseek 

fseek - C++ Reference (cplusplus.com)

  • fseek是根据文件指针的位置和偏移量来定位文件指针
  • 头文件#include
  • 参数FILE*stream 是文件指针类型的指针变量 指向了一个文件信息区
  • 参数offset 表示你选择的位置(当前/开头/结尾)到你需要的位置 偏移量
  • 参数origin 表示你选择位置

C语言之文件操作篇(2)_第20张图片

  • 返回值是int类型 
  1. 如果成功,该函数将返回零。
  2. 否则,它将返回非零值。如果发生读取或写入错误,则设置错误指示器(ferror)
#include
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	fseek(pf, -4, SEEK_CUR);//移动光标的作用
	fseek(pf, 0, SEEK_SET);
	fseek(pf, -4, SEEK_END);
	ch = fgetc(pf);
	printf("%c", ch);

	close(pf);
	pf = NULL;
	return 0;
}

C语言之文件操作篇(2)_第21张图片

特别提醒:如果我们想熟练掌握这个函数运用,我们必须对我们要读取的文件格式内容了如指掌 

ftell

ftell - C++ Reference (cplusplus.com)

  • 返回文件指针相对于起始位置的偏移量
  • 头文件#include
  • 参数FILE*stream 是文件指针类型的指针变量 指向了一个文件信息区
#include
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	long int ret=ftell(pf);
	printf("%d", ret);

	close(pf);
	pf = NULL;
	return 0;
}

 C语言之文件操作篇(2)_第22张图片

rewind

rewind - C++ Reference (cplusplus.com)

  • rewind让文件指针的位置回到文件的起始位置
  • 头文件#include
  • 参数FILE*stream 是文件指针类型的指针变量 指向了一个文件信息区
  •  无返回值
#include
int main()
{
	FILE* pf = fopen("data.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
	int ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	ch=fgetc(pf);
	printf("%c", ch);

	rewind(pf);

	ch = fgetc(pf);
	printf("%c", ch);

	close(pf);
	pf = NULL;
	return 0;
}

C语言之文件操作篇(2)_第23张图片

特别提醒:以上函数移动的都是文件状态指针,和指向文件信息区的指针没有关系(它没动!) 

文件结束的判定

  • 文本文件读取是否结束,判断返回值是否为EOF (fgetc),或者NULL(fgets)
  1. fgetc判断是否为EOF.
  2. fgets判断返回值是否为NULL.
  • 二进制文件的读取结束判断,判断返回值是否小于<实际要读的个数
  1. fread判断返回值是否小于实际要读的个数。

feof

被错误使用的feof

牢记:在文件读取过程中,不能用feof函数的返回值直接用来判断文件的是否结束。

 而是应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束。

 feof - C++ Reference (cplusplus.com)

 

A non-zero value is returned in the case that the end-of-file indicator associated with the stream is set.Otherwise, zero is returned. 

  • feof的作用是:当文件读取结束的时候,判断是读取结束的原因是否是:遇到文件结尾结束。
  • feof在文件读取结束后,用来判断文件是否因为读取过程中遇到了文件结束标志而结束!

ferror

ferror - C++ Reference (cplusplus.com)

 

A non-zero value is returned in the case that the error indicator associated with the stream is set.Otherwise, zero is returned. 

  • ferroe在文件读取结束后,用来判断文件是否因为读取过程中遇到错误而结束!

练习1:将data1.txt的内容拷贝到data2.txt上面去。

#include
int main()
{
	FILE* pfread = fopen("data1.txt", "r");
	if (pfread == NULL)
	{
		perror("fopen");
		return 1;
	}

	FILE* pfwrite = fopen("data2.txt", "w");
	if (pfwrite == NULL)
	{
		perror("fopen");
		fclose(pfread);
		pfread = NULL;
		return 1;
	}
	int ch = 0;
	while( (ch = fgetc(pfread)) != EOF)
	{
		fputc(ch, pfwrite);
	}

	if (ferror(pfread))
		puts("I/O error when reading");
	else if (feof(pfread))
		puts("End of file reached successfully");

	if (ferror(pfwrite))
		puts("I/O error when reading");
	else if (feof(pfwrite))
		puts("End of file reached successfully");

	fclose(pfread);
	pfread = NULL;
	fclose(pfwrite);
	pfwrite = NULL;
	return 0;
}

 练习2:二进制文件

#include
enum { SIZE = 5 };
int main(void)
{
  double a[SIZE] = {1.0,2.0,3.0,4.0,5.0};
  double b = 0.0;
  size_t ret_code = 0;
  FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
  fwrite(a, sizeof(*a), SIZE, fp); // 写 double 的数组
  fclose(fp);
  fp = fopen("test.bin","rb");
  // 读 double 的数组
  while((ret_code = fread(&b, sizeof(double), 1, fp))>=1)
 {
    printf("%lf\n",b);
 }
  if (feof(fp))
    printf("Error reading test.bin: unexpected end of file\n");
  else if (ferror(fp)) {
    perror("Error reading test.bin");
 }
  fclose(fp);
  fp = NULL;
}

✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正!

希望大家继续坚持在每天敲代码的路上。其实,没有人会一直带着你往前走,你自己一定要成为自己的救赎。

代码---------→【唐棣棣 (TSQXG) - Gitee.com】

联系---------→【邮箱:[email protected]

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