Linux系统编程三——文件操作


一、标准C库与Linux系统库函数区别

1.库示意图和内存示意图

2.文件描述符

  文件描述符是内核为了高效管理已被打开的文件所创建的索引,用于指向被打开的文件,所有执行I/O操作的系统调用都通过文件描述符;文件描述符是一个简单的非负整数,用以表明每个被进程打开的文件。程序刚刚启动时,第一个打开的文件是0,第二个是1,以此类推。也可以理解为文件的身份ID。

 3.系统I/O函数

 3.1 int open(const char *pathname, int flags);

/*
    #include #include #include //打开一个已经存在的文件 int open(const char *pathname, int flags); 参数: -pathname:文件路径 -flags :文件权限设置(O_RDONLY, O_WRONLY, or O_RDWR)只读只写和读写 返回值: -文件描述符 errono: -属于Linux系统函数库,属于全局变量,记录的是最近的错误号 #include void perror (const char * s);作用:打印error对应的错误描述 //创建一个新的文件 int open(const char *pathname, int flags, mode_t mode);
*/ #include #include #include #include #include int main(){ int fd = open("a.txt", O_RDONLY); if(fd == -1){ perror("open"); } //读写操作 //关闭文件描述符 close(fd); return 0; }

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

#include 
#include 
#include 
#include
#include
/*
      //创建一个新的文件
       int open(const char *pathname, int flags, mode_t mode);
        参数:
            -pathname:文件路径
            -flags :文件权限设置和其他参数
                -必选项(O_RDONLY, O_WRONLY, or O_RDWR)
                -可选项(O_APPEND追加,O_CREATE文件不存在则进行创建等)
            -mode:八进制的数,表示创建出的新的文件的操作权限,比如0775
            r=4,w=2,x=1
            最终权限是(mode & ~umask)
            umask作用是抹去某些权限
        返回值:
            -文件描述符
        errono:
            -属于Linux系统函数库,属于全局变量,记录的是最近的错误号

*/

int main(){

    int fd = open("create.txt",O_RDWR | O_CREAT, 0777);
    if(fd==-1){
        perror("open");
    }

    //关闭
    close(fd);

    return 0;
}

3.3 文件读写命令ssize_t read(int fd, void *buf, size_t count); &&  ssize_t write(int fd, const void *buf, size_t count);

/*
    #include 

    ssize_t read(int fd, void *buf, size_t count);
        参数:  
            -fd:文件描述符
            -buf:读取数据存放的地方,(数组地址)
            -count:指定的数组的大小
        返回值:
            -成功:
                >0:返回实际的读取到的字节数
                ==0: 文件已经读取完毕
            -失败:
                -1: errno
        #include 

        ssize_t write(int fd, const void *buf, size_t count);
        参数:  
            -fd:文件描述符
            -buf:写入数据存放的地方,(数组地址)
            -count:需要写的数据的大小
        返回值:
            -成功:实际写入的字节数
            -失败:返回-1,设置errno
*/
#include 
#include 
#include
#include 
#include


int main(){
    //通过open打开english.txt
    int srcfd = open("english.txt",O_RDONLY);
    if(srcfd==-1){
        perror("open");
        return -1;
    }
    //创建一个新的文件
    int desfd = open("cpy.txt",O_WRONLY|O_CREAT,0664);
    if(desfd==-1){
        perror("open");
        return -1;
    }
    //频繁的读写操作
    char buf[1024] = {0};
    int len = 0;
    do {
        len = read(srcfd, buf, sizeof(buf));
        write(desfd, buf, len);
    }while(len>0);
    //关闭文件
    close(desfd);
    close(srcfd);
    return 0;
}

3.4 off_t lseek(int fd, off_t offset, int whence);

/*
        标准C库
       #include 
       int fseek(FILE *stream, long offset, int whence);

        Linux系统函数
       #include 
       #include 

       off_t lseek(int fd, off_t offset, int whence);
       参数
            -fd: 文件描述符,通过open得到,可以用来操作某个文件
            -offset:偏移量
            -whence:
                SEEK_SET:设置文件指针偏移量
                SEEK_CUR:设置偏移量:当前位置+第二个参数offset值
                SEEK_END:设置偏移量:文件大小+第二个参数offset值
        lseek作用
            1.移动文件指针到头文件
                lseek(fd, 0, SEEK_SET)
            2.获取当前文件指针位置
                lseek(fd, 0, SEEK_CUR)
            3.获取文件长度
                lseek(fd, 0, SEEK_END)
            4.拓展文件长度
                lseek(fd, 100, SEEK_END )
*/

