学习记录——day22 文件IO

          文件IO是使用系统调用(内核提供的函数)来完成数据的读写操作,不提供缓冲区,基于文件描述符操作文件,每进行一次文件io操作,进程就会从用户空间向内核空间进行一次切换,效率没有标准io高。

文件描述符

        1、文件描述符本质上是一个整数,当文件存储在本地磁盘时,属于静态文件,当使用open函数打开文件时,该文件就属于动态文件,后期需要对文件进行操作的化就需要基于一个句柄来完成,这个句柄就是文件描述符,用一个整数表示。

        2、文件描述符是一个非负整数,一个进程能够使用的文件描述符默认为1024个【0,1023】,可以通过指令uimit -a查看,并且可以通过ulimit -n进行修改进程能够打开的文件描述符个数

         3、使用原则: 最小未分配原则

        4、特殊的文件描述符:0、1、2,分别对应了标准输入、标准输出、标准出错

stdin->_fileno     ===>   0    ===> STDIN_FILENO
stdout->_fileno    ===>  1    ===> STDOUT_FILENO
stderr->_fileno    ===>  2    ===> STDERR_FILENO

#include <myhead.h>

int main(int argc, char const *argv[])
{
    printf("stdin -> _fileno = %d\n",stdin ->_fileno);  //0
    printf("stdout -> _fileno = %d\n",stdout ->_fileno); //1
    printf("stderr -> _fileno = %d\n",stderr ->_fileno); //2

    FILE *fp = NULL;
    if ((fp = fopen("./file.txt","w")) == NULL)
    {
        perror("fopen error");
        return -1;
    }

    printf("fp -> _fileno = %d\n", fp -> _fileno);//3 最小未分配
    
    return 0;
}

 

myhead.h 包含头文件如下

open打开文件

       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

       int open(const char *pathname, int flags);
       int open(const char *pathname, int flags, mode_t mode);
        功能:以指定的方式打开指定的文件
        参数1:要打开的文件路径
        参数2:文件打开标识
            必须包含以下三者之一:
                O_RDONLY:以只读的形式打开文件
                O_WRONLY:以只写的形式打开文件
                O_RDWR:以读写的形式打开文件
            以下的可以选择性加入,使用位或完成:
                O_CREAT:如果文件存在,就直接打开,如果文件不存在,则创建文件,此时,参数3必须要加,表示创建的文件权限
                O_TRUNC:表示清空文件内容
                O_APPEND:以追加的形式打开文件
                O_EXCL:确保打开的是不存在的文件,通常跟O_CREAT一起使用,

                                表示本次操作必须创建新文件,如果文件存在,则open函数报错,错误                                   码为:EEXIST
——————————————————————————————————————
                "r":O_RDONLY
                "r+":O_RDWR
                "w":O_WRONLY|O_CREAT|O_TRUNC
                "w+":O_RDWR|O_CREAT|O_TRUNC
                "a":O_WRONLY|O_APPEND|O_CREAT
                "a+":O_RDWR|O_APPEND|O_CREAT

——————————————————————————————————————
            参数3:

当参数2中有O_CREAT属性时,参数3必须加上,表示本次创建文件的文件权 限,

                但是,最终的权限不是用户给定的权限,

                  文件最终的权限是 给定的权限                          mode&~umask的值,umask可以通过指令umask进行查看当前终端的默认掩码,
           可以通过 umask -n   来将掩码更改成 n,但是,这种方式更改的umask只在当前终端              有效,其他终端无效
           也可以通过umask(n)来更改umask的值
                  #include <sys/stat.h>

                 mode_t umask(mode_t cmask);
                 功能:更改当前的umask的值
                 参数:要更改的umask的值
            普通文件权限一般为:0664
            目录文件权限一般为:0775
         ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
         返回值:成功返回一个新的文件描述符,失败返回-1并置位错误码                      
        

man  fopen

close 关闭文件

       #include <unistd.h>

       int close(int fd);
    功能:关闭文件描述符
    参数:要关闭的文件描述符
    返回值:成功返回0,失败返回-1并置位错误码
    

       #include <unistd.h>

       int close(int fd);
    功能:关闭文件描述符
    参数:要关闭的文件描述符
    返回值:成功返回0,失败返回-1并置位错误码
    
