文件IO编程三:系统IO应用实例+标准IO

一、系统IO另外一个应用实例。----触摸屏

1、触摸屏设备文件

        /dev/input/event0

        

2、关于触摸屏的一些概念

        (1)事件  --event

        当一些外接设备(比如鼠标、键盘、触摸屏 wifi  按键)接入到嵌入式平台(开发板)时,而且当这些设备的状态(触摸屏被摸了一下,键盘被按下  wifi 被连接)发生变化时,称之为事件的发生。

        (2)输入子系统--input

        当事件发生的时候,就是由输入子系统 帮助 系统自动计算这些事件产生的值。

3、如果我们想要把触摸屏的坐标读取出来 ?

 

         (1)打开触摸屏设备文件

                int tsfd = open("/dev/input/event0");

        (2)分析 触摸屏的数据是什么数据类型?

                int  char

        (3)读取触摸屏数据

                read(tsfd,buf,1024);

        (4)根据拿到的触摸屏数据做出相应的处理

                if(x >200 && x<400 )
                {
                        printf("打开qq\n");
                }

        (5)关闭触摸屏文件

4、触摸屏数据类型 到底 是什么?

        如果想要知道从文件event0中读取出来的数据是什么类型,首先必须先知道输入子系统计算完这个结果之后,这个结果是以什么方式存放在event0这个文件。

        (1)在linux中,输入子系统如何描述一个事件?

        使用结构体去描述,该结构体是被定义在 /usr/include/linux/input.h:

        事件结构体
        struct input_event {
            struct timeval time; 事件发生的时间
            __u16 type; 事件的类型
            __u16 code; 事件的编码 
            __s32 value; 事件的值
        };

        (2)事件的类型:

        /*
         * Event types
         */

        #define EV_SYN            0x00  异步通信
        #define EV_KEY            0x01 按键事件  压力事件
        #define EV_REL            0x02
        #define EV_ABS            0x03 触摸屏事件
        #define EV_MSC            0x04
        #define EV_SW            0x05
        #define EV_LED            0x11
        #define EV_SND            0x12
        #define EV_REP            0x14
        #define EV_FF            0x15
        #define EV_PWR            0x16
        #define EV_FF_STATUS    0x17
        #define EV_MAX            0x1f
        #define EV_CNT            (EV_MAX+1)

        (3)code   事件的编码(对事件进行进一步描述):

        #define ABS_X            0x00  触摸屏事件发生之后,进一步描述是X轴事件
        #define ABS_Y            0x01  触摸屏事件发生之后,进一步描述是Y轴事件

        #define BTN_TOUCH        0x14a  压力事件   330

        (4)value; 事件的值:

        BTN_TOUCH 压力事件中   
        按下  value 1

        松开  value 0

5、练习

(1)用手按一次,显示一次坐标,怎么做?自己将触摸屏的代码进行封装成一个接口(函数)。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include <sys/mman.h>

#include <linux/input.h>//触摸屏头文件



int ts_func(int *x,int *y)
{
	//1)打开触摸屏设备文件 ---
	int tsfd = open("/dev/input/event0",O_RDWR);
	if(tsfd == -1)
	{
		printf("open /dev/input/event0 error\n");
		return -1;
	}
	
	while(1)
	{
		//2)分析 触摸屏的数据是什么数据类型???
		//输入子系统计算完结果之后会将这个结果存储在结构体中
		struct input_event info;
		
		//3)读取触摸屏数据
		read(tsfd,&info,sizeof(struct input_event));
		
		//判断当前发生的是触摸屏事件,接着判断触发的是X轴事件
		if(info.type == EV_ABS && info.code == ABS_X)
			*x = info.value;
		//判断当前发生的是触摸屏事件,接着判断触发的是Y轴事件
		if(info.type == EV_ABS && info.code == ABS_Y)
			*y = info.value;
		
		//松开的时候,才进行打印  type:1 code:330 value:0
		if(info.type == EV_KEY && info.code == BTN_TOUCH &&  info.value == 0){
			
			//如果你的板子是黑色的,那么需要进行坐标转换(1024,600) ---(800,480)
			//*x = (*x *1.0)/1024 * 800 ;
			//*y = (*y *1.0)/600  * 480 ;
			
			printf("(%d,%d)\n",*x,*y);
			break;
		}
	}
	
	//5)关闭触摸屏文件
	close(tsfd);
}

