Skip to content

LV020-标准IO简介

本文主要是C语言——标准I/O基本操作的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

一、文件打开与关闭

1. 基本函数

1.1 fopen()

在 linux 下可以使用 man 3 fopen 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
FILE *fopen(char *filename, char *mode);

函数说明】该函数用于打开一个文件。

函数参数

  • filename :为文件名(可以包含文件路径),为字符串型。
  • mode :打开方式,为字符串型。

返回值】 返回值是一个文件句柄指针,函数获取的文件信息后,包括文件名、文件状态、当前读写位置等,将这些信息保存到一个 FILE 类型的结构体变量中,然后将该变量的地址返回。打开文件出错时,将返回一个空指针,也就是 NULL 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
FILE *fp;
fp = fopen("file_name","mode");

注意事项】 none

1.2 fclose()

在 linux 下可以使用 man 3 fclose命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int fclose(FILE *stream);

函数说明】该函数关闭一个打开的文件。

函数参数

  • stream :FILE类型指针变量,表示打开的文件的文件句柄。

返回值】返回值是一个整数,文件正常关闭时,返回 0 ;如果返回非 0 值则表示有错误发生。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
int ret;
ret = fclose(fp);/* fp 为文件指针 */

注意事项】 none

1.3 fopen的mode

1.3.1 mode的取值
控制读写权限的字符串(必须写)
打开方式说明
"r"以只读方式打开文件。只允许读取,不允许写入,文件必须存在,否则打开失败。
"w"以写入方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
"a"以追加方式打开文件。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。
"r+"以读写方式打开文件。既可以读取也可以写入。文件必须存在,否则打开失败。
"w+"以写入/更新方式打开文件,相当于w和r+叠加的效果。既可以读取也可以写入。如果文件不存在,那么创建一个新文件;如果文件存在,那么清空文件内容(相当于删除原文件,再创建一个新文件)。
"a+"以追加/更新方式打开文件,相当于a和r+叠加的效果。既可以读取也可以写入。如果文件不存在,那么创建一个新文件;如果文件存在,那么将写入的数据追加到文件的末尾(文件原有的内容保留)。
控制读写方式的字符串,与上边连用(可以省略)
"t"文本文件。如果不写,默认为"t"。
"b"二进制文件。

【说明】读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部。例如, "rb" 、 "r+b" 、 "rb+" 。

【注意】对于 Windows 平台,确保换行符不会有所影响,最好用 "t" 来打开文本文件,用 "b" 来打开二进制文件。对于 Linux 平台,则没什么区别。

1.3.2 打开标准 I/O 的六种不同方式小结
限制rwar+w+a+
文件已存在
删除文件以前内容
流可以读
流可以写
流可以在尾部写

2. 使用实例

c
/* 头文件 */
#include <stdio.h>
#include <errno.h>
#include <string.h>

int main(int argc,char*argv[])
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	int fpget;
	/* 以读的方式打开当前路径下文件 */
	fp = fopen("1f_open.txt", "r");
	/* 判断是否打开成功 */
	if(fp == NULL)
	{
		/* 错误提示输出方式1 */
		printf("Open file Failed\n");
		/* 错误提示输出方式2 */
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		/* 错误提示输出方式3 */
		printf("fopen:%s\n", strerror(errno));/* errno 存放错误号,由系统生成  */
	}
	else
	{
		/* 成功提示输出方式1 */
		printf("Open file success\n");
		/* 成功提示输出方式2 */
		perror("fopen");
		/* 成功提示输出方式3 */
		printf("fopen:%s\n", strerror(errno));/* errno 存放错误号,由系统生成  */
		fpget = fclose(fp);
		if(fpget == 0)
		{
			printf("file close success\n");
			perror("fclose");
			printf("fclose:%s\n", strerror(errno));/* errno 存放错误号,由系统生成  */
		}
		else
		{
			perror("fclose");
			printf("fclose:%s\n", strerror(errno));/* errno 存放错误号,由系统生成  */
		}
	}
	return 0;
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

然后,终端会有以下信息显示(想要打开的文件不存在):

shell
Open file Failed
fopen: No such file or directory
fopen:No such file or directory

二、文件读写

1. 按字符读写

1.1 fgetc()

在 linux 下可以使用 man 3 fgetc 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int fgetc(FILE *stream);

函数说明】该函数读取文件中的一个字符。。

