当前位置: 首页 > news >正文

C语言文件的相关操作

C语言中文件的相关操作

文件的打开

  • 使用文件的打开函数需要引入这个头文件:#include <fcntl.h>

  • open函数

    • int open(char const *pathname, int flags, mode_t mode)
      • 功能:打开已有的文件或者创建新文件
      • 参数
        • pathname:文件路径名,可以是相对路径或绝对路径
        • flags:打开文件的标志,状态标志,多选使用|,常用的有以下几种
          • O_RDONLY:只读
          • O_WRONLY:只写
          • O_RDWR:读写
          • O_APPEND:追加
          • O_CREAT:不存在即创建,已存在即打开
          • O_EXCL:不存在即创建,已存在即报错
          • O_TRUNC:不存在即报错,一般配合O_CREAT使用,已存在即清空
          • O_RDONLYO_WRONLYO_RDWR三者只能选择一个
        • mode:权限模式,格式例如07770755
      • 返回值:反回非负整数作为文件描述符。如果返回-1,表示打开文件失败
  • 使用示例

    #include <stdio.h>  // 使用perror
    #include <fcntl.h>  // 使用openint main(void)
    {// 打开文件,不存在就创建int fd = open("./test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0755);if(fd == -1){perror("open");return -1;}// 操作文件// 关闭文件return 0;
    }
    

文件的关闭

  • 使用文件的关闭函数需要引入这个头文件:#include <unistd.h>

  • close函数

    • int close(int fd);
      • 功能:关闭处于打开状态的文件描述符
      • 参数:fd表示的是处于打开状态的文件描述符
      • 返回值:成功返回0,失败返回-1
  • 使用示例

    #include <stdio.h>  // 使用perror
    #include <fcntl.h>  // 使用open
    #include <unistd.h>  // 使用closeint main(void)
    {// 打开文件,不存在就创建int fd = open("./test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0755);if(fd == -1){perror("open");return -1;}// 操作文件// 关闭文件int ret = close(fd);if(ret == -1){perror("close");return -1;}return 0;
    }
    

文件打开和关闭在内核中的结构

  • 多次打开同一个文件,无论是在同一个进程中还是在不同的进程中,都会在系统内核中产生一个v节点

  • 每次打开文件都会产生一个新的文件表项,各自维护各自的文件状态标志和当前文件偏移

  • 多个进程打开同一个文件,其实是产生了多个文件表项,而v节点其实只有一个

  • 以下是使用一个进程打开一个文件的示例图

    在这里插入图片描述

  • 下面是使用多个进程打开一个文件的示例图
    在这里插入图片描述

文件描述符

  • 为了便于管理在系统中运行的各个进程,内核会维护一张存有各进程信息的列表,称为进程表。系统中的每个进程在进程表中都占有一个表项。每个进程表项都包含了针对特定进程的描述信息,如PIDUIDGID,其中也包含了一个称为文件描述符表的数据结构。

  • 文件描述符的每个表项都至少包含两个数据项–文件描述符表中和文件表项指针,所谓文件描述符,其实就是文件描述符表项在文件描述符表中从0开始的下标,open函数的返回值就是文件描述符表项在文件描述符表中的下标

  • 文件描述符表中,前三个都是默认的,在unistd.h头文件中被定义

    • #define STDIN_FILENO 0 标准输入
    • #define STDOUT_FILENO 1 标准输出
    • #define STDERR_FILENO 2 标准错误
  • 以下是示例图

在这里插入图片描述

  • 下面是根据文件描述表的前三个默认被定义的特性写的一个示例

    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>int main(void)
    {// 如果这里直接使用open,那返回的文件描述符表项一般都是3// 所以我这里做了一些操作,让open返回的文件描述符表项为0close(1);  // 关闭1,标准输出int fd = open("./stdout.txt", O_WRONLY | O_CREAT | O_TRUNC, 0755);if(fd == -1){perror("open");return -1;}printf("fd = %d\n", fd);// 这里如果关闭了fd,会出现写不进去// if(close(fd) == -1)// {//     perror("close");//     return -1;// }return 0;
    }
    
  • 上面一个例子在写的时候发现了一些问题,目前还不知道是啥原因,不能关闭文件,关闭了就会发现写不进去,下面是我用dup2函数写的

    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>int main(void) {int fd = open("./stdout.txt", O_WRONLY | O_CREAT | O_TRUNC, 0755);if (fd == -1) {perror("open");return -1;}// 使用dup2函数将新的文件描述符复制到文件描述符1if (dup2(fd, STDOUT_FILENO) == -1) {perror("dup2");return -1;}printf("输出重定向到文件\n");printf("此时fd=%d\n", fd);if (close(fd) == -1) {perror("close");return -1;}return 0;
    }
    