int main()
{
	int ts_x,ts_y;
	
	while(1)
	{
		ts_func(&ts_x,&ts_y); //调用一次,得到一个坐标值
		
		if(ts_x<300)
			printf("左边\n");
		else if(ts_x>=300 && ts_x<600) 
			printf("右边\n");
		else{
			printf("退出\n");
			break;
		}
	}
	
	
	return 0;
}


结果:

(2)修改色盲检测系统:点击左边,显示上一种颜色,点击右边,显示下一种颜色。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include <sys/mman.h>


#include <linux/input.h>//触摸屏头文件
/*
练习2:修改色盲检测系统
			点击左边,显示上一种颜色
			点击右边,显示下一种颜色
*/
#define COLORCOUNT	4

#define BLACK	0x00000000//黑色
#define RED		0x00ff0000//红色
#define GREEN	0x0000ff00//绿色
#define YELLOW	0x00ffff00//黄色

int ts_func(int *x,int *y)
{
	//1)打开触摸屏设备文件 ---
	int tsfd = open("/dev/input/event0",O_RDWR);
	if(tsfd == -1)
	{
		printf("open /dev/input/event0 error\n");
		return -1;
	}
	
	while(1)
	{
		//2)分析 触摸屏的数据是什么数据类型???
		//输入子系统计算完结果之后会将这个结果存储在结构体中
		struct input_event info;
		
		//3)读取触摸屏数据
		read(tsfd,&info,sizeof(struct input_event));
		
		//判断当前发生的是触摸屏事件,接着判断触发的是X轴事件
		if(info.type == EV_ABS && info.code == ABS_X)
			*x = info.value;
		//判断当前发生的是触摸屏事件,接着判断触发的是Y轴事件
		if(info.type == EV_ABS && info.code == ABS_Y)
			*y = info.value;
		
		//松开的时候,才进行打印  type:1 code:330 value:0
		if(info.type == EV_KEY && info.code == BTN_TOUCH &&  info.value == 0){
			
			//如果你的板子是黑色的,那么需要进行坐标转换(1024,600) ---(800,480)
			//*x = (*x *1.0)/1024 * 800 ;
			//*y = (*y *1.0)/600  * 480 ;
			
			printf("(%d,%d)\n",*x,*y);
			break;
		}
	}
	
	//5)关闭触摸屏文件
	close(tsfd);
	
}


int  main()
{
	//红色
	int colorbuf[800*480];
	int i;
	int ts_x,ts_y;
	
	//1、打开液晶屏文件/dev/fb0,
	int lcdfd = open("/dev/fb0",O_RDWR);
	if(lcdfd == -1)
	{
		printf("open lcd error\n");
		return -1;
	}
	//将液晶屏文件 通过内存映射mmap的方式 映射到虚拟内存空间的某一块空间上,得到这一块内存的起始地址,后续我们操作这片内存空间,就相当于直接操作液晶屏文件
	int *lcd_p = mmap(NULL, //你要映射的内存空间的起始地址,为NULL系统自动给你分配
		 800*480*4,//你要映射的内存空间的大小
		 PROT_READ|PROT_WRITE,//映射的权限 
		 MAP_SHARED,//1、进程共享  2、对应的文件会同步发生变化 	
		 lcdfd,//映射液晶屏文件
		 0//偏移量 ,默认为0
		);
	//int lcd_p[800*480];	---液晶屏数组
		
		
	if(lcd_p == MAP_FAILED)
	{
		printf("mmap lcd error\n");
		return -1;
	}
	
	int color[COLORCOUNT] = {BLACK,RED,GREEN,YELLOW};
	int flag=0;
	
	while(1)
	{
		ts_func(&ts_x,&ts_y); //调用一次,得到一个坐标值
		
		//相当于直接操作液晶屏文件lcd_p
		for(i=0; i<800*480; i++)
		{
			lcd_p[i] = color[flag];//
		}
		
		if(ts_x<400)//左边
		{
			printf("左边\n");
			flag--;
			//如果当前是在开头 flag== -1,那么点击左边,回到末尾
			if(flag == -1)
				flag = COLORCOUNT-1; //3
		}
		else{//右边
			printf("右边\n");
			flag++;
			//如果当前是在末尾 flag == 4,那么点击右边,回到开头
			if(flag == COLORCOUNT)
				flag = 0;
		}
	} 
	
	
	
	//解除映射
	munmap(lcd_p, 800*480*4);
	
	//3、关闭文件
	close(lcdfd);
	
}

