【嵌入式学习】IO进程线程-Day1-IO基础

一、IO基础

1.1 IO的概念

1> i:input o:output

2> 所谓IO就是程序对外部设备完成信息的交换过程

1.2 已经接触的IO

1> #include 该头文件中封装了有关标准的输入输出相关的操作

2> 接触的IO函数:printf\scanf、getchar\putchar、gets\puts

3> 以上函数的特点:输入输出的目标都是终端文件

1.3 分类

1> 标准IO:依赖于库函数,程序提供的函数,该函数中封装了一个缓冲区,调用函数时先将要操作的数据放入缓冲区,等待缓冲区刷新时机到了后,该函数在调用系统调用函数将数据同一刷入到内核空间,效率较高

2> 文件IO:依赖于系统调用,内核提供的函数,每次进行该调用,都会执行一次系统调用,效率较低

3> 区别:标准IO = 文件IO + 缓冲区

4> 每次调用系统调用函数,进程进入阻塞状态,程序就会从用户空间向内核空间进行一次切换,当IO事件完成后,进程会重新进入就绪状态

【嵌入式学习】IO进程线程-Day1-IO基础_第1张图片

1.4 常用接口函数

1> 标准IO:printf\scanf、getchar\putchar、gets\puts 、fopen\fclose、fprintf\fscanf、fgetc\fputc、fgets\fputs、fread\fwrite、fseek\ftell\rewind

2> 文件IO:open、close、read、write、lseek

【嵌入式学习】IO进程线程-Day1-IO基础_第2张图片

二、标准IO

2.1 FILE文件结构体类型

1> 该类型是在stdio.h中已经定义了的用于描述一个文件的所有信息的结构体类型

2> 由fopen函数打开某个文件,所返回的操作句柄就是文件指针,后期可以通过该指针操作文件

2> 该结构体中的内容为

如何找到FILE结构体

1、到根目录下的usr目录中的include目录中:ctags -R 创建索引

2、在任意地方输入指令:vi -t 要查询的名字

3、找到对应的文件数字后,输入数字并回车,查看对应文件信息