文件的写入

  • 使用文件的写入函数需要引入这个头文件:#include <unistd.h>

  • write函数

    • ssize_t wirte(int fd, void const *buf, size_t count);
      • 功能:向指定的文件写入数据
      • 参数
        • fd:文件描述符,即open函数的返回值
        • buf:内存缓冲区,即要写入的数据
        • count:期望写入的字节数
      • 返回值:成功返回实际写入的字节数,失败返回-1
  • 示例

    #include <stdio.h>  // perror
    #include <fcntl.h>  // open
    #include <unistd.h>  // close wirte
    #include <string.h>  // strlenint main(void)
    {// 1. 打开文件int fd = open("./test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0755);if(fd == -1){perror("open");return -1;}// 2. 写入数据char *buf = "哈哈";ssize_t size = write(fd, buf, strlen(buf));if(size == -1){perror("write");return -1;}printf("实际写入了%ld个字节\n", size);// 3. 关闭文件if(close(fd) == -1){perror("close");return -1;}return 0;
    }
    

文件的读取

  • 使用文件的读取函数需要引入这个头文件:#include <unistd.h>

  • read函数

    • ssize_t read(int fd, void *buf, size_t count);
      • 功能:从指定的文件中读取数据
      • 参数:
        • fd:文件描述符
        • buf:内存缓冲区,存读取到的数据
        • count:期望读取的字节数
      • 返回值:成功返回实际读取的字节数,失败返回-1
  • 示例

    #include <stdio.h>  // perror
    #include <fcntl.h>  // open
    #include <unistd.h>  // close readint main(void)
    {// 1. 打开文件,这里使用的是 int open(const char *pathname, int flags);,直接读取的int fd = open("./test.txt", O_RDONLY);// 2. 读取数据char buf[16] = {};ssize_t size = read(fd, buf, sizeof(buf));if(size == -1){perror("read");return -1;}printf("读取的字节数是%ld,读取到内容是 %s\n", size, buf);// 3. 关闭文件if(close(fd) == -1){perror("close");return-1;}return 0;
    }
    

补充部分

在 Unix-like 操作系统中,man 命令后面的数字参数用于指定手册页面的节(section)。不同的节对应于不同类型的手册页面,每个节有其特定的内容和用途。以下是常见的 man 命令数字参数及其对应的节:

  • man 1:用户命令(User Commands)。该节包含系统中可用的一般用户命令和可执行程序的手册页面。例如,man 1 ls 将显示与列表命令 ls 相关的手册页面。
  • man 2:系统调用(System Calls)。该节包含操作系统提供的内核级别函数和系统调用接口的手册页面。这些函数和接口用于访问低级别的操作系统功能。例如,man 2 open 将显示与文件系统调用 open 相关的手册页面。
  • man 3:C 库函数(Library Functions)。该节包含标准 C 库函数和其他库函数的手册页面。它提供了函数的接口、参数和返回值等详细信息。例如,man 3 printf 将显示与打印函数 printf 相关的手册页面。
  • 其他节:除了上述常见的节之外,还存在其他节,用于特定目的或特定类型的手册页面。例如,man 5 用于文件格式和配置文件的手册页面,man 7 用于杂项的手册页面。

因此,根据所选择的数字参数,man 命令可以用来查看不同类型的手册页面,从用户命令、系统调用到库函数等。可以根据需要选择适当的数字参数来浏览相关的手册页面,以获取相关命令、函数或系统的详细信息和用法说明。


文件读写的位置

  • 使用lseek函数可以改变文件读写位置,使用它需要引用unistd.h头文件

  • lseek函数

    • off_t lseek(int fd, off_t offset, int whence);
      • 功能: 调整文件读写位置
      • 参数
        • fd:文件描述符
        • offset:文件读写位置偏移字节数
        • whenceoffset参数的偏移起点
          • SEEK_SET:文件头
          • SEEK_CUR:当前位置(最后被读写字节下一个字节)
          • SEEK_END:文件尾
      • 返回值:成功返回文件读写位置,失败返回0
  • 示意图
    在这里插入图片描述

  • 示例代码

    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <string.h>int main(void)
    {// 打开文件int fd = open("./test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);if(fd == -1){perror("open");return -1;}// 写入数据char *w_data = "aaaaaaa";if(write(fd, w_data, strlen(w_data)) == -1){perror("write");return -1;}// 跳转文件读写位置到开头if(lseek(fd, 0, SEEK_SET) == -1){perror("lseek");return -1;}// 写入数据,这里会将之前的数据覆盖,并不会将原来的数据自动往后移char *w_data2 = "abcdefghij";if(write(fd, w_data2, strlen(w_data2)) == -1){perror("write");return -1;}// 调整文件读写位置 3 SEEK_SETif(lseek(fd, 3, SEEK_SET) == -1){perror("lseek");return -1;}// 读取文件数据char r_data1[100] = {};if(read(fd, r_data1, sizeof(r_data1)) == -1){perror("read1");return -1;}printf("第一次读取数据: %s\n", r_data1);// 调整文件读写位置 -5 SEEK_CUR,如果这里填 4 SEEK_CUR,因为上一个读取操作已经将指针移到最后了if(lseek(fd, -5, SEEK_CUR) == -1){perror("lseek");return -1;}// 读取文件数据char r_data2[100] = {};if(read(fd, r_data2, sizeof(r_data2)) == -1){perror("read1");return -1;}printf("第二次读取数据: %s\n", r_data2);// 调整文件读写位置 -2 SEEK_ENDif(lseek(fd, -2, SEEK_END) == -1){perror("lseek");return -1;}// 读取数据char r_data3[100] = {};if(read(fd, r_data3, sizeof(r_data3)) == -1){perror("read");return -1;}printf("第三次读取数据: %s\n", r_data3);// 关闭文件if(close(fd) == -1){perror("close");return -1;}return 0;
    }
    

