当前位置:   article > 正文

第十六篇,LINUX文件IO编程概念以及系统IO函数open,close,read,write使用。_o_rdwr

o_rdwr

一、linux IO编程学习大纲。
1、文件IO概念、文件概念、文件类型。
2、系统IO  --> 如何使用系统IO接口访问/写入/读取/关闭文件。
3、系统IO文件描述符的概念?文件描述符与文件的关系?研究文件描述符的值。
4、文件偏移量。
5、系统IO应用实例:lcd液晶设备、触摸屏。
6、系统IO另外一个处理lcd液晶设备方式:内存映射。
7、标准IO  --> 如何使用标准IO接口访问/写入/读取/关闭文件。
8、标准IO  --> 一系列处理字符/字符串函数。
9、标准IO应用实例:显示bmp格式图片。
10、目录IO  -->  访问目录、切换工作路径、读取目录、关闭目录。
11、将文件知识点逐渐地加入小孩的项目中。

二、文件IO概念。
1、什么是文件?
在linux下,一切都是文件。
普通文件: 图片、视频、音乐、压缩包、PPT...
设备文件: led灯、蜂鸣器、lcd液晶设备、触摸屏..

结论:除了我们常见的文件是文件之外,linux系统还会把硬件设备当做是文件,所以在linux眼中,所有的东西都是文件。

2、什么是IO?
IO       --->  input/output    -->  输入输出
文件IO   --->  对文件输入/输出  -->  写入数据到文件/从文件中读取数据出来

3、如何实现文件读取/写入?
不需要用户自己写自定义函数,因为在linux下,已经有现成的函数来实现。

访问文件方式有两种:
系统IO    --->  系统调用    ---> 第2手册   System calls (functions provided by the kernel)
标准IO    --->  库调用      ---> 第3手册   Library calls (functions within program libraries)

4、什么时候使用系统IO?什么时候使用标准IO?
当你访问设备文件(led灯、触摸屏、蜂鸣器...)时,就是使用系统IO接口来访问的。
例如:写了一个温湿度传感器驱动    --> 那么就使用系统IO接口来访问温湿度传感器。

当你访问普通文件(图片、文本文档..)时,就是使用标准IO接口来访问。
例如:你想打开test.txt这个文件    --> 那么就使用标准IO接口来访问这个文件。

5、文件类型。   -->  7种
'-'   普通文件         --->  标准IO
'd'   目录文件         --->  目录IO
'l'   链接文件
'p'   管道文件         --->  系统IO
's'   套接字文件       --->  有一套独特的方式来访问
'c'   字符设备文件     --->  系统IO
'b'   块设备文件       --->  系统IO

三、如何使用系统IO接口来处理文件?
1、如何打开文件?   --->  open()    --->  man 2 open

    //头文件   --> 只要工程中使用了open函数,就一定要包含以下三个头文件。
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>

    //函数原型
   int open(const char *pathname, int flags);

参数:
    pathname: 需要打开的那个文件的路径。
    flags: 操作文件的权限。(三个必须选一个)
           O_RDONLY, O_WRONLY, or O_RDWR
         只读      只写       可读可写

返回值:
    成功: new file descriptor  新的文件描述符  (最小,非负,没有使用过的数字)
    失败: -1

思考题:open函数什么时候会打开失败?
1)你访问的文件不存在。
2)如果文件本身权限不允许的,那么操作权限不对,也会失败。
   例如: 某一个文件本身权限"-wx-wx-wx",如果使用O_RDONLY/O_RDWR去打开文件,那么就会失败。

2、如何关闭文件?   --->  close()   --> man 2 close

   //头文件    --> 只要工程中使用了close函数,就一定要包含以下头文件
   #include <unistd.h>

   //函数原型
   int close(int fd);

参数:
    fd: 需要关闭的那个文件的文件描述符。