4、使用 ctrl + ] 继续向前追 使用ctrl + [ :回退

245 struct _IO_FILE {
 
 
 
257   char* _IO_buf_base;   /* Start of reserve area. */   缓冲区的起始地址
258   char* _IO_buf_end;    /* End of reserve area. */     缓冲区的结束地址
 
267 
268   int _fileno;                          //文件描述符,用于调用系统调用函数
}
 

3> 特殊的文件指针有三个,这三个指针,主要是针对于终端操作的

stdin:标准输入流指针

stdout:标准输出流指针

stderr:标准错误输出流指针

2.2 fopen函数

       #include 
 
       FILE *fopen(const char *pathname, const char *mode);
功能:通过指定的模式,打开指定的文件,并返回该文件的地址
参数1:是一个字符串,表示要打开的文件,是要打开的文件的路径。 例如: "/usr/include/stdio.h"
参数2:也是一个字符串,表示要打开的文件模式,以下面的字符开头,表示不同的打开方式
        r      Open  text file for reading.  The stream is positioned at
              the beginning of the file.    //以只读的形式打开一个文件,光标定位在开头,如果文件不存在,则open函数报错
 
       r+     Open for reading and writing.  The stream  is  positioned
              at the beginning of the file.  //以读写的形式打开一个文件,光标定位在开头,如果文件不存在,则open函数报错
 
       w      Truncate  file  to  zero  length  or create text file for
              writing.  The stream is positioned at  the  beginning  of
              the file.                    //以只写的形式打开文件,如果文件不存在,则创建文件,如果文件存在则清空文件内容,光标定位在开头
 
       w+     Open  for reading and writing.  The file is created if it
              does not exist, otherwise it is truncated.  The stream is
              positioned at the beginning of the file.    //以读写的形式打开文件,如果文件不存在,则创建文件,如果文件存在则清空文件内容,光标定位在开头
 
       a      Open for appending (writing at end of file).  The file is
              created if it does not exist.  The stream  is  positioned
              at the end of the file.    //以结尾写的形式打开文件,如果文件不存在,则创建文件,如果文件存在则结尾写,光标定位在开结尾
 
       a+     Open  for reading and appending (writing at end of file).
              The file is created if it does not  exist.   The  initial
              file  position  for  reading  is  at the beginning of the
              file, but output is always appended to  the  end  of  the
              file.     //以读写追加的形式打开文件,如果文件不存在,则创建文件,如果文件存在则结尾写,开头读
返回值:成功返回打开的文件的指针,失败返回NULL并置位错误码

2.3 fclose函数

       #include 
 
       int fclose(FILE *stream);
功能:关闭指定的文件
参数:由open函数打开的文件指针
返回值:成功返回0, 失败返回EOF(-1),并置位错误码
#include
 
int main(int argc, const char *argv[])
{
	FILE *fp;        //定义文件指针
 
	fp = fopen("./test.txt", "r");
	//功能:以只写的形式,打开当前路径下的test.txt文件
	//如果文件不存在,则open函数会报错
	//如果文件存在,则正常打开,可以读取数据
	//fp = fopen("./test.txt", "w");
	//功能:以只写的形式打开当前路径下的test.txt文件
	//如果文件不存在,则创建一个文件
	//如果文件存在,则清空文件中的内容
	if(NULL == fp)        //说明文件打开失败
	{
		printf("open file error\n");
		return -1;
	}
 
	printf("文件打开成功\n");
 
	//关闭文件
	fclose(fp);
 
	return 0;
}
 

2.4 错误码

1> 错误码:当调用内核提供的函数出错时,内核空间向用户空间返回一个错误信息,每个错误信息会对应一个编号,内核空间向用户空间返回的就是该编号,这个编号就是错误码

2> 查看错误码:vi -t EIO

  5 #define EPERM        1  /* Operation not permitted */
  6 #define ENOENT       2  /* No such file or directory */
  7 #define ESRCH        3  /* No such process */
  8 #define EINTR        4  /* Interrupted system call */
  9 #define EIO      5  /* I/O error */
 10 #define ENXIO        6  /* No such device or address */
 11 #define E2BIG        7  /* Argument list too long */
 12 #define ENOEXEC      8  /* Exec format error */
 13 #define EBADF        9  /* Bad file number */
 14 #define ECHILD      10  /* No child processes */
 15 #define EAGAIN      11  /* Try again */
 16 #define ENOMEM      12  /* Out of memory */
 17 #define EACCES      13  /* Permission denied */
 18 #define EFAULT      14  /* Bad address */
 19 #define ENOTBLK     15  /* Block device required */
 20 #define EBUSY       16  /* Device or resource busy */
 21 #define EEXIST      17  /* File exists */
 22 #define EXDEV       18  /* Cross-device link */
 23 #define ENODEV      19  /* No such device */
 24 #define ENOTDIR     20  /* Not a directory */
 25 #define EISDIR      21  /* Is a directory */
 

3> 处理错误码和错误信息

#include
int error;
错误码定义在当前头文件中,如果想要使用错误码变量,需要加上该头文件
 
       #include 
 
       char *strerror(int errnum);
功能:将给定的错误码,转换成错误信息
参数:错误码
返回值:错误信息
 
 
       #include 
 
       void perror(const char *s);
功能:输出最新的错误码对应的错误信息
参数:字符串,是一个提示信息,原样输出,并在后面加上一个冒号,冒号后是错误信息
返回值:无
#include
#include
#include
 
int main(int argc, const char *argv[])
{
 
	//定义文件指针
	FILE * fp = NULL;
 
	//以只读的形式打开当前路径下的test.txt文件
	fp = fopen("./test.txt", "r"); 
	if(NULL == fp)
	{
	//	printf("open flie error,errno = %d,errmsg = %s\n",\
				errno, strerror(errno));
		perror("fopen error");       //输出当前错误码对应的错误信息
 
		return -1;
	}
 
 
	//关闭文件
	fclose(fp);
 
	return 0;
}
 

2.5 fputc、fgetc函数

       #include 
 
       int fputc(int c, FILE *stream);
功能:向指定的文件中输出一个字符
参数1:要输出的字符数据
参数2:要输出的文件指针,由fopen函数打开后指向的某个文件
返回值:成功返回输出的字符,失败返回EOF
 
       #include 
 
       int fgetc(FILE *stream);
功能:从指定的文件中读取一个字符
参数:文件指针
返回值:读取光标所在位置的一个字符,失败返回EOF
 
#include
 
int main(int argc, const char *argv[])
{
	//定义文件指针
	FILE *fp = NULL;
 
	//以只写的形式打开文件
	if((fp=fopen("./test.txt", "w")) == NULL)
	{
		perror("fopen error");
		return -1;
	}
 
	//向文件中写入字符数据
	fputc('H', fp);
	fputc('e', fp);
	fputc('l', fp);
	fputc('l', fp);
	fputc('o', fp);
	fputc('\n', fp);
 
	//关闭文件
	fclose(fp);
 
	//重新以只读的形式打开文件
	if( (fp = fopen("./test.txt", "r")) == NULL)
	{
		perror("fopen r error");
		return -1;
	}
 
	//定义一个变量,用于从文件中读取数据
	char buf = 0;
	while(1)
	{
		buf = fgetc(fp);     //从外部文件中读取一个字符
		if(buf == EOF)
		{
			break;
		}
 
		printf("%c", buf);           //输出到终端上
	}
 
	/*
	while((buf = fgetc(fp)) != EOF)
	{
		printf("%c", buf);
	}*/
 
 
	//关闭文件
	fclose(fp);
 
	return 0;
}
 