文件描述符的复制

dup函数

  • dup函数可以复制文件描述项,可以将指定的文件描述项复制到最小的空闲项中,使用这个函数需要引入unistd.h头文件

  • dup函数

    • int dup(int oldfd);
      • 功能:复制文件描述符表的特定条目到最小可用项
      • 参数
        • oldfd:源文件描述符
      • 返回值:成功返回模板文件描述符,失败返回-1
  • 示例图
    在这里插入图片描述

  • 示例代码

    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>
    #include <unistd.h>int main(void)
    {// 打开文件int fd = open("./test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);if(fd == -1){perror("open");return -1;}// 写入数据char *w_data = "abcdefg";if(write(fd, w_data, strlen(w_data)) == -1){perror("write");return -1;}// 使用dup赋值int newfd = dup(fd);if(newfd == -1){perror("dup");return -1;}// 移动fd的文件读写位置到dif(lseek(fd, 3, SEEK_SET) == -1){perror("lseek");return -1;}// 读取newfd的数据,看是否随着fd的读写位置而改变char r_data[100] = {};if(read(fd, r_data, sizeof(r_data)) == -1){perror("read");return -1;}printf("读取newfd的数据: %s\n", r_data);  // defg// 关闭文件if(close(newfd) == -1){perror("newfd close");return -1;}if(close(fd) == -1){perror("fd close");return -1;}return 0;
    }
    

dup2函数

  • dup2函数可以复制文件描述项,可以将指定的文件描述项复制到指定的空闲项中,使用这个函数需要引入unistd.h头文件

  • dup2函数

    • int dup2(int oldfd, int newfd);
      • 功能:复制文件描述符表中特定条目到指定项
      • 参数
        • oldfd:源文件描述符
        • newfd:目标文件描述符
      • 返回值:成功返回目标文件描述符newfd,失败返回-1
      • 使用前一定要确保newfd是空闲的
  • 示例代码

    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>
    #include <unistd.h>int main(void)
    {// 打开文件int fd = open("./test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);if(fd == -1){perror("open");return -1;}// 复制文件描述项int newfd = dup2(fd, STDOUT_FILENO);if(newfd == -1){perror("dup2");return -1;}// 测试是否复制成功printf("test,hahah");// 关闭文件(复制的不需要手动关闭,手动关闭会发现上一步printf写不进去)if(close(fd) == -1){perror("close");return -1;}return 0;
    }
    

访问测试

  • 想要查看操作文件的权限,可以使用access函数,使用这个函数需要引入unistd.h头文件

  • access函数

    • int access(char const *pathname, int mode);
      • 功能:判断当前进程是否可以对某个给定的文件执行某种访问。
      • 参数:
        • pathname:文件路径
        • mode:被测试的权限
          • R_OK:是否可读
          • W_OK:是否可写
          • X_OK:是否可执行
          • F_OK:是否存在
      • 返回值:成功返回0,失败返回-1
  • 示例代码

    #include <stdio.h>
    #include <unistd.h>int main(int argc, char *argv[])
    {// 判断是否输入文件名if(argc < 2){fprintf(stderr, "用法: %s <filename/filepath>\n", argv[0]);return -1;}// 判断文件是否存在if(access(argv[1], F_OK) == -1){printf("%s is no such file!\n", argv[1]);return -1;}printf("%s: ", argv[1]);// 判断文件是否可读if(access(argv[1], R_OK) ==  0)printf("read ");// 判断文件是否可写if(access(argv[1], W_OK) == 0)printf("write ");// 判断文件是否可执行if(access(argv[1], X_OK) == 0)printf("execute");printf("\n");return 0;
    }
    

