Bootstrap

【嵌入式开发之标准I/O】流的刷新、定位以及格式化输出、输入

流的刷新

 int fflush(FILE *fp);
  • 成功时返回0;出错时返回EOF。

  • 将流缓冲区中的数据写入实际的文件。

  • Linux下只能刷新输出缓冲区,输入缓冲区丢弃。

  • 如果输出到屏幕使用fflush(stdout)。

流的定位

流的定位:ftell()函数 

long ftell(FILE *stream);

ftell() 函数的作用是获取文件的 当前指针位置 相对于 文件首地址 的 偏移字节数 ;

流的定位:fseek()函数 

long fseek(FILE *stream, long offset,  int whence);

描述及参数介绍

fseek()函数用来设置流 stream 的文件位置为给定的偏移 offset,参数 offset 意味着从给定的 whence 位置查找的字节数。 

  • fseek 参数whence参数:SEEK_SET/SEEK_CUR/SEEK_END

  • SEEK_SET :从距文件开头 offset 位移量为新的读写位置

  • SEEK_CUR:以目前的读写位置往后增加 offset 个位移量

  • SEEK_END:将读写位置指向文件尾后再增加 offset 个位移量

  • offset参数:偏移量,可正可负

返回值

如果成功,则该函数返回零,否则返回非零值。

流的定位:rewind()函数

void rewind(FILE *stream);
主要功能

将文件内部的位置 指针重新指向一个流( 数据流/文件)的开头。 

返回值

该函数不返回任何值。

流的定位代码实例(ftell函数、fseek函数和rewind函数)

#include <stdio.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	FILE *fp;
	char *buff;
	int ret;

	buff = (char *)malloc(20);

	if ((fp = fopen("ffseek.txt", "w+")) == NULL) {
		perror("fopen");
		return 0;
	}

	//向空文件些人字符,并输出文件指针fp的位置
	fwrite("abcdef", 6, 1, fp);
	printf("after fwrite fp = %d\n", (int)ftell(fp));

	//文件指针指向文件开头,并读取文件内容,再显示文件指针位置
	rewind(fp);
	printf("after rewind fp = %d\n", (int)ftell(fp));
	ret = fread(buff, 12, 1, fp);
	printf("fread: %s\n", buff);
	printf("after fread fp = %d\n\n", (int)ftell(fp));

	//利用SEEK_SET参数,设置偏移为3,显示文件指针位置
	fseek(fp, 3, SEEK_SET);
	printf("after SEEK_SET=3 fp = %d\n", (int)ftell(fp));

	//从指针新指向的位置开始替换字符,并显示替换后指针位置
	fwrite("aa", 2, 1, fp);
	printf("after fwrite 2 fp = %d\n", (int)ftell(fp));

	//文件指针指向文件开头,显示被替换后的内容
	rewind(fp);
	ret = fread(buff, 12, 1, fp);
	printf("after SEEK_SET=3 and instead \"aa\":%s\n", buff);
	printf("after SEEK_SET=3 fp = %d\n\n", (int)ftell(fp));

	//设置SEEK_CUR 3参数后,fp指针的位置
	fseek(fp, 3, SEEK_CUR);
	printf("after SEEK_CUR 3 fp = %d\n", (int)ftell(fp));

	//设置SEEK_END 2参数后,fp指针的位置
	fseek(fp, -2, SEEK_END);
	printf("after SEEK_END -2 fp = %d\n", (int)ftell(fp));

	return 0;
}

注意事项

  1. 文件的打开使用a模式 fseek无效

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

  3. 这三个函数只适用2G以下的文件

格式化输入和输出

格式化输出

格式化输出:printf()函数

int printf(const char *format, ...);
printf("<格式化字符串>", <参量表>);
主要功能

发送格式化输出到标准输出 stdout。

返回值

如果成功,则返回写入的字符总数,否则返回一个负数。

格式化输出:fprintf()函数

int fprintf(FILE *stream, const char *fmt, …);
主要功能

