(六)Linux系统IO函数

1 open打开文件

  • 打开文件
    int open(const char *pathname, int flags);
  • 创建文件
    int open(const char *pathname, int flags, mode_t mode);
  • 关闭文件
    int close(int fd);

不是重载,C语言不支持重载,是可变参数

open.c

/*
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    // 函数声明在<fcntl.h>,与flag有关的宏定义在了<sys/types.h>,<sys/stat.h>中
    // 打开一个已经存在的文件
    int open(const char *pathname, int flags);
	    参数:
	        - pathname:要打开的文件路径
	        - flags:对文件的操作权限设置还有其他的设置
	          O_RDONLY,  O_WRONLY,  O_RDWR  这三个设置是互斥的
	    返回值:返回一个新的文件描述符,如果调用失败,返回-1(此时,errno会被设置成合适的值)
	    errno:属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号。

    #include <stdio.h>
    void perror(const char *s);
	    作用:打印errno对应的错误描述
	    s参数:用户描述,比如hello,最终输出的内容是  hello:xxx(实际的错误描述)
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main() {

    // 打开一个文件
    int fd = open("a.txt", O_RDONLY);

    if(fd == -1) {
        perror("尝试打开文件");
    }
    // 读写操作

    // 关闭
    close(fd);

    return 0;
}

运行示例代码。

root@LDY:~/cpp-study/lesson09# gcc open.c -o open
root@LDY:~/cpp-study/lesson09# ./open
尝试打开文件: No such file or directory

2 open创建新文件

create.c

/*
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    //  创建文件
    int open(const char *pathname, int flags, mode_t mode);
	    参数:
	        - pathname:要创建的文件的路径
	        - flags:对文件的操作权限和其他的设置
	            - 必选项:O_RDONLY,  O_WRONLY, O_RDWR  这三个之间是互斥的
	            - 可选项:O_CREAT 文件不存在,创建新文件
	        - mode:八进制的数,表示创建出的新的文件的操作权限,比如:0777,
	                最终的权限是:mode & ~umask
	            0777   ->   111111111
	        &   0775   ->   111111101
	        ----------------------------
	                        	111111101
	        按位与:0和任何数都为0
	        umask的作用就是抹去某些权限。
	        本例中,传入0777,umask为0002,最终结果是0775

	        flags参数是一个int类型的数据,占4个字节,32位。
	        flags 32个位,每一位就是一个标志位。
	       (每一种标记都有一位对应1,所以用按位或"|"能实现标记的组合)
	          ... 0 0 0 1
	        | ... 0 0 1 0
	        --------------
	          ... 0 0 1 1
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {

    // 创建一个新的文件
    int fd = open("create.txt", O_RDWR | O_CREAT, 0777);

    if(fd == -1) {
        perror("open");
    }

    // 关闭
    close(fd);

    return 0;
}

查看当前的umask。

root@LDY:~/cpp-study/lesson09# umask
0022

运行示例代码。

root@LDY:~/cpp-study/lesson09# gcc create.c -o create
root@LDY:~/cpp-study/lesson09# ./create
root@LDY:~/cpp-study/lesson09# ll create.txt
-rwxr-xr-x 1 root root 0 Oct 24 21:57 create.txt*

3 read、write函数

  • 读文件
    ssize_t read(int fd, void *buf, size_t count);
  • 写文件
    ssize_t write(int fd, const void *buf, size_t count);

copyfile.c文件复制示例。

/*  
    #include <unistd.h>
    ssize_t read(int fd, void *buf, size_t count);
        参数:
            - fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
            - buf:需要读取数据存放的地方,数组的地址(传出参数)
            - count:指定的数组的大小
        返回值:
            - 成功:
                >0: 返回实际的读取到的字节数
                =0:文件已经读取完了
            - 失败:-1 ,并且设置errno

    #include <unistd.h>
    ssize_t write(int fd, const void *buf, size_t count);
        参数:
            - fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
            - buf:要往磁盘写入的数据,数组
            - count:要写的数据的实际的大小
        返回值:
            成功:实际写入的字节数
            失败:返回-1,并设置errno
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {

    // 1.通过open打开English.txt文件
    int srcfd = open("example.txt", O_RDONLY);
    if(srcfd == -1) {
        perror("open");
        return -1;
    }

    // 2.创建一个新的文件(拷贝文件)
    int destfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
    if(destfd == -1) {
        perror("open");
        return -1;
    }

    // 3.频繁的读写操作
    char buf[1024] = {0};
    int len = 0;
    while((len = read(srcfd, buf, sizeof(buf))) > 0) {
        write(destfd, buf, len);
    }

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

    return 0;
}

运行示例代码。

root@LDY:~/cpp-study/lesson10# gcc copyfile.c -o copy
root@LDY:~/cpp-study/lesson10# ./copy
root@LDY:~/cpp-study/lesson10# tree
.
├── copy
├── copyfile.c
├── cpy.txt
└── example.txt

4 lseek函数

  • 文件指针偏移
    off_t lseek(int fd, off_t offset, int whence);

lseek.c文件扩容示例。

/*  
    标准C库的函数
    #include <stdio.h>
    int fseek(FILE *stream, long offset, int whence);

    Linux系统函数
    #include <sys/types.h>
    #include <unistd.h>
    off_t lseek(int fd, off_t offset, int whence);
        参数:
            - fd:文件描述符,通过open得到的,通过这个fd操作某个文件
            - offset:偏移量
            - whence:
                SEEK_SET
                    设置文件指针的偏移量
                SEEK_CUR
                    设置偏移量:当前位置 + 第二个参数offset的值
                SEEK_END
                    设置偏移量:文件大小 + 第二个参数offset的值
        返回值:返回文件指针的位置


    作用:
        1.移动文件指针到文件头
        lseek(fd, 0, SEEK_SET);

        2.获取当前文件指针的位置
        lseek(fd, 0, SEEK_CUR);

        3.获取文件长度
        lseek(fd, 0, SEEK_END);

        4.拓展文件的长度,当前文件10b, 110b, 增加了100个字节
        lseek(fd, 100, SEEK_END)
        注意:需要写一次数据

*/

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