修改文件大小

  • 以下两个函数是用于修改文件大小的函数,一般在下载的时候都会用到这类函数,使用这两个函数需要引入unistd.h头文件

  • truncate函数

    • int truncate(char const *path, off_t length);

      • 功能:修改指定文件的大小
      • 参数
        • path:文件路径
        • length:文件大小
      • 返回值:成功返回0,失败返回-1
    • 示例代码(这里展示的是截断的效果,会从尾部往前开始截短)

      #include <stdio.h>
      #include <string.h>
      #include <fcntl.h>
      #include <unistd.h>int main(void)
      {// 打开文件int fd = open("./test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);if(fd == -1){perror("open");return -1;}// 写入数据char *data = "hello bhlu";  // 10字节if(write(fd, data, strlen(data)) == -1){perror("write");return -1;}// 将读写位置移到最后,看返回的位置int pos1 = lseek(fd, 0, SEEK_END);if(pos1 == -1){perror("lseek");return -1;}printf("初始状态: 文件字节长度: %d\n", pos1);// 截短文件为4字节if(truncate("./test.txt", 4) == -1){perror("truncate");return -1;}printf("截短成功\n");// 将读写位置移到最后,看返回的位置int pos2 = lseek(fd, 0, SEEK_END);if(pos2 == -1){perror("lseek");return -1;}printf("截短后: 文件字节长度: %d\n", pos2);// 关闭文件if(close(fd) == -1){perror("close");return -1;}return 0;
      }/*
      初始状态: 文件字节长度: 10
      截短成功
      截短后: 文件字节长度: 4此时文件: hell
      */
      
  • ftruncate函数

    • int ftruncate(int fd, off_t length);

      • 功能:修改指定文件的大小
      • 参数
        • fd:文件描述符
        • length:文件大小
      • 返回值:成功返回0,失败返回-1
    • 示例代码(这里展示的加长的效果,加长之后会在尾部空字符填充)

      #include <stdio.h>
      #include <string.h>
      #include <fcntl.h>
      #include <unistd.h>int main(void)
      {// 打开文件int fd = open("./test.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);if(fd == -1){perror("open");return -1;}// 写入数据char *data = "hello";  // 5字节if(write(fd, data, strlen(data)) == -1){perror("write");return -1;}// 将读写位置移到最后,看返回的位置int pos1 = lseek(fd, 0, SEEK_END);if(pos1 == -1){perror("lseek");return -1;}printf("初始状态: 文件字节长度: %d\n", pos1);// 加长文件为10个字节if(ftruncate(fd, 10) == -1){perror("truncate");return -1;}printf("加长成功\n");// 将读写位置移到最后,看返回的位置int pos2 = lseek(fd, 0, SEEK_END);if(pos2 == -1){perror("lseek");return -1;}printf("加长后: 文件字节长度: %d\n", pos2);// 关闭文件if(close(fd) == -1){perror("close");return -1;}return 0;
      }/*
      初始状态: 文件字节长度: 5
      加长成功
      加长后: 文件字节长度: 10此时文件: hello^@^@^@^@^@   ^@就是ASCII码中的空字符
      */
      

文件锁

当一个文件同时被多个进程进行写操作的时候,就会出现造成数据错乱,以下是一个每秒往文件里写一个字符的示例代码。

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>int main(int argc, char *argv[])
{if(argc < 2){fprintf(stderr, "用法: %s <string>\n", argv[0]);return -1;}// 打开文件int fd = open("./test.txt", O_WRONLY | O_CREAT | O_APPEND, 0644);if(fd == -1){perror("open");return -1;}// 写入数据,一秒写一个for(int i = 0; i < strlen(argv[1]); i++){if(write(fd, &argv[1][i], sizeof(argv[1][i])) == -1){perror("write");return -1;}sleep(1);}// 关闭文件if(close(fd) == -1){perror("close");return -1;}return 0;
}

下面使用使用两个进程同时执行这个代码./write haha & ./write bhlu &

在这里插入图片描述

可以看到写入的数据是错乱的,所以才需要使用文件锁,来保证写入数据的正确性。

使用文件锁需要用到fcntl函数,使用这个函数需要引入fcntl.h头文件

  • fcntl函数

    • int fcntl(int fd, F_SETLK/F_SETLKW, struct flock *lock);

      • 功能:加解锁

      • 参数

        • F_SETLK非阻塞模式加锁,F_SETLKW阻塞模式加锁

        • lock对文件要加的锁

          • struct flock {short l_type;  // 锁类型:F_RDLCK/F_WRLCK/F_UNLCKshort l_whence;  // 锁区偏移起点: SEEK_SET/SEEK_CUR/SEEK_ENDoff_t l_start;  // 锁区偏移字节数off_t l_len;  // 锁区字节数pid_t l_pid;  // 加锁进程的PID,-1表示自动设置
            }
            
      • 返回值:成功返回0.失败返回-1

  • 示例代码

    #include <stdio.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>int main(int argc, char *argv[])
    {if(argc < 2){fprintf(stderr, "用法: %s <string>\n", argv[0]);return -1;}// 打开文件int fd = open("./test.txt", O_WRONLY | O_CREAT | O_APPEND, 0644);if(fd == -1){perror("open");return -1;}// 创建锁struct flock lock;lock.l_type = F_WRLCK;  // 写锁lock.l_whence = SEEK_SET;  // 头部开始lock.l_start = 0;lock.l_len = 0;  // 锁到文件尾lock.l_pid = -1;// 阻塞加锁if(fcntl(fd, F_SETLKW, &lock) == -1){perror("fcntl");return -1;}// 写入数据for(int i = 0; i < strlen(argv[1]); i++){if(write(fd, &argv[1][i], sizeof(argv[1][i])) == -1){perror("write");return -1;}sleep(1);}// 解锁struct flock unlock;unlock.l_type = F_UNLCK;  // 解锁unlock.l_whence = SEEK_SET;unlock.l_start = 0;unlock.l_len = 0;unlock.l_pid = -1;if(fcntl(fd, F_SETLKW, &unlock) == -1){perror("fcntl");return -1;}// 关闭文件if(close(fd) == -1){perror("close");return -1;}return 0;
    }
    
  • 使用阻塞加锁的展示图
    在这里插入图片描述

  • 非阻塞加锁部分代码

    // 非阻塞加锁
    while(fcntl(fd, F_SETLK, &lock) == -1)
    {if(errno == EACCES || errno == EAGAIN){printf("文件被锁定,稍等一下!\n");sleep(1);}else {perror("fcntl");return -1;}
    }// 非阻塞解锁
    if(fcntl(fd, F_SETLK, &unlock) == -1)
    {perror("fcntl");return -1;
    }
    
  • 非阻塞加锁效果
    在这里插入图片描述