返回值:
    成功:0
    失败:-1


    例子: 假设家目录下有一个文件名字叫ggg.txt,现在要求写一个程序去打开该文件:
           如果打开成功,则输出open ggg.txt success! 如果打开失败,则输出open ggg.txt error!
       接下来,就关闭该文件。
           如果关闭成功,则输出close ggg.txt success!如果关闭失败,则输出close ggg.txt error!


#include <stdio.h>      //for printf
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close

int main(int argc,char *argv[])
{
    //1. 打开ggg.txt文件
    int fd;
    fd = open("ggg.txt",O_RDWR);
    if(fd >= 0)
    {
        printf("open ggg.txt success!\n");
    }
    else{
        printf("open ggg.txt error!\n");
    }
    
    //2. 关闭ggg.txt文件
    int ret;
    ret = close(fd);
    if(ret == 0)
    {
        printf("close ggg.txt success!\n");
    }
    else{
        printf("close ggg.txt error!\n");
    }
    
    return 0;
}
 
    练习1: 看看文件描述符等于多少?  //3
    练习2: 在开发板的根目录下dev目录下,有一个文件叫fb0,现在要求写一个程序去打开该文件:
            如果打开成功,则输出open fb0 success! 如果打开失败,则输出open fb0 error!
        接下来,就关闭该文件。
            如果关闭成功,则输出close fb0 success!如果关闭失败,则输出close fb0 error! 

核心代码:
fd = open("/dev/fb0",O_RDWR);

代码需要交叉编译:
arm-linux-gcc xxx.c -o xxx

四、文件描述符?
1、什么是文件描述符?
文件描述符其实就是open函数的返回值,当open函数执行成功之后,就会返回一个非负的,最小的,没有使用过的整数。
例如:
3 = open("1.txt");   //在后面的代码中,3就是代表1.txt这个文件
4 = open("2.txt");   //在后面的代码中,4就是代表2.txt这个文件
A105 = open("关国源");

结论:将来需要处理某一个文件时候,我们不需要提供文件的名字,只需要提供文件对应的文件描述符就可以了。

2、访问文件时候,发现fd从3开始分配,说明0/1/2已经被别人使用了,究竟是谁使用了?
其实在系统启动时,就会默认打开3个文件,分别是"标准输入","标准输出","标准出错",他们其实是一些宏定义来的,是被定义在一个头文件中。
头文件的路径:/usr/include/unistd.h

/* Standard file descriptors.  */
/* 标准文件描述符 */

#define    STDIN_FILENO    0    /* 标准输入  */    ---> 对象:键盘
#define    STDOUT_FILENO    1    /* 标准输出  */    ---> 对象:屏幕
#define    STDERR_FILENO    2    /* 标准出错  */    ---> 对象:屏幕

可以理解,只要系统启动,就会默认做以下三件事情:
0 = open("标准输入");
1 = open("标准输出");
2 = open("标准出错");

3、验证文件描述符特性: 非负,最小,没有使用过的整数。
假设当前目录下: a.txt b.txt c.txt d.txt

open("a.txt");    
open("b.txt");    
open("c.txt");    
close("b.txt");
open("d.txt");   
close("a.txt");
close("c.txt");
open("a.txt");  3
open("c.txt");  5
close("d.txt");
请问这时候open("d.txt")返回值是多少? -> 4

结论: 
打开一个文件,得到一个文件描述符                                    (申请资源)
关闭一个文件,这个文件对应的文件描述符就会释放,然后别人就可以占用。  (释放资源)

4、研究文件描述符的最大值是多少?

#include <stdio.h>      //for printf
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close

int main(int argc,char *argv[])
{
    int fd;
    while(1)
    {
        fd = open("ggg.txt",O_RDWR);
        printf("fd = %d\n",fd);
        
        if(fd == -1)
        {
            break;
        }    
    }
    
    return 0;
}

最大值:1023
有效文件描述符值有1024个。


五、open函数的拓展参数。

    int open(const char *pathname, int flags, mode_t mode);