函数参数

  • stream :FILE类型指针变量,表示打开的文件的文件句柄。

返回值】返回值是 int 类型不是 char 类型,主要是为了扩展返回值的范围。读取成功时返回读取到的字符,读取到文件末尾或读取失败时返回 EOF 。EOF 是 end of file 的缩写,表示文件末尾,是在 stdio.h 中定义的宏,它的值是一个负数,往往是 -1 ,但是不绝对是 -1 ,也可以是其他负数,这要看编译器的规定。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
char ch;
FILE *fp = fopen("file_name", "r");
ch = fgetc(fp); /* fp 为文件指针。 */

注意事项

(1)在文件内部有一个位置指针(并非 C 语言中的指针,仅仅是一个标志),用来指向当前读写到的位置,也就是读写到第几个字节。在文件打开时,该指针总是指向文件的第一个字节。使用 fgetc() 函数后,该指针会向后移动一个字节,所以可以连续多次使用 fgetc() 读取多个字符。

(2)如何区分 EOF 是读取完毕还是读取出错?读取完毕和读取出错都会返回 EOF ,此时我们可以借助下边的两个函数来确定到底是哪种情况:

c
int feof(FILE *stream);   /* 当指向文件末尾时返回非0值,否则返回0。 */
int ferror(FILE *stream); /* 出错时返回非0值,否则返回0值。 */

上边的两个函数都在 stdio.h 文件中,使用之前需要加上这个头文件。

1.2 fputc()

在 linux 下可以使用 man 3 fputc 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int fputc(int c, FILE *stream);

函数说明】该函数向指定的文件中写入一个字符。

函数参数

  • c :为需要写入的字符。
  • stream :FILE类型指针变量,表示打开的文件的文件句柄。

返回值】 返回值是一个 int 型整数,写入成功时返回写入的字符,失败时返回 EOF 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
char ch = 'a';
int ret;
FILE *fp = fopen("file_name", "w");
ret = fputc(ch, fp);/* fp 为文件指针 */

注意事项】 每写入一个字符,文件内部位置指针向后移动一个字节。

1.3 使用实例

c
/* 头文件 */
#include <stdio.h>

void test_fgetc();
void test_fputc();

int main(int argc,char*argv[])
{
	// test_fgetc();
	test_fputc();
	return 0;
}

/**
 * @Function: int fgetc(FILE *stream);
 * @Description: 按字符读取文件内容,每读完一个字符,读写指针就会后移。
 */
void test_fgetc()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	int rec;
	/* 1. 以读的方式打开当前路径下文件 */
	fp = fopen("01fget_put.txt", "r");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 读取文件内容 */
	rec = fgetc(fp);
	printf("get char:%c\n", rec);
	rec = fgetc(fp);
	printf("get char:%c\n", rec);
	rec = fgetc(fp);
	printf("get char:%c\n", rec);
	if(rec == -1)
	{
		perror("fgetc");
		fclose(fp);
		return;
	}
	/* 4. 关闭文件 */
	fclose(fp);
}

/**
 * @Function: int  fputc(int c, FILE *stream);
 * @Description: 按字符输出到文件中
 */
void test_fputc()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	int rec;
	int wrc;
	/* 1. 以追加的方式打开当前路径下文件 */
	fp = fopen("01fget_put.txt", "a+");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 写入文件内容 */
	wrc = 'H';
	rec = fputc(wrc,fp);
	if(rec == -1)
	{
		perror("fputc");
		fclose(fp);
		return;
	}
	putchar(wrc);
	putchar('\n');
	/* 4. 关闭文件 */
	fclose(fp);
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

2. 按行读写

2.1 fgets()

在 linux 下可以使用 man 3 fgets()命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
char *fgets(char *s, int size, FILE *stream);

函数说明】该函数读取文件一行中指定大小字节的数据,至多读取一行。

函数参数

  • s :为读取的字符串所存放的位置,可以是一个字符数组。

  • size :为读取的字节数。

  • stream :为文件指针变量。

返回值】 回值是 char 类型的指针,读取成功时返回字符数组首地址,也即 s ;读取失败时返回 NULL ;如果开始读取时文件内部指针已经指向了文件末尾,那么将读取不到任何字符,也返回 NULL 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
FILE * fp;
char * ret;
char buff[100];
fp = fopen("file_name", "r");
ret = fgets(buff, 20, fp); /* fp 为文件指针。 */

注意事项