#include<myhead.h>

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

    //创建一个文件描述符变量,用于存储文件描述符
    int fd = -1;
    if((fd = open("./file.txt", O_WRONLY|O_CREAT|O_TRUNC, 0664)) == -1)
    {
        perror("open error");
        return -1;
    }

    printf("open success fd = %d\n", fd);     //3


    //关闭文件
    if(close(fd) == -1)
    {
        perror("close fd 1");
        return -1;
    }


    return 0;
}

read/write 数据读写

       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);
    功能:向指定的文件中,写入指定的信息
    参数1:已打开的文件描述符
    参数2:要写入的数据的起始地址
    参数3:要写入的数据大小
    返回值:成功返回写入字符的个数,失败返回-1并置位错误码
    
           #include <unistd.h>

       ssize_t read(int fd, void *buf, size_t count);
    功能:从指定的文件中读取count个字节的数据到buf中
    参数1:打开的文件描述符
    参数2:要存放数据的容器起始地址
    参数3:要读取的字节个数
    返回值:成功返回读取字节的个数,失败返回-1并置位错误码

       #include <unistd.h>

       ssize_t write(int fd, const void *buf, size_t count);
    功能:向指定的文件中,写入指定的信息
    参数1:已打开的文件描述符
    参数2:要写入的数据的起始地址
    参数3:要写入的数据大小
    返回值:成功返回写入字符的个数,失败返回-1并置位错误码
    
           #include <unistd.h>

       ssize_t read(int fd, void *buf, size_t count);
    功能:从指定的文件中读取count个字节的数据到buf中
    参数1:打开的文件描述符
    参数2:要存放数据的容器起始地址
    参数3:要读取的字节个数
    返回值:成功返回读取字节的个数,失败返回-1并置位错误码
#include <myhead.h>

typedef struct
{
    char name[20];
    int age;
    double score;
} Stu;

int main(int argc, char const *argv[])
{
    int fd = -1; // 创建一个文件描述符变量,用于存储文件描述符
    if ((fd = open("./file.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664)) == -1)
    {
        perror("open error");
        return -1;
    }

    printf("open success fd = %d\n", fd); // 3

    Stu s[3] = {{"张三", 10, 99.5}, {"李四", 18, 90.5}, {"王五", 16, 89.5}};

    write(fd, s, sizeof(s));

    // 关闭文件
    if (close(fd) == -1)
    {
        perror("close fd 1");
        return -1;
    }

    if ((fd = open("./file.txt", O_RDONLY, 0664)) == -1)
    {
        perror("open error");
        return -1;
    }

    printf("open success fd = %d\n", fd); // 3

    Stu temp;
    read(fd,&temp,sizeof(temp));

    printf("%s %d %.2lf\n",temp.name,temp.age,temp.score);

    return 0;
}

lseek

       #include <sys/types.h>
       #include <unistd.h>

       off_t lseek(int fd, off_t offset, int whence);
    功能:移动文件的光标
    参数1:文件描述符
    参数2:偏移量
            >0:向后偏移
            =0:不偏移
            <0:向前偏移
    参数3:偏移起始位置
            SEEK_SET:文件开头
            SEEK_END:文件结尾
            SEEK_CUR:文件当前位置
    返回值:成功返回文件光标当前的位置,失败返回-1并置位错误码

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //打开文件,以读写的形式
    int fd = -1;
    if((fd = open("./gg.bmp", O_RDWR)) == -1)
    {
        perror("open error");
        return -1;
    }

    //偏移光标
    lseek(fd, 2, SEEK_SET);

    //定义变量接受大小
    int img_size = 0;
    read(fd, &img_size, sizeof(img_size));
    printf("img_size = %d\n", img_size);

    //将光标直接偏移到文件末尾,返回值就是文件大小
    printf("size = %ld\n", lseek(fd, 0, SEEK_END));

    //关闭文件
    close(fd);

    return 0;
}

使用文件IO完成两个文件的拷贝

#include <myhead.h>
int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        // printf("终端输入有误\n");
        write(2,"input file error\n",sizeof("input file error\n"));
        return -1;
    }
    
    umask(0000);
    
    int fd = open(argv[1],O_RDONLY);
    int fb = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0664);

    char temp[128] = "";

    while (1)
    {
        int flag = read(fd,temp,sizeof(temp));
        if (flag == 0)
        {
            break;
        }
        
        write(fb,temp,flag);
    }
    
    //下面子规方法会拷贝多余的信息
    // while (read(fd,temp,sizeof(temp)))
    // {
    //     write(fb,temp,sizeof(temp));
    // }
    
    close(fd);
    close(fb);
    
    return 0;
}