#include 
#include 
#include
#include 
#include

int main(){
    //获取文件描述符
    int srcfd = open("hello.txt",O_RDWR);
    if(srcfd==-1){
        perror("open");
        return -1;
    }
    //扩展文件长度
    int ret = lseek(srcfd,100,SEEK_END);
    if(ret==-1){
        perror("lseek");
        return -1;
    }
    //写入空数据

    write(srcfd," ",1);
    //关闭文件
    close(srcfd);
    return 0;
}

3.5  文件信息命令int stat(const char *pathname, struct stat *statbuf);

/*

        #include 
        #include 
        #include 

        int stat(const char *pathname, struct stat *statbuf);
        作用:
            获取文件相关的信息
        参数:
            -pathname:要操作的文件路径
            -statbuf:结构体变量,传出参数,用于保存获取到的文件信息
        返回值:
            -成功:返回0
            -失败:返回-1,设置errno

        int lstat(const char *pathname, struct stat *statbuf);
        作用:获取软连接文件的信息,而不是软连接指向的文件的信息


*/
#include 
#include 
#include 
#include

int main(){

    struct stat statbuf;

    int ret = stat("a.txt", &statbuf);
    if(ret == -1){
        perror("stat");
        return -1;
    }
    printf("size:%ld\n", statbuf.st_size);
    return 0;
}

3.6 通过stat函数返回struct stat.st_mode成员模拟实现Linux ls -l 指令

  首先是stat结构体成员

struct stat {
  dev_t st_dev ;//文件的设备编号
  ino_t st_ino; //节点
  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 ; //块数
  time_t st_atime; //最后—次访问时间
  time_t st_mtime; //最后一次修改时间
  time_t st_ctime; //最后一次改变时间(指属性)
} ;

  st_mode变量组成(权限)

  模拟实现ls -l

//模拟实现ls -l 
//-rw-rw-r-- 1 coco coco    12 12月  2 14:12 a.txt
#include 
#include 
#include 
#include
#include
#include 
#include
#include<string.h>

int main(int argc, char * argv[]){
    //判断输入的参数是否正确
    if(argc < 2){
        printf("%s filename\n", argv[0]);
        return -1;
    }
    //通过stat函数获取文件信息
    struct stat st;
    int ret = stat(argv[1], &st);
    if(ret==-1){
        perror("stat");
        return -1;
    }
    //获取文件权限
    char perms[11] = {0};//用于保存文件类型和权限的字符串
    switch (st.st_mode& S_IFMT)
    {
        case S_IFLNK:
            perms[0]='l';
        case S_IFDIR:
            perms[0]='d';
        case S_IFREG:
            perms[0]='-';
        case S_IFCHR:
            perms[0]='c';
        case S_IFBLK:
            perms[0]='b';
        case S_IFSOCK:
            perms[0]='s';
        case S_IFIFO:
            perms[0]='p';
        break;
        default:
            perms[0]='?';
    }
    //判断文件的访问权限
    //对用户自身
    perms[1]=st.st_mode & S_IRUSR ? 'r' : '-';
    perms[2]=st.st_mode & S_IWUSR ? 'w' : '-';
    perms[3]=st.st_mode & S_IXUSR ? 'x' : '-';
    //对同组
    perms[4]=st.st_mode & S_IRGRP ? 'r' : '-';
    perms[5]=st.st_mode & S_IWGRP ? 'w' : '-';
    perms[6]=st.st_mode & S_IXGRP ? 'x' : '-';
    //对其他组
    perms[7]=st.st_mode & S_IROTH ? 'r' : '-';
    perms[8]=st.st_mode & S_IWOTH ? 'w' : '-';
    perms[9]=st.st_mode & S_IXOTH ? 'x' : '-';
    //获取硬链接数
    int linknum = st.st_nlink;
    //获取文件所有者
    char * fileuser = getpwuid(st.st_uid)->pw_name;
    //文件所在组
    char * filegroup = getgrgid(st.st_gid)->gr_name;

    //获取文件大小
    long int filesize = st.st_size;

    //获取修改时间
    char * time = ctime(&st.st_mtime);
    char mtime[512] = {0};
    strncpy(mtime ,time, strlen(time)-1);

    //输出所有信息
    char buf[1024];
    sprintf(buf, "%s %d %s %s %ld %s %s",perms, linknum, fileuser, filegroup, filesize, mtime, argv[1]);
    printf("%s\n", buf);
    return 0;
}

3.7 判断权限命令 int access(const char *pathname, int mode);