(1)读取到的字符串会在末尾自动添加 '\0' , size 个字符也包括 '\0' 。即实际只读取到了 size-1 个字符,如果希望读取 32 个字符, size 的值应该为 33 。

(2)在读取到 size-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束。这就意味着,不管 size 的值多大, fgets() 最多只能读取一行数据,不能跨行。

(3) fgets() 遇到换行时,会将换行符一并读取到当前字符串(若是在 size 字节内遇到换行的话)。

2.2 fputs()

在 linux 下可以使用 man 3 fputs 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int fputs(const char *s, FILE *stream);

函数说明】该函数向指定的文件中写入一个字符串。

函数参数

  • s :为要写入的字符串。
  • stream :为文件指针变量。

返回值】 返回值是一个 int 型整数,写入成功时返回非负数,失败时返回 EOF 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
FILE * fp;
int rec;
fp = fopen("file_name", "a+");
rec = fputs("string",fp);/* fp 为文件指针 */

注意事项】fputs 将缓冲区s中的字符串输出到 stream ,不追加 '\n' 。

2.3 使用实例

c
/* 头文件 */
#include <stdio.h>

void test_fgets();
void test_fputs();

int main(int argc,char*argv[])
{
	// test_fgets();
	test_fputs();
	return 0;
}

/**
 * @Function: char *fgets(char *s, int size, FILE *stream);
 * @Description: 按行读取文件内容。
 */
void test_fgets()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	char * ret;
	char buff[100];
	/* 1. 以读的方式打开当前路径下文件 */
	fp = fopen("02fgets_puts.txt", "r");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 读取文件内容 */
	ret = fgets(buff, 20, fp);
	printf("buff=%s\n",buff);
	ret = fgets(buff, 20, fp);
	printf("buff=%s\n",buff);
	if(ret == NULL)
	{
		perror("fgets");
		fclose(fp);
		return;
	}
	
	/* 4. 关闭文件 */
	fclose(fp);
}

/**
 * @Function: int fputs(const char *s,  FILE *stream);
 * @Description: 按行输出到文件中
 */
void test_fputs()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	int rec;
	/* 1. 以追加的方式打开当前路径下文件 */
	fp = fopen("02fgets_puts.txt", "a+");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 写入文件内容 */
	rec = fputs("\nHello World!",fp);
	if(rec == -1)
	{
		perror("fputs");
		fclose(fp);
		return;
	}
	/* 4. 关闭文件 */
	fclose(fp);
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

3. 按数据块读写

3.1 fread()

在 linux 下可以使用 man 3 fread 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
/* size_t 是在 stdio.h 和 stdlib.h 头文件中使用 typedef 定义的数据类型,表示无符号整数,也即非负数,常用来表示数量。*/
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

函数说明】该函数从指定文件中读取块数据。即若干个字节的数据,可以是一个字符,可以是一个字符串,可以是多行数据,并没有什么限制。

函数参数

  • ptr :为内存区块的指针,它可以是数组、变量、结构体等。

  • size :为读取数据块的字节数。

  • nmemb :为要读写的数据块的块数。

  • stream :为文件指针变量。

返回值】 返回值是 size_t 类型,读取成功时返回成功读取的块数,也即 nmemb 。若如果返回值小于 nmemb (一般是 EOF ),则可能读到了文件末尾,也可能发生了错误,可以用 ferror() 或 feof() 检测。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
FILE * fp;
char * buff;
size_t ret;
fp = fopen("03fread_write.txt", "r");
buff=(char*)malloc(100);/* 100字节,malloc要加上<stdlib.h>头文件 */
ret = fread(buff, 10, 1, fp);/* fp 为文件指针。 */

注意事项】 fread() 函数可以读取多行,即它遇到 '\n' 的时候也会进行读取。

3.2 fwrite()

在 linux 下可以使用 man 3 fwrite 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

函数说明】该函数向指定的文件中写入一个块数据。

函数参数

  • ptr :为内存区块的指针,它可以是数组、变量、结构体等,为要写入的数据。

  • size :为写入数据块的字节数。

  • nmemb :为要写入的数据块的块数。

  • stream :为文件指针变量。

返回值】返回值是一个 size_t 类型,写入成功时返回成功写入的块数,也即 nmemb 。若如果返回值小于 nmemb (一般是 EOF ),则是发生了错误。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
struct student stu = {"fanhua", 18, "male"};
FILE * fp;
size_t ret;
fp = fopen("file_name", "w");
ret = fwrite(&stu, sizeof(stu), 1, fp); /* fp 为文件指针 */