二、标准IO

1、系统IO和标准IO的区别

(1)系统IO接口具有通用性 和 简约性,适用于绝大部分的文件,功能比较简洁,单一。一般用来访问硬件设备文件。

(2)标准IO接口功能丰富,其实是系统IO的二次封装,属于标准C库中的函数接口。 所以在man手册中第三章。一般用来访问普通文件。

2、如何使用标准IO访问普通文件?(fopen)

打开文件 --- man 3 fopen

头文件:
        #include <stdio.h>
函数原型:
        FILE *fopen(const char *path, const char *mode);
函数作用:
        打开或者创建一个文件。

参数:
        path---》你要打开的文件的路径名。  路径+名字
        mode---》你要以什么方式打开这个文件。

            r      以可读的方式打开文件.  文件光标在开头,文件必须要存在,如果不存在会返回失败
            r+     以可读可写的方式打开文件,文件光标在开头,文件必须要存在,如果不存在会返回失败
            w      以可写的方式打开文件,如果文件不存在则创建,如果存在则清空. 文件光标在开头
            w+     以可读可写的方式打开文件.  如果文件不存在则创建,如果存在则清空.文件光标在开头
            a      以追加的方式打开文件(可写). 如果文件不存在则创建.  文件光标在末尾
            a+     以追加的方式打开文件(可读可写). 如果文件不存在则创建.  文件光标在末尾

返回值:
        成功返回 文件指针fp
        失败返回  NULL

注意: 
        fopen("./1.txt", "rb+"); //b 以二进制的方式打开文件    ---》原封不动地读取 和 写入     
        fopen("./1.txt", "r+"); // 以文本的方式打开文件        
                linux: \n---占一个字符 0x0A   window: \n 占两个字符

以后写程序时,fopen第二个参数  写上一个 b  
            rb+
            wb+
            wb 

3、关闭文件(fclose)

#include <stdio.h>

int fclose(FILE *fp);

#include<stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include <sys/mman.h>


int main()
{
	//打开文件
	FILE *fp = fopen("./b.txt","w+");
	if(fp == NULL)
	{
		printf("fopen error\n");
		return -1;
	}

	
	//关闭文件
	fclose(fp);
	
	return 0;
}

三、默认打开的三个 文件

(1)系统IO ----文件描述符

其实在系统启动的时候,就会默认打开3个文件,分别是 “标准输入”  “标准输出” "标准出错"。

这些其实就是 宏定义 ,在/usr/include/unistd.h 头文件中:

/* Standard file descriptors.  */
#define	STDIN_FILENO	0	/* Standard input.  */    “标准输入”  ---》对象:键盘
#define	STDOUT_FILENO	1	/* Standard output.  */   “标准输出”   ---》对象:屏幕
#define	STDERR_FILENO	2	/* Standard error output.  */  "标准出错" ---》对象:屏幕

(2)标准IO-----文件指针

/* Standard streams.  */
extern struct _IO_FILE *stdin;		/* Standard input stream.  */
extern struct _IO_FILE *stdout;		/* Standard output stream.  */
extern struct _IO_FILE *stderr;		/* Standard error output stream.  */
/* C89/C99 say they're macros.  Make them happy.  */

#define stdin stdin  	“标准输入”  ---文件指针 
#define stdout stdout   “标准输出” 
#define stderr stderr  	"标准出错" 

总结:

                    文件描述符                            文件指针 
标准输入    STDIN_FILENO                        stdin
标准输出    STDOUT_FILENO                   stdout
标准出错    STDERR_FILENO                   stderr

四、关于标准IO的读写操作

1、按块数 读取文件(fread)

man 3  fread

#include <stdio.h>

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

参数:
        ptr:将读取的数据存储到这里 
        size:每一块的大小
        nmemb:多少块数据
        stream: 你要读取哪个文件,将这个文件的文件指针传递进来