int main() {

    int fd = open("hello.txt", O_RDWR);

    if(fd == -1) {
        perror("open");
        return -1;
    }

    // 扩展文件的长度
    int ret = lseek(fd, 100, SEEK_END);
    if(ret == -1) {
        perror("lseek");
        return -1;
    }

    // 写入一个空数据,保证扩展成功
    write(fd, " ", 1);

    // 关闭文件
    close(fd);

    return 0;
}

运行前后文件大小变换。

-rw-r--r--  1 root root   11 Oct 25 20:29 hello.txt
-rw-r--r--  1 root root   112 Oct 25 20:36 hello.txt

5 stat、lstat函数

  • 获取文件信息
    int stat(const char *pathname, struct stat *statbuf);
  • 获取软连接文件信息
    int lstat(const char *pathname, struct stat *statbuf);
  • 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; // 最后一次改变时间(指属性)
    };
    

stat.c

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

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

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

*/

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

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

运行示例代码。

root@LDY:~/cpp-study/lesson12# gcc stat.c -o stat
root@LDY:~/cpp-study/lesson12# ./stat
size: 11
  • st_mode变量
    image

6 案例:模拟ls -l命令

ls-l.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>

// 模拟实现 ls -l 指令
// -rw-rw-r-- 1 nowcoder nowcoder 12 12月  3 15:48 a.txt
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';
            break;
        case S_IFDIR:
            perms[0] = 'd';
            break;
        case S_IFREG:
            perms[0] = '-';
            break; 
        case S_IFBLK:
            perms[0] = 'b';
            break; 
        case S_IFCHR:
            perms[0] = 'c';
            break; 
        case S_IFSOCK:
            perms[0] = 's';
            break;
        case S_IFIFO:
            perms[0] = 'p';
            break;
        default:
            perms[0] = '?';
            break;
    }

    // 判断文件的访问权限

    // 文件所有者
    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 * fileGrp = 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, fileGrp, fileSize, mtime, argv[1]);

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

    return 0;
}

运行案例代码。

root@LDY:~/cpp-study/lesson12# gcc ls-l.c -o ls
root@LDY:~/cpp-study/lesson12# ./ls
./ls filename
root@LDY:~/cpp-study/lesson12# ./ls a.txt
-rw-r--r-- 1 root root 11 Tue Oct 25 20:57:14 2022 a.txt
root@LDY:~/cpp-study/lesson12# ls -l a.txt
-rw-r--r-- 1 root root 11 Oct 25 20:57 a.txt
root@LDY:~/cpp-study/lesson12# 

参考链接
https://www.nowcoder.com/study/live/504/1/25

posted @   DaleLee  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示