IO-文件IO

文件IO

1.1文件描述符

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

2> 文件描述符是一个非负整数,一个进程能够使用的文件描述符默认为1024个。【0,1023】,可以通过指令 ulimit -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, const char *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;
}

 1.2 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并置位错误码

1.3 close关闭文件 

#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;
}

1.4 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<myhead.h>
//定义结构体类型
typedef struct 
{
    char name[20];
    int age; 
    double score;
}Stu;

/*********************主程序*************************/
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

    //定义三个学生
    Stu s[3] = {{"zhangsan", 10, 99.5},\
                {"lisi", 15, 88},\
                {"wangwu", 8, 60}};
    //将所有学生写入文件中
    write(fd, s, sizeof(s));

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

    //重新打开文件
    if((fd = open("./file.txt", O_RDONLY)) == -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);

    //关闭文件
    close(fd);

    return 0;
}

1.5 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, const char *argv[])
{
    //判断传入的文件个数
    if(argc != 3)
    {
        write(2, "input file error\n", sizeof("input file error\n"));
        return -1;
    }

    //以只读的形式打开源文件
    int srcfd = open(argv[1], O_RDONLY);
    if(srcfd == -1)
    {
        perror("open srcfile error");
        return -1;
    }

    //以只写的形式打开目标文件
    int destfd = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0664);
    if(destfd == -1)
    {
        perror("open destfile error");
        return -1;
    }

    //定义搬运工
    char buf[128] = "";

    while(1)
    {
        int src = read(srcfd, buf, sizeof(buf));
        if(src == 0)
        {
            break;
        }
        
        //将读取的数据全部写入到新文件中
        write(destfd, buf, src);
    }

    printf("拷贝成功\n");

    //关闭文件
    close(srcfd);
    close(destfd);


    return 0;
}

1.6 文件描述符拷贝问题

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

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

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件描述符变量
    int fd1 = -1;
    //打开文件
    if((fd1 = open("./file.txt", O_RDONLY)) == -1)
    {
        perror("open error");
        return -1;
    }
    
    //定义变量存储文件描述符
    int fd2 = fd1;

    //通过fd2完成文件光标的偏移
    lseek(fd2, 8, SEEK_SET);

    //从fd1中读取数据、
    char buf[128] = "";
    read(fd1, buf, 5);
    printf("buf = %s\n", buf);          //nihao:说明fd1和fd2共用同一个光标
                            //hello:说明不共用光标

    //关闭文件描述符
    close(fd1);

    return 0;
}

2> dup函数完成拷贝

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

#include <unistd.h>

int dup(int oldfd);

        功能:拷贝旧文件描述符,生成新的文件描述符

        参数:旧文件描述符

        返回值:新文件描述符

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件描述符变量
    int fd1 = -1;
    //打开文件
    if((fd1 = open("./file.txt", O_RDONLY)) == -1)
    {
        perror("open error");
        return -1;
    }
    
    //定义变量存储文件描述符
    int fd2 = dup(fd1);

    printf("fd1 = %d, fd2 = %d\n", fd1, fd2);  //3   4

    //通过fd2完成文件光标的偏移
    lseek(fd2, 8, SEEK_SET);

    //从fd1中读取数据、
    char buf[128] = "";
    read(fd1, buf, 5);
    printf("buf = %s\n", buf);          //nihao:说明fd1和fd2共用同一个光标
                            //hello:说明不共用光标

    //关闭文件描述符
    close(fd1);

    return 0;
}

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

int dup2(int oldfd, int newfd);

        功能:拷贝旧文件描述符,放入新文件描述符中,如果新文件描述符之前已经打开,则在拷贝时,默认将其关闭

        参数1:旧文件描述符

        参数2:新文件描述符

        返回值:成功返回新文件描述符,失败返回-1并置位错误码

 

#include<myhead.h>

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

    //打开一个文件
    int fd2 = -1;
    if((fd2 = open("./tt.c", O_RDONLY))==-1)
    {
        perror("open error");
        return -1;
    }
    printf("fd2 = %d\n", fd2);         //3


    //定义文件描述符变量
    int fd1 = -1;
    //打开文件
    if((fd1 = open("./file.txt", O_RDONLY)) == -1)
    {
        perror("open error");
        return -1;
    }
    
    //定义变量存储文件描述符
    dup2(fd1 ,fd2);

    printf("fd1 = %d, fd2 = %d\n", fd1, fd2);  //4    3

    //通过fd2完成文件光标的偏移
    lseek(fd2, 8, SEEK_SET);

    //从fd1中读取数据、
    char buf[128] = "";
    read(fd1, buf, 5);
    printf("buf = %s\n", buf);          //nihao:说明fd1和fd2共用同一个光标
                                    //hello:说明不共用光标

    //关闭文件描述符
    close(fd1);

    return 0;
}

4> dup2常用用途

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件描述符
    int fd = -1;
    if((fd = open("./aa.txt", O_WRONLY|O_CREAT|O_TRUNC, 0664)) == -1)
    {
        perror("open error");
        return -1;
    }

    //将标准输出拷贝到fd中
    dup2(fd, STDOUT_FILENO);       //将标准输出重定向到文件中
    dup2(fd, STDIN_FILENO);        //将标准输入重定向到文件中
    dup2(fd, STDERR_FILENO);       //将标准出差重定向到文件中

    printf("hello a\n");        //?
    printf("hello a\n");        //?
    printf("hello a\n");        //?
    printf("hello a\n");        //?
    printf("hello a\n");        //?



    //关闭文件
    close(fd);

    return 0;
}

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

#include<myhead.h>

int main(int argc, const char *argv[])
{
    //定义文件描述符
    int fd1 = -1;
    if((fd1 = open("./file.txt", O_RDONLY)) == -1)
    {
        perror("open error");
        return -1;
    }

    //定义文件描述符
    int fd2 = -1;
    if((fd2 = open("./file.txt", O_RDONLY)) == -1)
    {
        perror("open error");
        return -1;
    }

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

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

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


    //关闭文件
    close(fd1);
    close(fd2);

    return 0;
}

文件属性函数

1> 函数原型

     #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;
}

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

源文件内容不变

#include<myhead.h>

int main(int argc,const char *argv[])
{
    //判断是否为三个文件
    if(argc!=3)
    {
        perror("file error");
        return -1;
    }
    //读取源文件
    int fp=-1;
    if((fp=open(argv[1],O_RDONLY))==-1)
    {
        perror("open error");
        return -1;
    }

    //写入目标文件
    int fd=-1;
    if((fd=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0664))==-1)
    {
        perror("open error");
        return -1;
    }

    //定义一个字符转换大小写
    char buf;

    while(1)
    {
        //读取每一位到buf中
        int src=read(fp,&buf,1);
        if(src==0)
        {
            return 0;
        }
        //大小写转化
        if(buf>='a'&& buf<='z')
        {
            buf-=32;
        }else if(buf>='A'&& buf<='Z')
        {
            buf+=32;
        }
        write(fd,&buf,1);
    }
    close(fp);
    close(fd);
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值