注意事项】 数据写入完毕后,位置指针在文件的末尾,要想读取数据,必须将文件指针移动到文件开头,如何移动,后边会有说明。

3.3 使用实例

c
/* 头文件 */
#include <stdio.h>
#include <stdlib.h>

struct student
{
	char name[16];
	int age;
	char sex[8];
};

void test_fread();
void test_fwrite();

int main(int argc,char*argv[])
{
	test_fread();
	// test_fwrite();
	return 0;
}

/**
 * @Function: size_t fread(void *ptr, size_t size, size_t n, FILE *fp);
 * @Description: 按对象读取文件内容。
 */
void test_fread()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	/* 定义一个存储字符的缓冲区 */
	char * buff;
	size_t ret;
	/* 1. 以读的方式打开当前路径下文件 */
	fp = fopen("03fread_write.txt", "r");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 为缓冲区申请内存空间 */
	buff=(char*)malloc(100);/* 100字节 */
	if(buff == NULL)
	{
		printf("buff malloc failed\n");
		return;
	}
	/* 3. 读取文件内容 */
	ret = fread(buff, 30, 1, fp);
	if(ret == -1)
	{
		perror("fread");
		fclose(fp);
		free(buff);
		return;
	}
	printf("buff=%s\n", buff);
	/* 4. 关闭文件 */
	fclose(fp);
	free(buff);
}

/**
 * @Function: size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);
 * @Description: 按对象写入到文件
 */
void test_fwrite()
{
	/* 定义一个学生信息结构体变量 */
	struct student stu = {"fanhua", 18, "male"};
	struct student stu1;
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	size_t ret;
	/* 1. 以写入的方式打开当前路径下文件 */
	fp = fopen("03fread_write.bin", "w");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 写入文件内容 */
	ret = fwrite(&stu, sizeof(stu), 1, fp);
	if(ret == -1)
	{
		perror("fwrite");
		fclose(fp);
		return;
	}
	else
	{
		printf("write struct student success!\n");
	}
	/* 4. 关闭文件 */
	fclose(fp);

	/* 下边为读取刚才写入的二进制文件 */
	/* 1. 以读的方式打开当前路径下文件 */
	fp = fopen("03fread_write.bin","r");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 读取文件内容 */
	ret = fread(&stu1, sizeof(stu), 1, fp);
	if(ret == -1)
	{
		perror("fread");
		fclose(fp);
		return;
	}
	printf("name=%s,age=%d,sex=%s\n",stu1.name,stu1.age,stu1.sex);
	/* 4. 关闭文件 */
	fclose(fp);
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

三、流的操作

1. 流的刷新

其实我们在前边进行文件的写入操作时可以观察一下,若是程序没有执行完毕的时候,我们打开相应的文件,会发现文件中依然什么数据都没有,只有等程序结束,关闭文件后,文件中才会有内容,当我们对文件进行写入的时候,只是将内容写入了缓冲区,却并没有直接写入磁盘文件,当然我们可以执行关闭文件的语句来使数据写入磁盘文件,但是关闭后我们就不能再对这个文件进行操作了,不想关闭文件,又想将数据写入磁盘,怎么做呢?我们可以使用 fflush 函数来实现。

1.1 fflush()

在 linux 下可以使用 man 3 fflush命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int fflush(FILE *stream);

函数说明】该函数清空文件缓冲区,如果文件是以写的方式打开 的,则把缓冲区内容写入文件。

函数参数

  • stream :文件指针变量。

返回值】 返回值是 int 类型,成功返回 0 ,失败返回 EOF ,错误代码存于 errno 中。指定的流没有缓冲区或者只读打开时也返回 0 值。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
FILE * fp;
char * buff;
size_t ret;
fp = fopen("03fread_write.txt", "r");
buff=(char*)malloc(100);/* 100字节,malloc要加上<stdlib.h>头文件 */
ret = fread(buff, 10, 1, fp);/* fp 为文件指针。 */

注意事项】 fflush() 也可用于标准输入( stdin )和标准输出( stdout ),用来清空标准输入输出缓冲区。

1.2 使用实例

c
/* 头文件 */
#include <stdio.h>
#include <unistd.h>


void test_none_fflush_stdout();
void test_fflush_stdout();
void test_none_fflush_file();
void test_fflush_file();

int main(int argc,char*argv[])
{
	// test_none_fflush_stdout();
	test_fflush_stdout();
	// test_none_fflush_file();
	// test_fflush_file();
	return 0;
}
/**
 * @Function: 
 * @Description: 无回车换行,也不刷新文件流的情况
 */
void test_none_fflush_stdout()
{
	printf("qidaink\n");/* 有换行,此句将会输出 */
	printf("fanhua");/* 无换行,后边也没有会换行的语句,将不会有输出 */
	
	while(1)
	{
		sleep(1);
	}
}

/**
 * @Function: 
 * @Description: 无回车换行,刷新文件流到标准输出。
 */
void test_fflush_stdout()
{
	printf("qidaink\n");/* 有换行,此句将会输出 */
	printf("fanhua");/* 无换行,后边也没有会换行的语句,将不会有输出 */
	fflush(stdout);/* 此行的存在将会显示上边第二个printf的输出 */
	while(1)
	{
		sleep(1);
	}
}

/**
 * @Function: 
 * @Description: 无回车换行,也无刷新文件流情况。
 */
void test_none_fflush_file()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	/* 1. 以写的方式打开当前路径下文件 */
	fp = fopen("01fflush.txt", "w");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 写入数据 */
	fwrite("Hello World!", 12, 1, fp);
	while(1)
	{
		sleep(1);
	}

}