练习:

1> 使用fgetc统计给定的某个文件的行号

2> 使用fgetc和fputc完成两个文件的拷贝

#include
 
int main(int argc, const char *argv[])
{
	//判断外部传参的个数
	if(argc != 3)
	{
		printf("input file error\n");
		printf("usage:./a.out srcfile dstfile\n");
		return 0;
	}
 
	//定义两个指针,分别指向源文件和目标文件
	FILE *srcfp, *dstfp;
	//以只读的形式打开源文件
	if((srcfp = fopen(argv[1], "r")) == NULL)
	{
		perror("srcfile open error");
		return -1;
	}
	//以只写的形式打开目标文件
	if((dstfp = fopen(argv[2], "w")) == NULL)
	{
		perror("dstfile open error");
		return -1;
	}
 
 
	//定义一个字符搬运工
	char buf = 0;
	while((buf = fgetc(srcfp)) != EOF)
	{
		//将从源文件中读取的字符写入到目标文件
		fputc(buf, dstfp);
	}
 
	//关闭两个文件
	fclose(srcfp);
	fclose(dstfp);
 
	printf("拷贝成功\n");
 
	return 0;
}
 

2.6 fputs、fgets函数

       #include 
 
       int fputs(const char *s, FILE *stream);
功能:将指定的字符串输出到指定的文件中去
参数1:要输出的字符串的起始地址
参数2:要被输出的文件
返回值:成功返回输出字符的个数,失败返回-1
 
 
       #include 
 
       char *fgets(char *s, int size, FILE *stream);
功能:从指定的文件中读取最多size-1个字符到s数组中
参数1:存放字符串的容器起始地址
参数2:要读取的字符个数,最大是size-1个字符,如果文件中的字符个数小于size-1,
        则有多少读多少,最后也会补个'\0',读取过程中,如果遇到'\n'也会结束一次读取
        并且将'\n'放入字符串中,后面再补个'\0'
参数3:要被读取的文件
返回值:成功返回读取字符的个数,失败返回EOF
#include
 
int main(int argc, const char *argv[])
{
	
	//定义文件指针
	FILE *fp = NULL;
	if((fp = fopen("./test.txt", "w")) == NULL)
	{
		perror("fopen error");
		return -1;
	}
 
	//将一个字符串输出到文件中
	fputs("hello world\n", fp);
	fputs("hello world\n", fp);
	fputs("hello world\n", fp);
	fputs("hello world\n", fp);
	//关闭文件
	fclose(fp);
 
	//以只读的形式重新打开文件
	if((fp = fopen("./test.txt", "r")) == NULL)
	{
		perror("fopen error");
		return -1;
	}
 
	//定义一个字符串的搬运工
	char buf[8] = "";
	while(1)
	{
		char * res = fgets(buf, sizeof(buf), fp);
 
		if(res == NULL)
		{
			break;
		}
 
		printf("%s\n", buf);
	}
 
 
 
	//关闭文件
	fclose(fp);
 
 
	return 0;
}
 

【嵌入式学习】IO进程线程-Day1-IO基础_第3张图片

2.7 关于时间的函数

       #include 
 
       time_t time(time_t *tloc);
功能:获取系统当前的时间,从197011000秒开始累计的秒数
参数:时间变量的指针
返回值:返回系统累计的时间
使用方式1time_t sys_time;
time(&sys_time);
 
使用方式2time_t sys_time = time(NULL);
 
 
 
         struct tm *localtime(const time_t *timep);
功能:将给定的秒数,转变成时间类型的结构体指针
参数:由time函数获取的秒数的地址
返回值:时间结构体类型的指针
    struct tm {
               int tm_sec;    /* Seconds (0-60) */   秒数
               int tm_min;    /* Minutes (0-59) */    分钟
               int tm_hour;   /* Hours (0-23) */     小时
               int tm_mday;   /* Day of the month (1-31) */  月中天数
               int tm_mon;    /* Month (0-11) */      月份+1
               int tm_year;   /* Year - 1900 */       年份 + 1900
               int tm_wday;   /* Day of the week (0-6, Sunday = 0) */ 周中天数
               int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */ 年中天数
               int tm_isdst;  /* Daylight saving time */     夏令时
           };
 