文件锁的内核结构

  • 每次对给定文件的特定区域加锁,都会通过fcntl函数向系统内核传递flock结构体,该结构体中包含了锁的一些信息,具体可以参考上面的函数介绍。
  • 系统内核收集到所有进程所加的各种锁,就会将flock结构体中的信息以链表的形式形成一张锁表,锁表的起始地址就保存在该文件的v节点中
  • 任何一个进程通过fcntl函数对文件加锁,系统内核都要遍历这张表,当出现已经存在冲突锁的情况下,会出现阻塞或报错。
  • 解锁其实就是调整或删除锁表中的相应节点

在这里插入图片描述

文件的元数据

  • 可以通过以下三个函数获取文件的元数据,使用这三个函数都必须引用sys/stat.h头文件

  • int stat(char const *path, struct stat *buf);

  • int fstat(int fd, struct stat *buf);

  • int lstat(char const *path, struct stat *buf);

    • lstat函数与另外两个函数的区别在于它不跟踪符号链接,假设有一个文件a,现在有一个b软连接指向a,它返回的是b的信息,前面两个都会返回a的信息。

    • 功能:三个函数功能都差不多,从i节点中提取文件的元数据,即文件的属性信息

    • 参数

      • path:文件路径

      • buf:文件元数据结构

          struct stat {dev_t st_dev;  // 设备IDino_t st_ino;  // i节点号mode_t st_mode;  // 文件的类型和权限nlink_t st_nlink;  // 硬链接数uid_t st_uid;  // 拥有者用户idgid_t st_git;  // 拥有者组iddev_t st_rdev;  // 特殊设备IDoff_t st_size;  // 总字节数blksize_t st_blksize;  // I/O块字节数blkcnt_t st_blocks;  // 存储块数time_t st_atime;  // 最后访问时间time_t st_mtime;  // 最后修改时间time_t st_ctime;  // 最后状态改变时间}/*其中mode_t类型,其原始类型在32位系统中被定义unsigned int,但目前只有低16位有意义,即B0-B15B15 - B12:文件类型B11 - B9:设置用户ID,设置组ID,粘滞B8 - B6:属主权限B5 - B3:数组权限B2 - B0:其他用户权限*/
        

        在这里插入图片描述

      • fd:文件描述符

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

  • 示例代码

    // 输出文件的元数据
    #include <stdio.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <time.h>// 转类型和权限
    char *f_mode(mode_t m)
    {static char s[11];if (S_ISDIR(m)){strcpy(s, "d");  // 目录}else if(S_ISSOCK(m)){strcpy(s, "s");  // 本地套接字 }else if(S_ISCHR(m)){strcpy(s, "c");  // 字符设备}else if(S_ISBLK(m)){strcpy(s, "b");  // 块设备}else if(S_ISLNK(m)){strcpy(s, "l");  // 符号链接}else if(S_ISFIFO(m)){strcpy(s, "p");  // 有名管道}else{strcpy(s, "-");}// 属主 属组 其他用户 权限strcat(s, m & S_IRUSR ? "r" : "-");strcat(s, m & S_IWUSR ? "w" : "-");strcat(s, m & S_IXUSR ? "x" : "-");strcat(s, m & S_IRGRP ? "r" : "-");strcat(s, m & S_IWGRP ? "w" : "-");strcat(s, m & S_IXGRP ? "x" : "-");strcat(s, m & S_IROTH ? "r" : "-");strcat(s, m & S_IWOTH ? "w" : "-");strcat(s, m & S_IXOTH ? "x" : "-");return s;
    }// 转换时间
    char *f_time(time_t t)
    {static char time[20];struct tm *l = localtime(&t);sprintf(time, "%04d-%02d-%02d %02d:%02d:%02d",l->tm_year + 1900, l->tm_mon + 1, l->tm_mday, l->tm_hour, l->tm_min, l->tm_sec);return time;
    }int main(int argc, char *argv[])
    {if(argc < 2){fprintf(stderr, "用法: %s <文件名>\n", argv[0]);return -1;}// 获取文件的元数据struct stat s;if(stat(argv[1], &s) == -1){perror("stat");return -1;}printf("设备ID: %lu\n", s.st_dev);printf("i节点号: %ld\n", s.st_ino);printf("硬链接数: %lu\n", s.st_nlink);printf("用户ID: %u\n", s.st_uid);printf("组ID: %u\n", s.st_gid);printf("特殊设备ID: %lu\n", s.st_rdev);printf("总字节数: %ld\n", s.st_size);printf("IO块字节数: %ld\n", s.st_size);printf("存储块数: %ld\n", s.st_blksize);printf("文件类型和权限: %s\n", f_mode(s.st_mode));printf("最后访问时间: %s\n", f_time(s.st_atime));printf("最后修改时间: %s\n", f_time(s.st_mtime));printf("最后状态修改时间: %s\n", f_time(s.st_ctime));return 0;
    }
    

内存映射文件

之前已经介绍过内存映射的建立与解除,以下是使用内存映射文件的例子

// 通过映射打开文件并写入数据
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>int main(void)
{// 打开文件int fd = open("./mmap.txt", O_RDWR | O_CREAT | O_TRUNC);if(fd == -1){perror("open");return -1;}// 修改文件大小if(ftruncate(fd, 4096) == -1){perror("ftruncate");return -1;}// 获取文件大小int f_len = lseek(fd, 0, SEEK_END);if(f_len == -1){perror("lseek");return -1;}// 建立文件映射char *start = mmap(NULL, f_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if(start == MAP_FAILED){perror("mmap");return -1;}// 写入数据strcpy(start, "haha\n");// 解除映射if(munmap(start, f_len) == -1){perror("munmap");return -1;}// 关闭文件描述符close(fd);return 0;
}

相关文章:

C语言文件的相关操作

C语言中文件的相关操作 文件的打开 使用文件的打开函数需要引入这个头文件&#xff1a;#include <fcntl.h> open函数 int open(char const *pathname, int flags, mode_t mode) 功能&#xff1a;打开已有的文件或者创建新文件参数 pathname&#xff1a;文件路径名&…...

Java入门级简单定时任务TimerTask

如果要执行一些简单的定时器任务&#xff0c;无须做复杂的控制&#xff0c;也无须保存状态&#xff0c;那么可以考虑使用JDK 入门级的定期器Timer来执行重复任务。 一、原理 JDK中&#xff0c;定时器任务的执行需要两个基本的类&#xff1a; java.util.Timer; java…...

Linux命令行教程:使用head和tail命令快速查看文件的开头和结尾

文章目录 简介A. 什么是head和tail命令B. head和tail命令的作用和用途 head命令A. 命令格式和语法B. 常见选项和参数1. -n&#xff1a;指定显示的行数2. -c&#xff1a;指定显示的字节数3. -v&#xff1a;显示文件名 C. 示例和应用实例1. 显示文件的前几行2. 显示多个文件的前几…...

[CISCN 2019 初赛]Love Math 通过进制转换执行命令

目录 hex2bin bin2hex base_convert 动态函数 第一种解法 通过get获取参数 绕过 第二种解法 读取请求头 getallheaders echo a,b 第三种解法 异或获得更多字符 这道题也是很有意思&#xff01; 通过规定白名单和黑名单 指定了 函数为数学函数 并且参数也只能是规…...

【Linux】系统编程生产者消费者模型(C++)

目录 【1】生产消费模型 【1.1】为何要使用生产者消费者模型 【1.2】生产者消费者模型优点 【2】基于阻塞队列的生产消费者模型 【2.1】生产消费模型打印模型 【2.2】生产消费模型计算公式模型 【2.3】生产消费模型计算公式加保存任务模型 【2.3】生产消费模型多生产多…...

【数据结构】图的应用:最小生成树;最短路径;有向无环图描述表达式;拓扑排序;逆拓扑排序;关键路径

目录 1、最小生成树 1.1 概念 1.2 普利姆算法&#xff08;Prim&#xff09; 1.3 克鲁斯卡尔算法&#xff08;Kruskal&#xff09; 2、最短路径 2.1 迪杰斯特拉算法&#xff08;Dijkstra&#xff09; 2.2 弗洛伊德算法&#xff08;Floyd&#xff09; 2.3 BFS算法&…...

大数据驱动业务增长:数据分析和洞察力的新纪元

文章目录 大数据的崛起大数据的特点大数据技术 大数据驱动业务增长1. 洞察力和决策支持2. 个性化营销3. 风险管理4. 产品创新 大数据分析的新纪元1. 云计算和大数据示例代码&#xff1a;使用AWS的Elastic MapReduce&#xff08;EMR&#xff09;进行大数据分析。 2. 人工智能和机…...

科技云报道:分布式存储红海中,看天翼云HBlock如何突围?

科技云报道原创。 过去十年&#xff0c;随着技术的颠覆性创新和新应用场景的大量涌现&#xff0c;企业IT架构出现了稳态和敏态的混合化趋势。 在持续产生海量数据的同时&#xff0c;这些新应用、新场景在基础设施层也普遍基于敏态的分布式架构构建&#xff0c;从而对存储技术…...

Java高级-动态代理

动态代理 1.介绍2.案例 1.介绍 public interface Star {String sing(String name);void dance(); }public class BigStar implements Star{private String name;public BigStar(String name) {this.name name;}public String sing(String name) {System.out.println(this.name…...

时序预测 | MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络时间序列预测

时序预测 | MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络时间序列预测 目录 时序预测 | MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现POA-CNN-LSTM鹈鹕算法优化卷积长短…...

n个不同元素进栈,求出栈元素的【不同排列】以及【排列的数量】?

我在网上看的博客大部分是告诉你这是卡特兰数&#xff0c;然后只给出了如何求解有多少种排列&#xff0c;没有给出具体排列是怎么样的。如果你还不知道卡特兰数&#xff0c;请查看&#xff1a;https://leetcode.cn/circle/discuss/lWYCzv/ 这里记录一下如何生成每种具体的排列…...

Python中TensorFlow的长短期记忆神经网络(LSTM)、指数移动平均法预测股票市场和可视化...

原文链接&#xff1a;http://tecdat.cn/?p23689 本文探索Python中的长短期记忆&#xff08;LSTM&#xff09;网络&#xff0c;以及如何使用它们来进行股市预测&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 相关视频 在本文中&#xff0c;你将看到如何使用…...

多线程的学习第二篇

多线程 线程是为了解决并发编程引入的机制. 线程相比于进程来说,更轻量 ~~ 更轻量的体现: 创建线程比创建进程,开销更小销毁线程比销毁进程,开销更小调度线程比调度进程,开销更小 进程是包含线程的. 同一个进程里的若干线程之间,共享着内存资源和文件描述符表 每个线程被独…...

git之撤销工作区的修改和版本回溯

有时候在工作区做了一些修改和代码调试不想要了,可如下做 (1)步骤1:删除目录代码,确保.git目录不能修改 (2)git log 得到相关的commit sha值 可配合git reflog 得到相要的sha值 (3)执行git reset --hard sha值,可以得到时间轴任意版本的代码 git reset --hard sha值干净的代…...

sed awk使用简介

简介 本文主要介绍 Linux 系统的两个神级工具&#xff1a;sed 和 awk &#xff0c;他们是Linux高手们必备的技能&#xff0c;很值得我们去研究的东西。 这里是我在网上书上收集的相关资料&#xff0c;因为这两个工具很有名也很重要&#xff0c;所以这些资料会帮助我更好的了解…...

竞赛选题 基于深度学习的人脸识别系统

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的人脸识别系统 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/…...

idea Terminal 回退历史版本 Git指令 git reset

——————强制回滚历史版本—————— 一、idea Terminal 第一步&#xff1a;复制版本号 &#xff08;右击项目–> Git --> Show History -->选中要回退的版本–>Copy Revision Number&#xff0c;直接复制&#xff1b;&#xff09; 第二步&#xff1a;ide…...

华为云云耀云服务器L实例评测|华为云上安装监控服务Prometheus三件套安装

文章目录 华为云云耀云服务器L实例评测&#xff5c;华为云上试用监控服务Prometheus一、监控服务Prometheus三件套介绍二、华为云主机准备三、Prometheus安装四、Grafana安装五、alertmanager安装六、三个服务的启停管理1. Prometheus、Alertmanager 和 Grafana 启动顺序2. 使用…...

C语言基础知识点(八)联合体和大小端模式

以下程序的输出是&#xff08;&#xff09; union myun {struct { int x, y, z;} u;int k; } a; int main() {a.u.x 4;a.u.y 5;a.u.z 6;a.k 0;printf("%d\n", a.u.x); } 小端模式 数据的低位放在低地址空间&#xff0c;数据的高位放在高地址空间 简记&#xff…...

一个线程运行时发生异常会怎样?

如果一个线程在运行时发生异常而没有被捕获(即未被适当的异常处理代码处理),则会导致以下几种情况之一: 线程终止:线程会立即终止其执行,并将异常信息打印到标准错误输出(System.err)。这通常包括异常的类型、堆栈跟踪信息以及异常消息。 ThreadDeath 异常:在某些情况…...

CSS中去掉li前面的圆点方法

1. 引言 在网页开发中&#xff0c;我们经常会使用无序列表&#xff08;<ul>&#xff09;来展示一系列的项目。默认情况下&#xff0c;每个列表项&#xff08;<li>&#xff09;前面都会有一个圆点作为标记。然而&#xff0c;在某些情况下&#xff0c;我们可能希望去…...

Python:获取当前目录下所有文件夹名称及文件夹下所有文件名称

获取当前目录下所有文件夹名称 def get_group_list(folder_path):group_list []for root, dirs, files in os.walk(folder_path):for dir in dirs:group_list.append(dir)return group_list获取文件夹下所有文件名称 def get_file_list(folder_path, group_name):file_list …...

系统架构设计师-数据库系统(1)

目录 一、数据库模式 1、集中式数据库 2、分布式数据库 二、数据库设计过程 1、E-R模型 2、概念结构设计 3、逻辑结构设计 三、关系代数 1、并交差 2、投影和选择 3、笛卡尔积 4、自然连接 一、数据库模式 1、集中式数据库 三级模式&#xff1a; &#xff08;1&#xff09;外…...

Docker的相关知识介绍以及mac环境的安装

一、什么是Docker 大型项目组件较多&#xff0c;运行环境也较为复杂&#xff0c;部署时会碰到一些问题&#xff1a; 依赖关系复杂&#xff0c;容易出现兼容性问题开发、测试、生产环境有差异 Docker就是来解决这些问题的。Docker是一个快速交付应用、运行应用的技术&#x…...

Android设计支持库

本文所有的代码均存于 https://github.com/MADMAX110/BitsandPizzas 设计支持库&#xff08;Design Support Library&#xff09;是 Google 在 2015 年的 I/O 大会上发布的全新 Material Design 支持库&#xff0c;在这个 support 库里面主要包含了 8 个新的 Material Design …...

【Java 基础篇】Java实现文件搜索详解

文件搜索是计算机应用中的一个常见任务&#xff0c;它允许用户查找特定文件或目录&#xff0c;以便更轻松地管理文件系统中的内容。在Java中&#xff0c;您可以使用各种方法来实现文件搜索。本文将详细介绍如何使用Java编写文件搜索功能&#xff0c;以及一些相关的内容。 文件…...

会C++还需要再去学Python吗?

提到的C、数据结构与算法、操作系统、计算机网络和数据库技术等确实是计算机科学中非常重要的基础知识领域&#xff0c;对于软件开发和计算机工程师来说&#xff0c;它们是必备的核心知识。掌握这些知识对于开发高性能、可靠和安全的应用程序非常重要。Python作为一种脚本语言&…...

vue部分/所有内容全屏切换展示

需求&#xff1a;就是把一个页面的某一部分内容点击全屏操作按钮后全屏展示&#xff0c;并非所有内容全屏&#xff0c;所有内容的话那肯定就所有全屏展示啊&#xff0c;可以做切换 1.部分全屏代码 element.requestFullscreen();这个就是全屏的代码了&#xff0c;注意前面的ele…...

8.gec6818开发板通过并发多线程实现电子相册 智能家居 小游戏三合一完整项目

并发 前面编写的程序都是从mian函数开始&#xff0c;从上往下执行&#xff0c;称为顺序执行 假设一个程序需要I输入 C计算 P输出&#xff0c;以顺序执行三个上述程序&#xff0c;则其执行过程如下&#xff1a; 程序内部的语句是一条一条的执行&#xff0c;如果要运行多个程序…...

角度回归——角度编码方式

文章目录 1.为什么研究角度的编码方式&#xff1f;1.1 角度本身具有周期性1.2 深度学习的损失函数因为角度本身的周期性&#xff0c;在周期性的点上可能产生很大的Loss&#xff0c;造成训练不稳定1.3 那么如何处理边界问题呢&#xff1a;&#xff08;以θ的边界问题为例&#x…...

教育网站开发文档/推广找客户平台

第六章数理统计的基本知识 数理统计是以概率论为基础,根据试验或观察得到数据,来研究随机现象.通过统计分析,对研究对象的客观规律性作出合理的估计和推断. 6.1总体和样本 在一个统计问题中,我们把所研究对象的全体称为一个总体 .总体中的每个元素(即每一个研究对象)称为个体 .…...

中国建设银行分行网站/金华网站建设

MyBatis是什么 MyBatis是什么&#xff0c;MyBatis的jar包中有它的官方文档&#xff0c;文档是这么描述MyBatis的&#xff1a; MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings. MyBatis eliminates alm…...

做网站可以用中文域名备案嘛/推广资源整合平台

目录 eLCDIF接口 相关寄存器 配置步骤 LCD像素时钟的设置 eLCDIF接口 LCDIF是I.MX6U自带的液晶屏幕接口,用于连接RGB LCD接口的屏幕,eLCDIF接口特性如下: 1.支持RGB LCD的DE模式。 2.支持VSYNC模式以实现高速数据传输。 3.支持ITU-R BT.656格式的4:2:2的YCbCr 数字视频…...

golang和php 做网站/服务营销包括哪些内容

package com.day10.Date类;import java.util.Date;public class DemoDate {/*** 常见对象(Date类的概述和方法使用)(掌握)* A:Date类的概述*    类 Date 表示特定的瞬间&#xff0c;精确到毫秒。* B:构造方法*    public Date()*   public Date(long date)* C:成员方法*…...

大连城市建设档案馆官方网站/杭州疫情最新情况

1. __attribute__ GNU C的一大特色&#xff08;却不被初学者所知&#xff09;就是__attribute__机制。 __attribute__可以设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute) __attribute__前后都有两个下划线&#xff0c;并且后面…...

新疆网络干部学院平台/潍坊seo建站

前言 本游戏纯属搞笑&#xff0c;你可以把你朋友的照片设置成贪吃蛇的蛇头&#xff0c;你们看最终结果就知道啦 相关文件 大家可以关注小编的公众号&#xff1a;Python日志 里面会不定时的发布一下Python小知识和一些高校有的源码的 源码获取可以在公众号里面回复&#xff1…...