/**
 * @Function: 
 * @Description: 无回车换行,刷新文件流到文件。
 */
void test_fflush_file()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	/* 1. 以写的方式打开当前路径下文件 */
	fp = fopen("01fflush.txt", "w");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 写入数据 */
	fwrite("Hello World!", 12, 1, fp);
	fflush(fp);/* 此行的存在将会写入文件 */
	while(1)
	{
		sleep(1);
	}

}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

2. 流的定位

有时候我们想从任意位置开始读写文件怎么办?其实当我们进行文件读写的时候,内部是有一个文件指针的,记录着已经打开的文件的许或者写的位置。

2.1 ftell()

在 linux 下可以使用 man 3 ftell 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
long ftell(FILE *stream);

函数说明】该函数返回给定流 stream 的当前文件读写指针的位置。

函数参数

  • stream :文件指针变量。

返回值】 返回值是long类型,返回的是位置标识符的当前值,出错时返回EOF

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
FILE * fp;
printf("ftell(fp)=%ld\n", ftell(fp));/* fp 为文件指针。 */

注意事项】 只适用2G以下的文件。

2.2 fseek()

在 linux 下可以使用 man 3 fseek 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int fseek(FILE *stream, long offset, int whence);

函数说明】该函数设置流 stream 的文件位置为给定的偏移offset

函数参数

  • stream:文件指针变量。
  • offset:相对 whence 的偏移量,以字节为单位,可正可负。
  • whence:添加偏移 offset 的位置。 whence 参数可取的值如下:
常量描述
SEEK_SET文件的开头
SEEK_CUR文件指针的当前位置
SEEK_END文件的末尾
【**返回值**】 返回值是一个`int`类型,成功时返回`0`,出错时返回`EOF` 。

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
fseek(fp, 3, SEEK_SET);/* SEEK_SET从距文件开头 offset 位移量为新的读写位置 */

注意事项

(1)追加模式(如a模式)打开的文件fseek无效。

(2)只适用2G以下的文件。

2.3 rewind()

在 linux 下可以使用 man 3 rewind 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
void rewind(FILE *stream);

函数说明】该函数将流定位到文件开始位置。

函数参数

  • stream :文件指针变量。

返回值】 none

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
rewind(fp);

注意事项

(1)rewind(fp); 相当于 fseek(fp,0,SEEK_SET);

(2)只适用2G以下的文件。

2.4 使用实例

c
/* 头文件 */
#include <stdio.h>

void test_ftell();
void test_fseek();
void test_rewind();

int main(int argc,char*argv[])
{
	// test_ftell();
	test_fseek();
	// test_rewind();
	return 0;
}

/**
 * @Function: long ftell(FILE *stream);
 * @Description: 成功时返回流的当前读写位置,出错时返回EOF。
 */