pathname:文件路径
flags:
必选(三选一)
O_RDONLY  -> 只读
O_WRONLY  -> 只写
O_RDWR    -> 可读可写

可选(0个/多个)   --> 如果选了多个,则每一个标志之间使用"|"连接起来。
O_APPEND   --> 以追加的方式来打开文件,在每一次写操作之前,文件的定位都是在文件的末尾。
O_CREAT    --> 如果想打开的那个文件不存在,则文件就会创建。
       --> mode参数就是那个新文件的起始权限。
mode: 填八进制权限  例如:0666
O_TRUNC    --> 如果文件存在,则清空该文件。


    例子:验证O_TRUNC参数是可以清空文件的。    

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close

int main(int argc,char *argv[])
{
    int fd;
    fd = open("ggg.txt",O_WRONLY|O_TRUNC);
    
    close(fd);
    
    return 0;
}


   练习3: 尝试在家目录下创建一个新的文件叫ggy.txt,并给0666的权限。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close

int main(int argc,char *argv[])
{
    int fd = open("/home/gec/ggy.txt",O_RDWR|O_CREAT,0666);
    if(fd < 0)
    {
        printf("open ggy.txt error!\n");
    }
    else{
        printf("open ggy.txt success!\n");
    }
    
    close(fd);
    
    return 0;
}

结论:一般设计文件权限的问题时候,都会先将umask设置为0,那么这样做好处是:你设置多少,结果就是多少。


六、文件数据输出/输入。
1、如何读取文件的数据?   -->  read()   --> man 2 read
功能: read from a file descriptor
    //从文件描述符中读取数据出来

    //头文件
    #include <unistd.h>

    //函数原型
    ssize_t read(int fd, void *buf, size_t count);

参数:
    fd:文件描述符
    buf:数据缓冲区
    count:尝试读取的字节数  (愿望值)

返回值:
    成功:已经读取到的字节数
    失败:-1

   例子: 尝试从文本文件中读取数据出来。
   注意: 从文本文件中读取出来的内容都是以字符串的形式存在。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close

int main(int argc,char *argv[])
{
    //1. 打开ggg.txt
    int fd = open("ggg.txt",O_RDWR);
    if(fd < 0)
    {
        printf("open ggg.txt error!\n");
    }
    
    //2. 尝试读取文件的内容出来
    char buf[100] = {0};
    int n;
    n = read(fd,buf,sizeof(buf));
    printf("n = %d\n",n);
    printf("buf = %s\n",buf);
    
    //3. 关闭文件
    close(fd);
    
    return 0;
}

-------------------------------------
运行结果:

文件内容: yueqian
gec@ubuntu:/mnt/hgfs/GZ2180/07 linux IO编程/01/code$ ./demo3
n = 7
buf = yueqian


    练习4: 重复读取一个文件,那么第二次读取的时候是在第一次基础上继续读,还是从文件开头读取?

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close
#include <strings.h>

int main(int argc,char *argv[])
{
    //1. 打开ggg.txt
    int fd = open("ggg.txt",O_RDWR);
    if(fd < 0)
    {
        printf("open ggg.txt error!\n");
    }
    
    //2. 尝试读取文件的内容出来
    //yueqianappletreehelloworld
    char buf[100] = {0};
    int n;
    n = read(fd,buf,10);
    printf("n = %d\n",n);  //10
    printf("buf = %s\n",buf); //yueqianapp
    
    bzero(buf,sizeof(buf));
    n = read(fd,buf,10);
    printf("n = %d\n",n);  //10
    printf("buf = %s\n",buf); //letreehell   --> 说明是继续读取
                  //yueqianapp   --> 说明是重新读取
    //3. 关闭文件
    close(fd);
    
    return 0;
}

答案: 继续读取。

2、写入数据到文件中。  -->  write()  --> man 2 write
功能: write to a file descriptor


   #include <unistd.h>

  ssize_t write(int fd, const void *buf, size_t count);
  