/*  
        #include 
        int access(const char *pathname, int mode);
        作用:判断某个文件是否有某个权限,或者判断文件是否存在
        参数:
            -pathname:文件路径
            -mode:
                R_OK:读权限
                W_OK:写权限
                X_OK: 执行权限
                F_OK:文件是否存在
        返回值:“
            -成功:0
            -失败:-1
        

*/

#include 
#include
int main(){
    int ret = access("a.txt",F_OK);
    if(ret == -1){
        perror("access");
    }
    else{
        printf("文件存在\n");
    }
    
    return 0;
}

3.8 文件权限设置命令 int chmod(const char *pathname, mode_t mode);

/*
        #include 

        int chmod(const char *pathname, mode_t mode);
        作用:修改权限
        参数:
            -pathname:文件路径
            -mode:需要修改的权限,八进制数
        返回:
            -成功:0
            -失败:-1


*/

#include 
#include 
#include
int main(){

    int ret = chmod("a.txt",0775);
    if(ret == -1){
        perror("chmod");
        return -1;
    }
    else{
        printf("修改成功");
    }
    return 0;
}

3.9文件大小更改命令int truncate(const char *path, off_t length);

/*
        #include 
        #include 

        int truncate(const char *path, off_t length);
        作用:缩减或者扩展文件的尺寸至指定的大小
        参数:
            -path:文件路径
            -length:需要最终文件变成的大小,扩展会使用空格填充

*/
#include 
#include 
#include

int main(){

    int ret = truncate("a.txt",20);
    if(ret == -1){
        perror("truncate");
    }
    else{
        printf("成功\n");
    }

    return 0;
}

3.10 更改进程的工作目录命令int chdir(const char *path);

/*  
       #include 

       int chdir(const char *path);
        作用:修改进程的工作目录
            比如在/home/coco目录启动了可执行程序a.out,进程的工作目录为/home/coco
        参数:
            -path:需要修改的目录路径
        返回值:
            -成功:0
            -失败:-1
       #include 
       char *getcwd(char *buf, size_t size);
        作用:获取当前的工作目录
            比如在/home/coco目录启动了可执行程序a.out,进程的工作目录为/home/coco
        参数:
            -buf:存储的路径,指向的是一个数组(传出参数)
            -size:数组的大小
        返回值:
            返回的是指向的一块内存,为第一个参数(buf)的地址
       char *getwd(char *buf);
    
*/
#include 
#include 
#include 
#include
#include
int main(){
    //获取当前的工作目录
    char buf[128];
    getcwd(buf, sizeof(buf));
    printf("当前的工作目录是:%s", buf);

    //修改工作目录
    int ret = chdir("/home/coco/LinuxTest/lession13");
    if(ret == -1){
        perror("chdir");
    }
    else{
        printf("目录更改成功\n");
        int fd = open("chdir.txt", O_CREAT|O_RDWR,0664);
        if(fd == -1){
            perror("open");
        }
        close(fd);
        char buf1[128];
        getcwd(buf1,sizeof(buf1));
        printf("当前的工作目录是:%s\n", buf1);
    }
    

    return 0;
}

3.11 目录新建命令int mkdir(const char *pathname, mode_t mode);

/*  
       #include 
       #include 

       int mkdir(const char *pathname, mode_t mode);
        作用:去创建一个目录
        参数:
            -pathname:需要创建的目录的名称
            -mode:权限,八进制数
        返回值:
            -成功:0
            -失败:-1
        
*/
#include 
#include 
#include 
#include

int main(){

    int ret = mkdir("aaa",0777);
        if(ret == -1){
        perror("mkdir");
    }
    else{
        printf("成功\n");
    }
    return 0;
}

3.12 目录遍历命令

struct dirent{
    //此目录进入点的inode
    ino_t d_ino;
    //目录文件开头至此目录进入点的位移
    off_t d off;
    // d_name 的长度,不包含NULL字符
    unsigned short int d_reclen;
    //d_name所指的文件类型
    unsigned char d_type;
    //文件名
    char d name [ 256];
} ;