返回值:
        函数正常返回读取到的块数,以下这几种情况将会返回不等于块数的返回值:        
        1、文件读到末尾了,剩余的内容不够读满那么多块,那么他就会返回读取到的多少块的整块,
        而后面不满一块的内容,虽然也会读取,但是他不会记录在返回值中
                判断文件是否读完了 feof    int feof(FILE *stream);
        2、文件读取出错了
                判断文件是否出错  int ferror(FILE *stream);

比如: 

例子1:

文件的大小 98个字节
        fread(buf,20,5,fp); //read(fd,buf,100);
    返回值:4
前面4块是整块,最后一块只有18个字节,不是整块,也会读取,但是不会记录在返回值中


文件的大小 98个字节
    fread(buf,100,1,fp);

    返回值: 0

#include<stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>

#include <sys/mman.h>


int main()
{
	//打开文件
	FILE *fp = fopen("./b.txt","r");
	if(fp == NULL)
	{
		printf("fopen error\n");
		return -1;
	}
	char buf[1024]={0};
	int ret = fread(buf,100,1,fp);
	
	printf("ret:%d\n",ret);
	printf("buf:%s\n",buf);
	
	//关闭文件
	fclose(fp);
	
	return 0;
}

2、写入文件(fwrite)

man 3 fwrite

#include <stdio.h>

size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);

参数:
        ptr: 你要写入的数据
        size:每一块的大小
        nmemb:多少块数据
        stream:你要将数据写入哪个文件,将这个文件的文件指针传递进来
返回值:
        函数正常返回写入的块数,以下有一种情况返回不等于块数的返回值:
                文件写入出错了

3、练习

使用标准IO实现 文件拷贝(./fcopy  srcfile  destfile)

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//使用标准IO实现 文件拷贝

// ./fcopy  srcfile  destfile


#define  MEMSIZE	(1024*1024*5)

//程序内存的初始化
char *initMem()
{
	char *buf = malloc(MEMSIZE); //buf 数据缓冲区 
	if(buf == NULL)
	{
		printf("malloc error\n");
		return NULL;
	}
	
	return buf;
}

void freeMem(char *buf)
{
	free(buf);
}

int main(int argc,char*argv[])
{
	//1、先判断当前命令行有没有传输了源文件和目标文件
	if(argc<3)
	{
		//如果没有传递进来,则提示
		printf("请传输源文件 和 目标文件的名字\n");
		return -1;
	}
	//2、以可读的方式 打开源文件
	FILE *srcfp = fopen(argv[1],"rb");
	if(srcfp == NULL)
	{
		printf("%s open error\n",argv[1]);
		return -1;
	}
	//3、以可写的方式 打开目标文件,如果没有存在则创建,如果存在则清空
	FILE *destfp = fopen(argv[2],"wb");
	if(destfp == NULL)
	{
		printf("%s open error\n",argv[2]);
		return -1;
	}
	
	char *buf = initMem(); //buf 数据缓冲区 
	if(buf == NULL)
	{
		printf("malloc error\n");
		return -1;
	}
	
	int r_ret ;
	int readByteCount=0;//记录读取的字节数
	int pos1,pos2;
	//4、循环地读取源文件数据
	while(1)
	{
		bzero(buf,MEMSIZE);
		//记录文件当前的位置
		pos1 = ftell(srcfp);
		//从源文件读取数据
		r_ret = fread(buf,MEMSIZE/5,5,srcfp);
		if(r_ret<5)//fread函数正常返回读取到的块数,以下这几种情况将会返回不等于块数的返回值
		{
			//如果源文件读取完了,则退出
			if(feof(srcfp) != 0)
			{
				//再次记录文件的位置
				pos2 = ftell(srcfp);
				readByteCount += pos2-pos1;
				printf("last byte:%d\n",pos2-pos1);
				//把剩余的部分写入目标文件
				fwrite(buf,pos2-pos1,1,destfp);
				printf("已读:%f M\n",readByteCount*1.0/1024/1024);
				break;
			}
			//2、文件读取出错了
			if(ferror(srcfp) !=0)
			{
				printf("%s fread error\n",argv[1]);
				break;
			}
		}
		readByteCount += MEMSIZE;		
		//将读取出来的数据写入到目标文件		
		fwrite(buf,MEMSIZE/5,5,destfp);
		
		printf("已读:%f M\n",readByteCount*1.0/1024/1024);
	}

	freeMem(buf);
	
	//5、关闭两个文件
	fclose(srcfp);
	fclose(destfp);
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值