void test_ftell()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	int rec;
	/* 1. 以读的方式打开当前路径下文件 */
	fp = fopen("02fposition.txt", "r");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 读取文件内容 */
	rec = fgetc(fp);
	printf("get char:%c ftell(fp)=%ld\n", rec, ftell(fp));
	rec = fgetc(fp);
	printf("get char:%c ftell(fp)=%ld\n", rec, ftell(fp));
	rec = fgetc(fp);
	printf("get char:%c ftell(fp)=%ld\n", rec, ftell(fp));
	if(rec == -1)
	{
		perror("fgetc");
		fclose(fp);
		return;
	}
	/* 4. 关闭文件 */
	fclose(fp);
}

/**
 * @Function: long fseek(FILE *stream, long offset,  int whence);
 * @Description: 定位一个流,成功时返回0,出错时返回EOF 
 */
void test_fseek()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	int rec;
	/* 1. 以读的方式打开当前路径下文件 */
	fp = fopen("02fposition.txt", "r");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 读取文件内容 */
	rec = fgetc(fp);
	printf("get char:%c ftell(fp)=%ld\n", rec, ftell(fp));
	rec = fgetc(fp);
	printf("get char:%c ftell(fp)=%ld\n", rec, ftell(fp));
	fseek(fp, 3, SEEK_SET);/* SEEK_SET从距文件开头 offset 位移量为新的读写位置 */
	printf("fseek: ftell(fp)=%ld\n", ftell(fp));
	rec = fgetc(fp);
	printf("get char:%c ftell(fp)=%ld\n", rec, ftell(fp));
	if(rec == -1)
	{
		perror("fgetc");
		fclose(fp);
		return;
	}
	/* 4. 关闭文件 */
	fclose(fp);
}

/**
 * @Function: void rewind(FILE *stream);
 * @Description: 将流定位到文件开始位置
 */
void test_rewind()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	int rec;
	/* 1. 以读的方式打开当前路径下文件 */
	fp = fopen("02fposition.txt", "r");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 读取文件内容 */
	rec = fgetc(fp);
	printf("get char:%c ftell(fp)=%ld\n", rec, ftell(fp));
	rec = fgetc(fp);
	printf("get char:%c ftell(fp)=%ld\n", rec, ftell(fp));
	rewind(fp);
	printf("rewind: ftell(fp)=%ld\n", ftell(fp));
	rec = fgetc(fp);
	printf("get char:%c ftell(fp)=%ld\n", rec, ftell(fp));
	
	if(rec == -1)
	{
		perror("fgetc");
		fclose(fp);
		return;
	}
	/* 4. 关闭文件 */
	fclose(fp);
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

3. 流的错误判断

很多对流进行操作的函数在失败或者到达文件末尾的时候,都可能会返回EOF,那怎么区分呢?可以使用下边两个函数进行判断:

c
#include <stdio.h>

int ferror(FILE *stream);/* 返回1表示流出错;否则返回0 */
int feof(FILE *stream);  /* 返回1表示文件已到末尾;否则返回0*/

四、格式化输入输出

1. 格式化输出

格式化输出就是将数据按照一定的格式输出到文件或者字符串。

1.1 fprintf()

在 linux 下可以使用 man 3 fprintf 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int fprintf(FILE *stream, const char *format, ...);

函数说明】该函数格式化写入数据到文件。

函数参数

  • stream:文件指针变量。
  • format:格式化字符串,与printf一致,format 标签属性是
c
 %[flags][width][.precision][length]specifier

返回值】返回值是int类型,成功时返回输出的字符个数;出错时返回EOF

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
FILE * fp;
size_t ret;
fp = fopen("file_name", "w");
ret = fprintf(fp, "%d-%d-%d", 2022, 5, 4);

注意事项】 如果将 fp 设置为 stdout,那么 fprintf() 函数将会向显示器输出内容,与 printf 的作用相同。

1.2 sprintf()

在 linux 下可以使用 man 3 sprintf命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int sprintf(char *str, const char *format, ...);

函数说明】该函数向指定的字符串中格式化写入数据。

函数参数

  • str:指向一个字符数组的指针,该数组存储了 C字符串。。
  • format:格式化字符串,与printf一致,format 标签属性是:
c
 %[flags][width][.precision][length]specifier

返回值】返回值是int类型,如果成功,则返回写入的字符总数,不包括字符串追加在字符串末尾的空字符,如果失败,则返回EOF

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
char buff[100]= { 0 };
sprintf(buff,"%d-%d-%d", 2022, 5, 4);

注意事项】 none

1.3 使用实例

c
/* 头文件 */
#include <stdio.h>
#include <stdlib.h>