文件描述符拷贝问题

拷贝用文件:

1、简单完成两个文件描述符变量的拷贝

        这种情况下,没有新文件描述符产生,使用的是同一个文件描述符,共享同一个文件光标

#include <myhead.h>
int main(int argc, char const *argv[])
{
    int fd1 = -1;
    if ((fd1 = open("./file2.txt",O_RDONLY)) == -1)
    {
        perror("open eroor");
        return -1;
    }

    //1、
    // int fd2 = fd1;  //定义变量存储文件描述符     
    //                 //未出现新的文件描述符fd1 fd2 公用光标

    //2、   
    // int fd2 = dup(fd1); //出现新的文件描述符  
    //                     //但仍然公用光标
    
    lseek(fd2,8,SEEK_SET);  //光标偏移

    char buf[128] = "";
    read(fd1,buf,5);
    printf("buf = %s\n",buf);  //1、buf = 55555   
                               //2、buf = 55555  

    close(fd1);
    return 0;
}

2、dup函数完成拷贝

        该操作,会生成新的文件描述符,但是与旧的文件描述符共享同一个光标

#include <myhead.h>
int main(int argc, char const *argv[])
{
    int fd1 = -1;
    if ((fd1 = open("./file2.txt",O_RDONLY)) == -1)
    {
        perror("open eroor");
        return -1;
    }

    //1、
    // int fd2 = fd1;  //定义变量存储文件描述符     
    //                 //未出现新的文件描述符fd1 fd2 公用光标

    //2、   
    // int fd2 = dup(fd1); //出现新的文件描述符  
    //                     //但仍然公用光标
    
    lseek(fd2,8,SEEK_SET);  //光标偏移

    char buf[128] = "";
    read(fd1,buf,5);
    printf("buf = %s\n",buf);  //1、buf = 55555   
                               //2、buf = 55555  

    close(fd1);
    return 0;
}

3、使用dup2拷贝文件描述符

       int dup2(int oldfd, int newfd);
    功能:拷贝旧文件描述符,放入新文件描述符中,如果新文件描述符之前已经打开,则在拷贝时,默认将其关闭
    参数1:旧文件描述符
    参数2:新文件描述符
    返回值:成功返回新文件描述符,失败返回-1并置位错误码

#include <myhead.h>
int main(int argc, char const *argv[])
{
    int fd2 = open("./04.c",O_RDONLY);

    int fd1 = open("./file2.txt",O_RDONLY);

    //使fd2指向fd1指向的文件,fd2原本指向的文件静默关闭
    dup2(fd1,fd2);  //不产生新的文件描述符
                    //共用光标
    printf("fd1= %d,fd2 = %d\n",fd1,fd2); //4  3
    
    lseek(fd2,8,SEEK_SET);

    char buf[128] = "";
    read(fd1,buf,5);
    printf("buf = %s\n",buf);  //buf = 55555
    
    close(fd1);
    close(fd2);
    return 0;
}

4、dup2的常规用法

#include <myhead.h>
int main(int argc, char const *argv[])
{
    int fd = -1;
    if ((fd = open("./aa.txt",O_WRONLY|O_CREAT|O_TRUNC)))
    {
        perror("open error");
        return -1;
    }
    
    //将标准输出重定向到fd中,标准输出静默关闭
    dup2(fd,STDOUT_FILENO);
    dup2(fd,STDIN_FILENO);//将标准输入重定向到fd中
    dup2(fd,STDERR_FILENO);//将标准出错重定向到fd中

    printf("hello\n");

    return 0;
}