这个函数根据指定的format(格式)发送信息(参数)到由stream(流)指定的文件.因此fprintf()可以使得信息输出到指定的文件.

返回值

成功时返回输出的字符个数;出错时返回EOF。

代码实例
#include <stdio.h>

int main(int argc, const char *argv[])
{
	FILE *fp;
	char buf[100] = {0};
	int year = 2021;
	int month = 10;
	int day = 1;

	if ((fp = fopen("fprintf.txt", "w")) == NULL) {
		perror("fopen");
		return 0;
	}

	fprintf(fp,"%d-%d-%d\n", year, month, day);

    fclose(fp);

	return 0;
}
运行结果及分析
2021-10-1

首先,通过fopen函数打开文件fprintf.txt,再将year,month,day三个变量的值读取到这个文件中,再通过cat命令显示文件中的内容。

格式化输出:sprintf()函数 

int sprintf( char *buffer, const char *format [, argument] … );
主要功能

主要功能是把格式化的数据写入某个字符串。

返回值

成功时返回输出的字符个数;出错时返回EOF。

代码实例
#include <stdio.h>

int main(int argc, const char *argv[])
{
	char buf[100] = {0};
	int year = 2021;
	int month = 10;
	int day = 1;

	sprintf(buf,"%d-%d-%d", year, month, day);
	printf("%s\n", buf);

	return 0;
}
运行结果及分析
2021-10-1

将year,month,day三个变量值读取到字符串buf中,再通过printf输出。 

格式化输入 

格式化输入:scanf()函数

int scanf(const char * restrict format,...);
主要功能

按用户指定的格式从键盘上把数据输入到指定的变量之中

函数的第一个参数是格式字符串,它指定了输入的格式,并按照格式说明符解析输入对应位置的信息并存储于可变参数列表中对应的指针所指位置。每一个指针要求非空,并且与字符串中的格式符一一顺次对应。

函数 scanf() 是从标准输入流stdin (标准输入设备,一般指向键盘)中读内容的通用子程序,可以说明的格式读入多个字符,并保存在对应地址的变量中。

返回值

scanf函数返回成功读入的数据项数,读入数据时遇到了“文件结束”则返回EOF。

 格式化输入:fscanf()函数

int fscanf(FILE *stream, const char *format, ...);
主要功能

根据数据格式 const char * format , 从文件 FILE * stream 中 , 读取数据存储到 [argument...] 参数中 。 

返回值

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

代码实例
#include <stdio.h>

int main(int argc, const char *argv[])
{
	FILE *fp;
	int year;
	int month;
	int day;

	if ((fp = fopen("fprintf.txt", "r")) == NULL) {
		perror("fopen");
		return 0;
	}

	fscanf(fp,"%d-%d-%d", &year, &month, &day);
	printf("%d-%d-%d\n", year, month, day);

	fclose(fp);

	return 0;
}
运行结果及分析
2021-10-1

将fprintf.txt文件中的数据,通过fscanf读取到year,month,和day三个中,然后通过printf函数输出。

格式化输入:sscanf()函数

int sscanf(const char *str, const char *format, ...);
主要功能

sscanf函数的主要功能是从字符串读取格式化输入。

返回值 

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

代码实例
#include <stdio.h>

int main(int argc, const char *argv[])
{
	char buf[100] = {0};
	int year = 2021;
	int month = 10;
	int day = 1;

	int syear;
	int smonth;
	int sday;

	sprintf(buf,"%d-%d-%d", year, month, day);
	printf("%s\n", buf);

	sscanf(buf,"%d-%d-%d", &syear, &smonth, &sday);
	printf("%d, %d, %d\n", syear, smonth, sday);

	return 0;
}
 运行结果及分析
2021-10-1
2021, 10, 1

先通过sprintf将年月日按一定格式输出到buf字符串中,在通过sscanf函数将buf字符串中的字符分别输入到syear,smonth和sday三个变量中,最后通过printf函数输出。

实例分析