void test_sprintf();
void test_fprintf();

int main(int argc,char*argv[])
{
	test_sprintf();
	// test_fprintf();
	return 0;
}

/**
 * @Function: int fprintf(FILE *stream, const char *fmt, …);
 * @Description: 格式化输出到文件
 */
void test_fprintf()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;
	size_t ret;
	/* 1. 以写入的方式打开当前路径下文件 */
	fp = fopen("03fprintf_sprintf.txt", "w");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 写入文件内容 */
	ret = fprintf(fp, "%d-%d-%d", 2022, 5, 4);
	if(ret == -1)
	{
		perror("fwrite");
		fclose(fp);
		return;
	}
	else
	{
		printf("write struct student success!\n");
	}
}

/**
 * @Function: int sprintf(char *s, const char *fmt, …);
 * @Description: 格式化输出到字符串
 */
void test_sprintf()
{
	char buff[100]= { 0 };
	
	sprintf(buff,"%d-%d-%d", 2022, 5, 4);
   
    printf("%s\n", buff);
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

2. 格式化输入

格式化输入就是按照一定的格式从文件或者字符串读取指定的格式数据。

2.1 fscanf()

在 linux 下可以使用 man 3 fscanf 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int fscanf(FILE *stream, const char *format, ...);

函数说明】该函数从流stream 读取格式化输入,即从文件格式化读取数据。

函数参数

  • stream:文件指针变量。
  • format:格式化字符串,与scanf一致,format 标签属性是:
c
 %[flags][width][.precision][length]specifier

返回值】返回值是int类型,如果成功,该函数返回成功匹配和赋值的个数,如果到达文件末尾或发生读错误,则返回 EOF

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
FILE * fp;
int year, month, day;
fp = fopen("file_name", "r");
fscanf(fp,"%d-%d-%d",&year, &month, &day);

注意事项】如果将 fp 设置为 stdin,那么 fscanf() 函数将会从键盘读取数据,与 scanf 的作用相同。

2.2 sscanf()

在 linux 下可以使用 man 3 sscanf 命令查看该函数的帮助手册。

c
/* 需包含的头文件 */
#include <stdio.h>

/* 函数声明 */
int sscanf(const char *str, const char *format, ...);

函数说明】该函数从字符串读取格式化输入。

函数参数

  • str:是格式化输入数据的来源。
  • format:格式化字符串,与scanf一致,format 标签属性是:
c
%[flags][width][.precision][length]specifier

返回值】返回值是int类型,如果成功,该函数返回成功匹配和赋值的个数。如果到达文件末尾或发生读错误,则返回 EOF

使用格式】一般情况下基本使用格式如下:

c
/* 需要包含的头文件 */
#include <stdio.h>

/* 至少应该有的语句 */
char buff[100] = "2022-5-4";
int syear, smonth, sday;
sscanf(buff,"%d-%d-%d",&syear, &smonth, &sday);

注意事项】 none

2.3 使用实例

c
/* 头文件 */
#include <stdio.h>
#include <stdlib.h>


void test_sscanf();
void test_fscanf();

int main(int argc,char*argv[])
{
	// test_sscanf();
	test_fscanf();
	return 0;
}

/**
 * @Function: int sscanf(const char *str, const char *format, ...);
 * @Description: 从字符串格式化输入
 */
void test_sscanf()
{
	int syear;
	int smonth;
	int sday;

	char buff[100] = "2022-5-4";

	sscanf(buff,"%d-%d-%d",&syear, &smonth, &sday);

	printf("%s\n", buff);
}

/**
 * @Function: int fscanf(FILE *stream, const char *format, ...);
 * @Description: 从文件格式化输入
 */
void test_fscanf()
{
	/* 定义一个文件结构体指针变量 */
	FILE * fp;

	int year;
	int month;
	int day;


	/* 1. 以读取的方式打开当前路径下文件 */
	fp = fopen("04fscanf_sscanf.txt", "r");
	/* 2. 判断是否打开成功 */
	if(fp == NULL)
	{
		perror("fopen");/* 将上一个函数发生错误的原因输出到标准错误(stderr)。 */
		return;
	}
	/* 3. 格式化读取文件内容 */
	fscanf(fp,"%d-%d-%d",&year, &month, &day);

	printf("%d,%d,%d\n", year, month, day);

	fclose(fp);
}

在终端执行以下命令编译程序:

shell
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序