5、多次使用open函数打开同一个文件时,这多个文件描述符是不共享文件光标的

#include <myhead.h>
int main(int argc, char const *argv[])
{
    int fd1 = -1;
    if ((fd1 = open("./file2.txt",O_RDONLY)))
    {
        perror("open error");
        return -1;
    }
    
    int fd2 = -1;
    if ((fd2 = open("./file2.txt",O_RDONLY)))
    {
        perror("open error");
        return -1;
    }

    lseek(fd1,8,SEEK_SET);  //移动fd1的光标

    char buf[10] = ""; //从读取fd2的指向读取数据
    read(fd2,,buf,5);

    printf("%s\n",buf); //  55555

    return 0;
}

文件属性函数

stat 函数原型 

       #include <sys/types.h>
       #include <sys/stat.h>
       #include <unistd.h>

       int stat(const char *pathname, struct stat *statbuf);
       功能:获取给定的文件状态属性
       参数1:要获取的文件名
       参数2:文件属性结构体指针,是一个地址传递,需要传递一个结构体变量的地址,通过函数调用结束后,该变量中就有内容了
       struct stat {
               dev_t     st_dev;         /* 设备号 */
               ino_t     st_ino;         /* Inode 号 */
               mode_t    st_mode;        /* 文件类型和文件权限 */
               nlink_t   st_nlink;       /* 硬链接数 */
               uid_t     st_uid;         /* 拥有者的用户id号 */
               gid_t     st_gid;         /* 拥有者的组id号 */
               dev_t     st_rdev;        /* 特殊文件的设备号 */
               off_t     st_size;        /* 总大小,以字节为单位 */
               blksize_t st_blksize;     /* 块的大小 */
               blkcnt_t  st_blocks;      /* 总块数 */

             
           };
        如何获取文件的类型和文件权限
        1>    使用st_mode & S_IFMT可以得到文件的类型
           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
        2>    使用st_mode & 0777可以得到文件的权限
           S_ISUID     04000   set-user-ID bit
           S_ISGID     02000   set-group-ID bit (see below)
           S_ISVTX     01000   sticky bit (see below)

           S_IRWXU     00700   owner has read, write, and execute permission
           S_IRUSR     00400   owner has read permission
           S_IWUSR     00200   owner has write permission
           S_IXUSR     00100   owner has execute permission

           S_IRWXG     00070   group has read, write, and execute permission
           S_IRGRP     00040   group has read permission
           S_IWGRP     00020   group has write permission
           S_IXGRP     00010   group has execute permission

           S_IRWXO     00007   others (not in group) have read,  write,  and
                               execute permission
           S_IROTH     00004   others have read permission
           S_IWOTH     00002   others have write permission
           S_IXOTH     00001   others have execute permission
       
       返回值:成功返回0,失败返回-1并置位错误码
#include<myhead.h>

int main(int argc, const char *argv[])
{
    //判断外部传来的文件
    if(argc != 2)
    {
        printf("input file error\n");
        return -1;
    }

    //定义接受文件的属性结构体变量
    struct stat sb;                 //stat buf
    //调用函数,获取文件状态
    if(stat(argv[1], &sb) == -1)
    {
        perror("stat error");
        return -1;
    }

    //程序执行至此,sb中就记录了传入的文件的信息
    //输出文件类型
    switch(sb.st_mode & S_IFMT)
    {
    case S_IFSOCK:
        {
            printf("套接字文件\t");
        }
        break;
    case S_IFLNK:
        {
            printf("链接文件\t");
        }
        break;
    case S_IFREG:
        {
            printf("普通文件\t");
        }
        break;
    case S_IFBLK:
        {
            printf("块设备文件\t");
        }
        break;
    case S_IFDIR:
        {
            printf("目录文件\t");
        }
        break;
    case S_IFCHR:
        {
            printf("字符设备文件\t");
        }
        break;
    case S_IFIFO:
        {
            printf("管道文件\t");
        }
        break;
    }

    //输出文件权限
    printf("%#o\t", sb.st_mode&0777);
    //输出文件大小
    printf("%ld\t", sb.st_size);
    //输出文件inode号
    printf("%ld\n", sb.st_ino);

    return 0;
}

