Linux下一切都是文件。
对于Linux下所有的资源都是文件,Linux给开发者提供了对文件操作的接口,这个接口就是文件IO。
文件IO就是Linux系统提供的对文件操作的接口。API函数接口。
1,系统IO == 》 Linux内核直接提供的对文件操作的接口
2,标准IO == 》标准库提供的对文件操作的接口(在系统IO的基础上进行封装使用)
==》open close read write lseek
1,open 函数 ==> 打开文件
NAME (函数的简介)
open, openat, creat - open and possibly create a file
SYNOPSIS
#include
#include
#include
int open(const char *pathname, int flags);
==> pathname :文件路径名 (字符串类型)
==> flags : 标志 (打开文件之后,对文件的操作权限)
O_RDONLY, //只读
O_WRONLY, //只写
or O_RDWR //读写
==》返回值:成功返回文件描述符,失败返回-1
文件描述符? ==》一个正整数。
打个比方:硬盘:一栋大楼, 文件:大楼里面的房间
操作系统:大楼管理员 open函数:找管理员申请房间钥匙。
文件描述符:对文件操作的钥匙。
例子:使用open打开一个文件 1.txt,打印文件描述符。
为什么返回值都是3?
==》返回值返回的是最小的非负整数。
==》在程序运行过程中,默认打开三个文件,占据三个文件描述符
0 1 2
标准输入 标准输出 标准出错
所以,在实际使用中,返回值为3,表示前面的三个已经被占据了。
能不能无限的打开文件? ==》验证一下
==》文件的打开数量是有限的,默认是1024个
==》如果想要不断打开文件,那么在对文件打开之后,不需要的情况下,就把文件描述符关闭。
==》关闭文件描述符 ==》 close
2,close
NAME
close - close a file descriptor
SYNOPSIS
#include
int close(int fd); //参数:需要关闭的文件描述符
3,read ==> 从文件中读取数据
NAME
read - read from a file descriptor
SYNOPSIS
#include
ssize_t read(int fd, void *buf, size_t count);
==》fd :需要读取的数据的文件的文件描述符
==》buf : 存储数据的缓冲区 ==》void * (任意类型指针)
==》count : 想要读取的字节数 --》单位是字节。
返回值:成功返回实际读取的字节数,0代表读取到文件末尾,失败返回-1
==》例子:使用read,读取一个文件的数据。
==》问题:出现乱码 ==》解决方法:读取的时候,缓冲区是64字节,只读取63字节,最后一个字节作为字符串结束标志 ‘\0’
思考:如何把一个大的文件里面的内容全部读取出来?
==》循环读取,循环读取文件,什么时候结束?
==》例子,编写程序,实现cat命令的功能。 ==》用法:cat 文件名
==》循环以文本形式读取文件中的数据,每次读取到数据,都显示在屏幕上。
4,write ==> 写入数据到文件
NAME
write - write to a file descriptor
SYNOPSIS
#include
ssize_t write(int fd, const void *buf, size_t count);
==> fd : 需要写入到的目标文件的文件描述符
==> buf : 需要写入的数据的缓冲区
==> count : 需要写入的字节数
返回值:成功返回实际写入的字节数,0代表没有数据可写。失败返回-1
==》例子:把一首诗写入1.txt
/*思考: cp 命令 ==》 拷贝文件
如何使用文件IO的函数实现cp命令?
==》 cp 1.txt 2.txt
==> 分别打开1.txt和2.txt,然后循环读取1.txt中的内容,每次读到一片内容,就把这片内容写入到2.txt,如此循环,直到1.txt中的数据被读取完毕,此时拷贝完毕,关闭1.txt,2.txt*/
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[])
{
//打印提示信息
if(argc != 3)
{
printf("please input:%s " ,argv[0]);
}
//1,打开文件
int fd0 = open(argv[1],O_RDONLY);
int fd1 = open(argv[2],O_WRONLY);
if(-1 == fd0 || -1 == fd1)
{
perror("open failed!");
return -1;
}
//2,循环读取1.txt写入2.txt中
ssize_t ret;
int buf[64] = {0};
while(1)
{
memset(buf,0,sizeof(buf));//清空buf缓冲区
ret = read(fd0,buf,sizeof(buf));//ret为读到的字节数
write(fd1,buf,ret);
if(0 == ret)
break;
}
//关闭文件
close(fd0);
close(fd1);
return 0;
}
==》注意 strlen() 函数功能是计算字符串的长度
==》字符串长度怎么计算? 从第一个字符开始到 ‘\0’字符结束
==》 “hello\0world” ==> strlen ==> 5
Cp 命令在拷贝文件时候: cp 源文件名 目标文件名
==》如果目标文件名不存在,cp命令可以创建一个文件,然后实现拷贝
==》能否在打开文件的同时创建文件?
==》 使用open 可以创建文件
#include
#include
#include
int open(const char *pathname, int flags); //只能打开文件
int open(const char *pathname, int flags, mode_t mode); //可以选择创建文件
==》 flags == 如果需要创建文件 ==> O_CREAT
例:使用open函数,打开文件1.txt, 如果文件不存在则创建。
Linux下的man中文手册安装步骤:
https://mp.weixin.qq.com/s/N4b8MhoUR2-w9VX_TWoW2A
系统IO — lseek
==> lseek ==> 偏移文件指针 (默认文件读写指针都在文件开头)
NAME
lseek - reposition read/write file offset (偏移文件读写指针)
SYNOPSIS
#include
#include
off_t lseek(int fd, off_t offset, int whence);
==> fd : 文件描述符
==> offset : 偏移量 ==> 单位字节
==> whence : 从什么地方开始偏移
SEEK_SET : 文件开头
SEEK_CUR : 当前文件指针
SEEK_END : 文件末尾
==》例:假设文件 1.txt 里面的内容是 helloworld ==> 想要读取第五个字节之后的数据。
==》练习:假设文件1.txt中里面的内容是 helloworld , 设计程序,打开这个文件之后,使用lseek函数把文件读写指针指向文件末尾,然后写入 nihao. (练习时间:20:50 ~ 21:05)
系统IO的一些应用实例:
==》例如:在程序中实现用户信息的注册。
==》strcmp() : 比较两个字符串,用户登录时比较密码是否正确。实现用户登录功能。
==》实现用户注册功能(用户输入用户名+用户密码),我们可以把这信息存储到指定的文件中,在注册功能中,需要使用结构体存储用户名+密码, 再把这个结构体存入文件。
例:实现用户信息的注册功能。
思考:注册的信息如何读取出来?试着实现它。
==》写一个读取用户信息的程序,从 userinfo.txt 中读取用户信息。
标准IO
由标准库提供的功能函数
标准IO函数: fopen , fclose, fread, fwrite, fseek.
==>fputc, fgets …
==> 标准IO调用系统IO实现文件访问,以及IO功能。
2,标准IO的函数
(1)fopen ==> man 3 fopen
#include
FILE *fopen(const char *path, const char *mode);
==> path : 文件路径 (需要的打开的文件路径)
==> mode : 打开之后操作的权限 ==> 字符串
==> “r” : 只读打开,并且文件流指针指向文件开头
==> “r+” : 读写打开,文件流指针指向文件开头
==> “w” : 如果文件不存在,那就创建文件,只写打开; 如果文件存在,那就先清空文件内容,然后只写打开,文件流指针指向文件开头
==> “w+” : 如果文件不存在,那就创建文件,读写权限打开,如果文件存在,那就读写权限打开,文件流指针指向文件开头
==> “a” : 以追加的形式打开文件(打开文件之后,文件流指针指向文件末尾),如果文件不存在就创建该文件
==> “a+” : 读写权限打开文件,如果文件不存在就创建,打开之后文件读取的指针指向文件开头,文件写入的指针指向文件末尾。
返回值: FILE * (结构体指针) ==> 这个结构体在/usr/include/stdio.h里面 文件流指针。
成功返回一个文件流指针,失败返回NULL
==> 在标准IO中,程序运行是默认打开的三个文件流分别是标准输入,标准输出,标准出错
==> 标准输入 标准输出 标准出错
标准IO ==> stdin stdout stderr
系统IO ==> 0 1 2
键盘 显示屏 显示屏
==》例:使用标准IO,以读写权限打开文件1.txt,如果文件不存在则创建它。
练习:使用fopen,打开文件hello.txt,如果文件不存在就创建它,以追加的形式打开文件。(20:36 ~ 20:46)
(2)fclose ==> 关闭文件流指针
#include
int fclose(FILE *stream);
(3)fread ==> 从文件流指针中读取文件信息
fwrite ==> 从缓冲地址中写入文件信息
#include
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
==> ptr : 存储数据的缓冲区,任意类型地址。
==> size: 读取/写入的数据块的大小,单位字节
==> nmemb : 读取/写入的数据块的数量,
==> stream : 操作的文件流 (文件流指针)
返回值:成功返回实际读取或者写入的数据块的数量,失败返回小于nmemb的值,或者0.
(注意:fread函数在读取到文件末尾或者是读取失败返回值是一样,此时需要使用feof,或者ferror来检测是读取到文件末尾还是读取失败。)
一次读取或者写入的大小 = size * nmemb
==> 例:使用fwrite往文件1.txt中写入50个字节的数据。
==> 例:使用fread从文件中读取数据,一次读取20个字节,循环读取。
练习:在自己的电脑上,使用fread和fwrite 实现对文件的读写操作
1,把一首诗写入到文件 hello.txt
2,把这首诗读取打印出来。
(4) int fseek(FILE *stream, //文件流指针
long offset, //偏移的字节数
int whence); //从哪里开始偏移
SEEK_SET,文件开头 SEEK_CUR, 当前文件指针 or SEEK_END文件末尾
(5)ftell ==> 获取文件指针定位的绝对值(返回文件指针相对于文件开头的偏移量)
long ftell(FILE *stream); //文件流指针
(6)rewind ==> 把文件指针定位到文件开头
void rewind(FILE *stream); //文件流指针
==> 相当于 fseek(fp, 0, SEEK_SET)
标准IO输入输出缓冲区
缓冲区:读取数据/写入数据,数据首先会被放置到缓冲区中,等待条件满足,才会输出内容。
printf() : 标准输出函数,
==> 例如:检测缓冲区的存在。
相关功能函数:
1)fputs
int fputs(const char *s, //输出的字符串
FILE *stream); //文件流指针
==> 把字符串 “helloworld” 输出到标准出错
==> fputs(“helloworld”, stderr);
标准IO常用函数
1,输入函数
#include
int fgetc(FILE *stream); //从文件流中获取一次字符
==>例如:从标准输入获取一个字符
==> char ch = fgetc(stdin);
char *fgets(char *s, int size, FILE *stream); //从文件中获取一个字符串
==> s : 存放获取的字符串缓冲区
==> size : 获取的字符串的大小
==> stream : 文件流指针
==> 这个函数一般用在什么地方? ==> 用来获取标准输入的字符串
==> scanf(); //标准输入函数,这个函数存在一个缺陷
==> 例如:从键盘获取一个字符串放入buf中
==> scanf(“%s”, buf); 获取字符串放入buf中,在碰到空格时,就会停止,不再获取
==> fgets可以从文件流指针中获取指定数量的字符,
int getc(FILE *stream); //从文件流中获取一个字符
int getchar(void); //从标准输入中获取一个字符
==> sscanf(), fscanf();
int scanf(const char *format, ...); //从标准输入获取指定格式的数据
int fscanf(FILE *stream, const char *format, ...); //从指定的文件流指针中获取指定格式的数据
int sscanf(const char *str, const char *format, ...); //从字符串中获取指定格式的内容
==>例:sscanf 获取一个字符串中的整型数。
fscanf(stdin, “%s”, buf) === scanf(“%s”, buf);
2, 输出函数
int fputc(int c, FILE *stream); //把字符c输出到文件流 stream
// 例如: fputc(‘A‘, stdout); ==> printf(“%c”, ‘A’);
int fputs(const char *s, FILE *stream); //把字符串输出到文件流 stream中
// 例如: fputs(“hellowrold”, stdout); == printf(“helloworld”);
int putc(int c, FILE *stream);
int putchar(int c); //把字符c输出到标准输出
int puts(const char *s); //把字符串s输出到标准输出
==> printf, fprintf, sprintf
int printf(const char *format, …); //标准格式化输出函数
int fprintf(FILE *stream, const char *format, …);//格式化数据输出到指定文件流
int dprintf(int fd, const char *format, …); //格式化数据输出到指定的文件描述符
int sprintf(char *str, const char *format, …); // 把格式化数据输出到指定的内存空间
int snprintf(char *str, size_t size, const char *format, …); //把指定大小的格式化数据输出到指定的内存空间
==> sprintf() ==> 把指定的数据输出的内存中 ==> 实现数据的拼接。
==> 例如:使用sprintf实现字符串的拼接
==> 练习:从键盘输入自己的姓名和年龄, 例如: 张三 18
将输入的姓名和年龄拼接,然后输出。
用两种方法实现: sprintf + strcat
三,文件属性相关函数
1, stat ==> 查看文件详细属性
#include
#include
#include
int stat(const char *pathname, struct stat *buf);
==> pathname : 文件路径名
==> buf : 结构体指针,存放文件属性内容
返回值:成功返回0,失败返回-1
struct stat {
dev_t st_dev; /* ID of device containing file */ 文件ID
ino_t st_ino; /* inode number */ 节点号
mode_t st_mode; /* protection */ 文件类型
nlink_t st_nlink; /* number of hard links */ 文件硬链接
uid_t st_uid; /* user ID of owner */ 文件拥有者用户
gid_t st_gid; /* group ID of owner */ 文件组用户
dev_t st_rdev; /* device ID (if special file) */ 设备ID
off_t st_size; /* total size, in bytes */ 文件大小
blksize_t st_blksize; /* blocksize for filesystem I/O */ 文件系统块大小
blkcnt_t st_blocks; /* number of 512B blocks allocated */ 文件块的数量
/* Since Linux 2.6, the kernel supports nanosecond
precision for the following timestamp fields.
For the details before Linux 2.6, see NOTES. */
struct timespec st_atim; /* time of last access */
struct timespec st_mtim; /* time of last modification */
struct timespec st_ctim; /* time of last status change */
#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
==> ls -l (该命令可以得到以上结构体信息)
==> 例:使用stat函数查看指定文件的大小。
文件类型查看: ==> st_mode
S_IFSOCK 0140000 socket //套接字
S_IFLNK 0120000 symbolic link //链接文件
S_IFREG 0100000 regular file //普通文件
S_IFBLK 0060000 block device //块设备
S_IFDIR 0040000 directory //目录文件
S_IFCHR 0020000 character device //字符设备
S_IFIFO 0010000 FIFO //管道文件
//示例:
if((sb.st_mode &S_IFMT) == S_IFREG)
{
/*regular file*/
}
2,access 检测文件相关属性是否存在
#include
int access(const char *pathname, int mode);
==> pathname:检测的文件的路径名
==> mode : 检测的权限
R_OK :读权限
W_OK : 写权限
X_OK. 执行权限
F_OK 是否存在
==> 返回值 : 权限具有则返回0,否则返回-1
==> 例:使用access函数测试文件是否存在
opendir , readdir, closedir
1, opendir ==> 打开目录文件
#include
#include
DIR *opendir(const char *name);
==> name: 需要打开的目录名
==> 返回值 :成功返回一个目录流指针,失败返回 NULL
2, readdir ==> 从目录中读取数据
#include
struct dirent *readdir(DIR *dirp);
==> dirp : 目录流指针
==> 返回值 struct dirent * 的结构体指针
struct dirent {
ino_t d_ino; /* inode number */ 索引号
off_t d_off; /* not an offset; see NOTES */ 从1开始
unsigned short d_reclen; /* length of this record */ //目录项的字节数
unsigned char d_type; /* type of file; not supported // 文件类型
by all filesystem types */
char d_name[256]; /* filename */ //文件名
};
==> Linux下,文件名的大小不能超过 255 字节(因为文件名是一个字符串,而字符串要以\0结尾,所以只能有255个字节)
DT_BLK This is a block device. 块设备
DT_CHR This is a character device. 字符设备
DT_DIR This is a directory. 目录文件
DT_FIFO This is a named pipe (FIFO). 管道文件
DT_LNK This is a symbolic link. 链接文件
DT_REG This is a regular file. 普通文件
DT_SOCK 套接字文件
3,closedir 关闭目录流指针
#include
#include
int closedir(DIR *dirp);
例:使用目录IO函数,对目录进行检索。
思考:在检索目录时,将目录里面所有的文件名输出,假设想要检索特定的文件,例如,检索目录1中的所有 .c 结尾的文件,把文件名输出,如何实现?15:49 ~ 15:54
5分钟, 把想到的实现方案在公屏打出来
==> strcmp
练习:参考readdir.c ,检索指定目录下的所有 .bmp结尾的文件,打印文件名
思考:多级目录检索功能的实现。
1,LCD显示原理
==> lcd也叫做液晶显示屏,液晶屏上存在像素点。像素点上显示不同的颜色数据,组合形成用户看到的图像。
2,6818开发板LCD参数
3,如何使用LCD?
Linux下一切都是文件。 ==> LCD也是一个文件 /dev/fb0
==> 如何对LCD文件进行打开?写入?关闭?
==> 例如:在LCD上显示纯红色图像。
4,Ubuntu下控制LCD显示图像
1)从图形界面进入命令界面
==> ctrl + alt + F2
2)运行程序 sudo ./red
==> 密码:123456 (输入的时候不会回显)
3)从命令界面切换回图形界面
==> ctrl + alt + F7
1,图片显示原理
将图片文件中的数据读取出来,放在一片内存缓冲区中,然后将缓冲区的内容写入到LCD文件,由此LCD显示图片的内容。
2,图片种类
常见的图片类型有 bmp, jpg, png, gif .... (LCD是可以显示任意类型的图片的)
LCD在显示图片的时候,需要将图片中的像素点数据转换成ARGB格式。
1)图片显示的素材,选择bmp格式。
==》bmp被称为原图,bmp图片的像素点数据是不经过压缩的,原封不动的保存在 bmp文件中。
2)关于bmp图片
==> bmp图片文件中的像素点数据都是RGB , ==> 每个像素点都是3字节
3)准备一张bmp图片。==> 800*480的bmp图片。
==> 准备的bmp图片大小应该是 800*480*3 字节 ==> 1152000字节
==> 图片的实际大小比计算的大小多54字节
==> bmp图片文件中会存在54字节的头信息,在bmp图片的开头会有54字节图片头信 息,不是像素点信息,而是包含了图片标志,图片的宽度,高度等内容。
3,bmp图片特点
1) bmp图片又被称为原图,24位位图,在bmp图片中,每个像素点的内容都是RGB,占3字节
2) 所有的bmp图片都会在文件的开头具有54字节的头信息,在图片的显示过程中,这54字节不需要写入到LCD文件中。
将bmp图片文件中的像素点数据读取出来,然后直接写入LCD
出现的问题:
1)图片失真 ==》bmp图片的像素点 RGB,LCD上的像素点 ARGB
2)图片不能填满LCD ==》bmp图片的大小只有LCD的四分之三
3)图片呈现倒立
==> bmp图片的编解码。
==> 图片失真解决方案:
Bmp图片的RGB ==> ARGB数据
==> 图片呈现倒立
1)图片倒立的原因
==》bmp图片在Windows下的存储,第一个像素点在图片的左下角,而LCD的第一个像素点在LCD的左上角。
将bmp图片的数据,写入到LCD中时,显示的图片与原图关于中间的这条线对称。
==> 图片倒立解决方案
==> 把解码之后的图片数据,里面的像素点数据,交换数据的位置
1,在LCD上显示任意大小的图片
2,在LCD上任意位置显示任意大小的图片
3,在LCD上居中显示图片 (自行去完成)
人与机器的交互,”人”通过输入设备(鼠标,键盘,按键,触摸屏…)给机器输入指令,机器根据人发送的指令去完成相应的工作,通过输出设备(显示屏,音响,LED灯…)将结果反馈给”人”。
1,触摸屏的分类
·电容屏 :硬屏,工作原理:在电容屏上存在电子层,当用户手指触摸屏幕时,电子层发生击穿现象,触摸屏驱动采集触摸的位置信息。
·电阻屏 :软屏,
==> Linux系统下输入子系统头文件 /usr/include/linux/input.h
/usr/include/linux/input-event-codes.h
//输入事件结构体
struct input_event {
struct timeval time; //事件发生的时间 (系统时间)
__u16 type; //事件的类型
__u16 code; //事件的编码
__s32 value; //事件的值
};
Event types:
#define EV_SYN 0x00 //事件分割标志
#define EV_KEY 0x01 //按键事件
#define EV_REL 0x02
#define EV_ABS 0x03 //触摸屏事件
==》思考:点击触摸屏时,位置信息如何描述? ==》通过X-Y坐标系的坐标描述
Event code;
#define ABS_X 0x00 //X轴坐标编码
#define ABS_Y 0x01 //Y轴坐标编码
#define ABS_PRESSURE 0x18 //触摸屏的压力值(手指按下屏幕,压力值为正数,手指弹开,压力值为0)
点击一次屏幕得到的信息。参考ts.c, 在自己的开发板上运行程序,点击一次屏幕观察效果。
type:3, code:0, value:87
type:3, code:1, value:522 ==> 点击屏幕的坐标信息 (87, 522)
type:1, code:330, value:1 //压力值为1,表示手指按下触摸屏
type:0, code:0, value:0 //事件分割
type:1, code:330, value:0 //压力值为0,表示手指离开触摸屏
type:0, code:0, value:0 //事件分割
在6818的触摸屏驱动中,把压力值的选项放在EV_KEY 中的BTN_TOUCH
==> 优化代码: 修改程序,实现只打印点击屏幕的坐标
·例1:设计一张图片,在LCD上显示这张图片,然后点击图片上的图标,切换到另一张图片,然后结束。
==》1)设计一个图片 800*480的bmp图片
==》2)设计功能代码
实现一个简单的电子相册功能。
功能要求:
1,从指定路径下检索图片文件 --> Pic/
2,可以实现图片的上一张,下一张切换
1)设计功能界面
2)准备相册图片的素材 --> 准备一些bmp图片 (图片大小不要超过680*480)
3)设计功能代码实现相册功能
1,滑动与点击屏幕的区别
·点击屏幕 : 点击触摸屏可以得到点击屏幕的绝对值坐标
·滑动屏幕 : 在屏幕上滑动时,是相对位置的判断