/*
        #include 
        #include 
        DIR *opendir(const char *name);
        作用:打开一个目录
        参数:
            -path:需要打开目录的路径
        返回值:
            -DIR*目录流信息]
            -失败返回NULL

        #include 
        struct dirent *readdir(DIR *dirp);
        作用:读取目录中的数据,指向下一个目录流的实体
        参数:
            -dirp:通过opendir返回的DIR指针
        返回值:
            -结构体,代表读取到的文件的信息
            -读取到了末尾或者失败,返回NULL



        #include 
        #include 
        int closedir(DIR *dirp);
        作用:关闭目录
        参数:
            -dirp:通过opendir返回的DIR指针
        返回值:


*/
#include
#include
#include
#include
#include
#include
#include<string.h>
#include
int getFilenm(const char * path);
//读取某个目录下所有文件个数
int main(int argc, char * argv[]){
    if(argc<2){
        printf("%s path\n",argv[0]);
        return -1;
    }
    int num = getFilenm(argv[1]);

    printf("普通文件的个数为:%d\n", num);
    return 0;
}
//用于递归获取目录下所有普通文件的个数
int getFilenm(const char * path){
    DIR *dir = opendir(path);
    if(dir==NULL){
        perror("opendir:");
        exit(0);
    }
    //记录文件个数
    int total=0;
    struct dirent * ptr;
    while((ptr = readdir(dir))!=NULL){
        //获取名称,忽略./和../
        char * dname = ptr->d_name;
        
        if(strcmp(dname,".")==0||strcmp(dname,"..")==0){
            continue;
        }
        //判断是普通文件还是目录
        if(ptr->d_type==DT_DIR){
            //目录,需要继续读取这个目录
            char newpath[256];
            sprintf(newpath,"%s/%s", path, dname);
            total += getFilenm(newpath);

        } 
        else if(ptr->d_type== DT_REG){
            total++;
        }

    }
    //关闭目录
    closedir(dir);
    return total;
}

 3.13 复制文件描述符和重定向文件描述符

/*
        #include 

        int dup(int oldfd);
        作用:用文件描述符表中,最小的文件描述符复制原文件描述符
        参数: 
            -旧文件描述符
        返回值:
            -成功:新的文件描述符
            -失败:-1
        int dup2(int oldfd, int newfd);
        作用:重定向文件描述符
        说明:oldfd指向a.txt,newfd指向b.txt,调用函数成功后: newfd和 b.txt做close,newfd指向了a.txt
        参数:用指定的new去复制old,新的指向old
*/

#include
#include
#include
#include
#include
#include
#include<string.h>
#include

int main(){
    //指向1.txt
    int fd = open("1.txt", O_RDWR|O_CREAT,0664);
    if(fd == -1){
        perror("open");
        return -1;
    }
    //指向2.txt
    int fd1 = open("2.txt",O_RDWR|O_CREAT,0664);
    if(fd1 == -1){
        perror("open");
        return -1;
    }
    printf("fd : %d, fd1 : %d\n", fd, fd1);
    //重定向fd1
    int fd2 = dup2(fd , fd1);
    if(fd2 == -1){
        perror("dup2:");
        return -1;
    }
    //通过fd1去写数据,操作的是1.txt而不是2.txt
    char * str = "hello, dup2";
    int len  = write(fd1, str, strlen(str));
    if(len==-1){
        perror("write");
        return -1;
    }
    printf("fd : %d, fd1 : %d, fd2: %d\n", fd, fd1, fd2);
    close(fd);
    close(fd1);
    return 0;
}

3.14 文件描述符操作命令

/* 
       #include 
       #include 
       int fcntl(int fd, int cmd, );
        作用:操作文件描述符
        参数:
            -fd:需要操作的文件描述符
            -cmd:表示对文件描述符进行什么操作
                1.复制文件描述符:-F_DUPFD,复制的是第一个参数fd,返回新的文件描述符
                2.获取指定的文件描述符状态flag:-F_GETFL
                                            获取的包含不限于O_CREAT,O_RWONLY
                3.设置文件描述符文件状态flag,必选项:O_RDONLY,O_WRONLY,O_RDWD,不可以被修改
                                            可选项:O_APPEND,NONBLOCK,可以被修改
                                            NONBLOCK:设置成非阻塞
                阻塞和非阻塞:描述的是函数调用的行为。阻塞等待输入
*/

#include
#include
#include
#include
#include
#include
#include<string.h>
#include
int main(){
    //复制文件描述符
    // int fd = open("1.txt",O_RDONLY);
    // int ret =fcntl(fd, F_DUPFD);

    //2.修改或者获取文件状态flag,修改
    int fd = open("1.txt",O_RDWR);
    if(fd == -1){
        perror("open");
        return -1;
    }
    //首先获取文件描述符状态flag
    int flag = fcntl(fd, F_GETFL);
    if(flag == -1){
        perror("fcntl");
        return -1;
    }
    flag |= O_APPEND;
    //修改状态描述符,加入O_APPEND指令
    int ret = fcntl(fd, F_SETFL, flag);
    if(ret == -1){
        perror("fcntl");
        return -1;
    }
    char * str = "nihao";
    write(fd, str, strlen(str));
    close(fd);

    return 0;
}

相关