使用文件I0完成,将源文件中的所有内容进行加密(大写转小写、小写转大写)后写入目标文件中源文件内容不变

#include <myhead.h>
int main(int argc, char const *argv[])
{
    if (argc != 3)
    {
        // printf("终端输入有误\n");
        write(2, "input file error\n", sizeof("input file error\n"));
        return -1;
    }

    umask(0000);

    int fd = open(argv[1], O_RDONLY);
    int fb = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0664);

    char temp = {0}; // 定义中转变量

    while (1)
    {
        int flag = read(fd, &temp, 1);
        if (flag == 0)
        {
            break;
        }
        if (temp >= 'A' && temp <= 'Z') // 大小转小写
        {
            temp = temp + 32;
        }
        else if (temp >= 'a' && temp <= 'z') // 小写转大写
        {
            temp = temp - 32;
        }

        write(fb, &temp, flag);
    }

    close(fd);
    close(fb);

    return 0;
}

结果

#INCLUDE <MYHEAD.H>
INT MAIN(INT ARGC, CHAR CONST *ARGV[])
{
    IF (ARGC != 3)
    {
        // PRINTF("终端输入有误\N");
        WRITE(2, "INPUT FILE ERROR\N", SIZEOF("INPUT FILE ERROR\N"));
        RETURN -1;
    }

    UMASK(0000);

    INT FD = OPEN(ARGV[1], o_rdonly);
    INT FB = OPEN(ARGV[2], o_rdwr | o_creat | o_trunc, 0664);

    CHAR TEMP = {0}; // 定义中转变量

    WHILE (1)
    {
        INT FLAG = READ(FD, &TEMP, 1);
        IF (FLAG == 0)
        {
            BREAK;
        }
        IF (TEMP >= 'a' && TEMP <= 'z') // 大小转小写
        {
            TEMP = TEMP + 32;
        }
        ELSE IF (TEMP >= 'A' && TEMP <= 'Z') // 小写转大写
        {
            TEMP = TEMP - 32;
        }

        WRITE(FB, &TEMP, FLAG);
    }

    CLOSE(FD);
    CLOSE(FB);

    RETURN 0;
}

 对文件夹的操作

opendir  打开文件夹

       #include <sys/types.h>
       #include <dirent.h>

       DIR *opendir(const char *name);
    功能:打开指定的文件夹
    参数:要打开的文件夹路径
    返回值:成功返回文件夹指针,失败返回NULL

#include <myhead.h>

int main(int argc, char const *argv[])
{
    //定义文件夹指针
    DIR * pd = opendir("./");
    if (NULL == pd)
    {
        perror("opendir");
        return -1;
    }

    //读取文件夹
    struct dirent* rp = NULL;  //读取文件目录信息

    while ((rp = readdir(pd)) != NULL)
    {
        printf("%-20s\t%#o\t%d\t%ld\n",rp->d_name,rp->d_type,rp->d_reclen,rp->d_ino);
    }//文件名  文件类型  结构体大小   inode号
    
    //关闭文件夹
    closedir(pd);
    
    return 0;
}

closedir   关闭文件夹

       #include <sys/types.h>

       #include <dirent.h>

       int closedir(DIR *dirp);
    功能:关闭指定的文件夹
    参数:之前打开的文件夹指针
    返回值:成功返回0,失败返回-1并置位错误码

readdir  读取文件夹信息

       #include <dirent.h>

       struct dirent *readdir(DIR *dirp);
    功能:读取打开的文件夹中的记录信息
    参数:文件夹指针
    返回值:成功返回当前文件夹指针对应的目录信息,失败返回NULL并置位错误码
     struct dirent {
               ino_t          d_ino;       /* Inode 号 */
               off_t          d_off;       /* 文件夹指针的偏移量*/
               unsigned short d_reclen;    /* 当前结构体的长度 */
               unsigned char  d_type;      /* 文件的类型,不支持所有的文件类型 */
               char           d_name[256]; /* 文件名称 */
           };
           关于文件类型:
             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     This is a UNIX domain socket.

              DT_UNKNOWN  The file type could not be determined.

  • 28
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值