2.8 sprintf函数

int sprintf(char *str, const char *format, ...);
功能:将后面的格式串转换为字符串放入一个给定的字符数组中
参数1:存放转换格式串的字符数组首地址
参数2:格式串
参数3:不定参数,个数由参数2中的格式控制符决定
返回值:成功返回转换的字符个数,失败返回-1
#include
#include
 
int main(int argc, const char *argv[])
{
	//定义一个时间类型的变量,存储秒数
	time_t sys_time = 0;
 
	//调用时间函数,获取秒数
	time(&sys_time);        //sys_time = time(NULL);
 
	//将秒数转变成时间结构体类型
	struct tm* t = localtime(&sys_time);
 
	//printf("%4d-%02d-%02d  %02d:%02d:%02d\n",t->tm_year+1900, t->tm_mon+1,\
			t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
 
	char buf[128] ="";     //存储格式串的容器
 
	sprintf(buf,"%4d-%02d-%02d  %02d:%02d:%02d\n",t->tm_year+1900, t->tm_mon+1,\
			t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
 
	printf("buf = %s\n", buf);
 
	return 0;
}
 

作业:

1> 使用fgets统计一个文件的行号

/*
 * Filename: 01line.c
 * Author: linus
 * Date: 2023-12-29
 * Version: 1.0
 *
 * Description: The purpose of this code.
 */

#include 

int main(int argc, const char *argv[]){
	FILE *fp=NULL;
	if(argc!=2){
		puts("输入有误");
		puts("eg:/a.out test.txt");
		return -1;
	}
	if((fp=fopen(argv[1],"r"))==NULL){
		perror("fopen error:");
		return -1;
	}

	int line=0;
	//char buff=0;

	char buff[128];
	while(NULL!=fgets(buff,sizeof(buff),fp)){
		line++;
	}

	printf("此文件有%d行\n",line);
	fclose(fp);
	return 0;
}

2> 使用fgets、fputs完成两个文件的拷贝

/*
 * Filename: 02cp.c
 * Author: linus
 * Date: 2023-12-29
 * Version: 1.0
 *
 * Description: The purpose of this code.
 */
 
#include 

int main(int argc, const char *argv[]){
	if(argc!=3){
		puts("输入有误!");
		return -1;
	}
	FILE* fp=NULL;
	FILE*fp_cp=NULL;
	if((fp=fopen(argv[1],"r"))==NULL){
		perror("打开1文件出错:");
		return -1;
	}

	if((fp_cp=fopen(argv[2],"w"))==NULL){
		perror("打开2文件出错:");
		return -1;
	}

	char buff[1024];
	while(fgets(buff,sizeof(buff),fp)!=NULL){
		fputs(buff,fp_cp);
	}

	puts("拷贝成功!");
	fclose(fp);
	fclose(fp_cp);
	return 0;
}

3> 向文件中输出当前的系统时间

1、16:42:50
2、16:42:51
3、16:42:52
  。。。
ctrl + C
./a.out
6、17:10:03
7、17:10:04
8、17:10:05
    。。。
/*
 * Filename: 03time.c
 * Author: linus
 * Date: 2023-12-29
 * Version: 1.0
 *
 * Description: The purpose of this code.
 */

#include 
#include 
#include 

time_t sys_time = 0;
char time_buff[128] = ""; // 写入文件的字符串
struct tm *t;
int line = 1;

int get_time()
{
	time(&sys_time);
	t = localtime(&sys_time);
	sprintf(time_buff, "%4d[%4d/%02d/%02d-%02d:%02d:%02d]\n", line,
			t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);

	return t->tm_sec;
}

int main(int argc, const char *argv[])
{

	FILE *fp = NULL;
	int last_sec = -1;

	if ((fp = fopen("./time.txt", "r")) == NULL)
	{
		perror("打开文件失败:");
		// return -1;
	}

	// 先读出文件有多少行
	char buff[128];
	while (NULL != fgets(buff, sizeof(buff), fp))
	{
		line++;
	}

	while (1)
	{
		// a:以结尾写的形式打开文件,如果文件不存在,则创建文件,如果文件存在则结尾写,光标定位在开结尾
		if ((fp = fopen("./time.txt", "a")) == NULL)
		{
			perror("打开文件失败:");
			return -1;
		}
		sleep(1);

		// 每秒才写入
		if (last_sec != get_time())
		{
			last_sec = t->tm_sec;
			fputs(time_buff, fp);
			line++;
		}
		printf("%d\n", line);
		fclose(fp);
	}

	return 0;
}

你可能感兴趣的:(学习)