参数:
    fd:    文件描述符
    buf:   数据缓冲区
    count: 写入的字节数

返回值:
    成功:    写入的字节数
    失败:  -1


    例子: 尝试写入一些数据到一个空白的文件中。

------------------------------------------------------------

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close
#include <strings.h>

int main(int argc,char *argv[])
{
    int fd = open("kkk.txt",O_RDWR);    //文件定位在开头
    if(fd < 0)
    {
        printf("open kkk.txt error!\n");
    }
    
    char buf[10] = "hello";
    int n = write(fd,buf,5);
    printf("n = %d\n",n);
    
    close(fd);
    
    return 0;
}

运行之前:
文件: 空白


运行之后:
n = 5
文件: hello

----------------------------------------------------------

如果不是空白文件,则会重头覆盖文件的内容。

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close
#include <strings.h>

int main(int argc,char *argv[])
{
    int fd = open("kkk.txt",O_RDWR);    //文件定位在开头
    if(fd < 0)
    {
        printf("open kkk.txt error!\n");
    }
    
    char buf[10] = "apple";
    int n = write(fd,buf,5);
    printf("n = %d\n",n);
    
    close(fd);
    
    return 0;
}


运行之前:
文件: hello


运行之后:
n = 5
文件: apple


-------------------------------------------------------

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close
#include <strings.h>

int main(int argc,char *argv[])
{
    int fd = open("kkk.txt",O_RDWR|O_APPEND);    //文件定位在末尾
    if(fd < 0)
    {
        printf("open kkk.txt error!\n");
    }
    
    char buf[10] = "apple";
    int n = write(fd,buf,5);
    printf("n = %d\n",n);
    
    close(fd);
    
    return 0;
}


运行之前:
文件: hello

运行之后:
n = 5
文件: helloapple

------------------------------------------------------


七、研究read/write函数的返回值。


--------------------------------------read函数返回值-----------------------------

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close
#include <strings.h>

int main(int argc,char *argv[])
{
    //文件内容:helloworld
    
    int fd = open("qqq.txt",O_RDWR);
    
    char buf[100] = {0};
    /*
    int n = read(fd,buf,100);
    printf("n = %d\n",n); //10
    printf("buf = %s\n",buf); //helloworld
    */
    
    /*
    int n = read(fd,buf,20);
    printf("n = %d\n",n); //10
    printf("buf = %s\n",buf); //helloworld
    */
    
    /*
    int n = read(fd,buf,10);
    printf("n = %d\n",n); //10
    printf("buf = %s\n",buf); //helloworld
    */
    
    /*
    int n = read(fd,buf,5);
    printf("n = %d\n",n); //5
    printf("buf = %s\n",buf); //hello
    */
    
    close(fd);
    return 0;
}

-------------------------------------------write函数返回值----------------------------

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //for open
#include <unistd.h>     //for close
#include <strings.h>

int main(int argc,char *argv[])
{
    //文件: 空白
    int fd = open("xxx.txt",O_RDWR|O_TRUNC);
    
    char buf[11] = "helloworld";
    
    /*
    int n = write(fd,buf,5);
    printf("n = %d\n",n); //5   文件的内容:hello
    */
    
    /*
    int n = write(fd,buf,8);
    printf("n = %d\n",n);  //8   文件的内容: hellowor
    */
    
    /*
    int n = write(fd,buf,10);
    printf("n = %d\n",n);  //10   文件的内容: helloworld
    */
    
    /*
    int n = write(fd,buf,11);
    printf("n = %d\n",n);   //11     文件的内容:helloworld + \0
    */
    
    int n = write(fd,buf,100);
    printf("n = %d\n",n);   // 100      文件的内容:helloworld + \0 + 89个乱码
    
    close(fd);
    
    return 0;
}

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/182948
推荐阅读
相关标签
  

闽ICP备14008679号