实例要求

每隔1秒向文件test.txt中写入当前系统时间,格式如下:

1,  2014-10-15 15:16:42

2,  2014-10-15 15:16:43

该程序无限循环,直到按Ctrl-C中断程序,每次执行程序时,系统时间追加到文件末尾,序号递增。

相关提示

time()用来获取系统时间(秒数)
time_t time(time_t *seconds) 1970.1.1 0:0:0
localtime()将系统时间转换成本地时间
struct tm *localtime(const time_t *timer)
struct tm {
   int tm_sec;         /* 秒,范围从 0 到 59                */
   int tm_min;         /* 分,范围从 0 到 59                */
   int tm_hour;        /* 小时,范围从 0 到 23                */
   int tm_mday;        /* 一月中的第几天,范围从 1 到 31                    */
   int tm_mon;         /* 月份,范围从 0 到 11                */
   int tm_year;        /* 自 1900 起的年数                */
   int tm_wday;        /* 一周中的第几天,范围从 0 到 6                */
   int tm_yday;        /* 一年中的第几天,范围从 0 到 365                    */
   int tm_isdst;       /* 夏令时                        */    
};

实例代码

#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <string.h>

#define FNAME "1.txt"
#define MODE "a+"

int main(int argc, const char *argv[])
{
	FILE *fp;
	time_t ctime;
	struct tm *ctimestr;
	int linecount = 0;
	char ch;

	//打开文件,打开失败直接返回
	if ((fp = fopen(FNAME, MODE)) == NULL) {
		perror("fopen");
		return -1;
	}

	//计算1.txt存储数据的行数,实现每次重新运行序号都是累增
	while ((ch = fgetc(fp)) != -1) {
		if (ch == '\n') {
			linecount++;
		}
	}
	
	//获取系统时间,转换为当地时间,并写入到文件
	while (1) {
		ctime = time(NULL);//获取系统时间
		ctimestr = localtime(&ctime);//将系统时间转换为当地时间

		//在屏幕上输出时间
		printf("%d, %04d-%02d-%02d %02d:%02d:%02d\n", linecount, ctimestr->tm_year+1900, ctimestr->tm_mon+1, ctimestr->tm_mday, 
				ctimestr->tm_hour,ctimestr->tm_min, ctimestr->tm_sec);

		//把时间输出到文件
		fprintf(fp,"%d, %04d-%02d-%02d %02d:%02d:%02d\n", linecount, ctimestr->tm_year+1900, ctimestr->tm_mon+1, ctimestr->tm_mday, 
				ctimestr->tm_hour,ctimestr->tm_min, ctimestr->tm_sec);
		fflush(fp);//写完文件记得刷新,写进磁盘

		sleep(1);
		linecount++;
	}

	fclose(fp);//关闭文件

	return 0;
}

运行结果及分析

0, 2024-07-21 00:42:45
1, 2024-07-21 00:42:46
2, 2024-07-21 00:42:47
3, 2024-07-21 00:42:48
4, 2024-07-21 00:42:49
5, 2024-07-21 00:42:50
6, 2024-07-21 00:42:51
7, 2024-07-21 00:42:52
8, 2024-07-21 00:42:53
9, 2024-07-21 00:43:17
10, 2024-07-21 00:43:18
11, 2024-07-21 00:43:19
12, 2024-07-21 00:43:20
13, 2024-07-21 00:43:21
14, 2024-07-21 00:43:22
15, 2024-07-21 00:43:23
16, 2024-07-21 00:43:24
17, 2024-07-21 00:43:25
18, 2024-07-21 00:43:26
19, 2024-07-21 00:43:27

注意事项

  • 写完文件记得fflush ,写到磁盘里面去。

  • int tm_mon;获取的值要加1是正确的月份

  • int tm_year;获取的值加1900是正确的年份

  • 每次运行都要在原来的基础上累计行号,可以用fgetc函数逐一读取文件中的字符,看能读取到多少个‘\n’则是多少行。

;