文件IO操作

news/2024/9/21 14:38:56

文件操作

cache:是读的缓冲区,读内容先读到cache中,是读的加速机制

buffer:是写的缓冲区,写内容先写到buff中,是写的加速机制

对一个文件的操作有两种不同的方式,既可以使用由操作系统直接提供的编程接口
(API),即系统调用,也可以使用由标准C库提供的标准IO函数。

系统IO

open

功能 打开一个指定的文件并获得文件描述符,或者创建一个新文件
头文件 <sys/types.h>
<sys/stat.h>
<fcntl.h>
原型 int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
参数 - pathname:即将要打开的文件路径
- flags
- O_RDONLY:只读方式打开文件
- O_WRONLY:只写方式打开文件
- O_RDWR:读写方式打开文件
- O_CREAT:如果文件不存在,则创建该文件
- O_EXCL:如果使用 O_CREAT 选项且文件存在,则返回错误消息
- O_NOCTTY:如果文件为终端,那么终端不可以作为调用 open() 系统调用的那个进程的控制终端
- O_TRUNC:如文件已经存在,则删除文件中原有数据
- O_APPEND:以追加方式打开文件
- mode:如果文件被新建,指定其权限为 mode(八进制表示法)
返回值 成功:大于等于0的整数(即文件描述符)
失败:-1
备注

** 示例代码**

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>int main()
{int fd;//以读写的方式创建新文件    fd=open("myhello",O_RDWR | O_CREAT,0777);//以读写的方式创建新文件,并判断是否存在    //fd=open("myhello",O_RDWR | O_CREAT | O_EXCL,0777);//以读写的方式创建新文件,若文件存在则进行截断(清空文件内容)  //fd = open("myhello", O_RDWR | O_CREAT | O_TRUNC, 0777);if (fd < 0){printf("can not open file %s\n", argv[1]);printf("errno = %d\n", errno);printf("err: %s\n", strerror(errno));perror("open");}else{printf("fd = %d\n", fd);}close(fd);return 0;
}

close

功能 关闭一个打开的文件描述符
头文件 <unistd.h>
原型 int close(int fd);
参数 fd:要关闭的文件描述符
返回值 成功:0
失败:-1
备注 重复关闭一个已经关闭的文件或者尚未打开的文件是安全的

示例代码

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>int main(int argc, char *argv[])
{int fd;// 假设 fd 是已经打开的文件描述符int ret = close(fd);if (ret < 0){printf("can not close file descriptor %d\n", fd);printf("errno = %d\n", errno);printf("err: %s\n", strerror(errno));perror("close");}return 0;
}

read

功能 从指定文件中读取数据
头文件 <unistd.h>
原型 ssize_t read(int fd, void *buf, size_t count);
参数 - fd:从文件 fd 中读取数据
- buf:指向存放读到的数据的缓冲区
- count:想要从文件 fd 中读取的字节数
返回值 成功:实际读到的字节数
失败:-1
备注 实际读到的字节数小于等于 count
  • 如果返回值是0,说明读到文件末尾

write

功能 将数据写入指定的文件
头文件 <unistd.h>
原型 ssize_t write(int fd, const void *buf, size_t count);
参数 - fd:将数据写入到文件 fd
- buf:指向即将要写入的数据
- count:要写入的字节数
返回值 成功:实际写入的字节数
失败:-1
备注 实际写入的字节数小于等于 count

** 示例代码 **

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>int main() {// 打开文件int fd = open("example.txt", O_RDWR | O_CREAT, 0666);if (fd < 0) {perror("open");exit(EXIT_FAILURE);}// 写入数据const char *message = "Hello, this is a test message.\n";ssize_t bytes_written = write(fd, message, strlen(message));if (bytes_written < 0) {perror("write");close(fd);exit(EXIT_FAILURE);}// 读取数据char buffer[100];lseek(fd, 0, SEEK_SET); // 将文件指针移至文件开头ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);if (bytes_read < 0) {perror("read");close(fd);exit(EXIT_FAILURE);}buffer[bytes_read] = '\0'; // 添加字符串结束符// 输出读取的数据printf("Data read from file: %s", buffer);// 关闭文件close(fd);return 0;
}
  • 这个程序的作用是打开一个文件,向文件中写入一条消息,然后再读取出来并打印。

dup dup2

功能 复制文件描述符
头文件 <unistd.h>
原型 int dup(int oldfd);
int dup2(int oldfd, int newfd);
参数 - oldfd:要复制的文件描述符
- newfd:指定的新文件描述符
返回值 成功:新的文件描述符
失败:-1
备注

dup是英文单词duplicate的缩写,意味着复制一个已有的文件描述符,dup将会返回一个最小未用的文件描述符作为拷贝;

dup2则可以通过第二个参数来指定描述符,如果这个描述符已经存在,则将会被覆盖。

lseek

功能 调整文件位置偏移量
头文件 <sys/types.h>
<unistd.h>
原型 off_t lseek(int fd, off_t offset, int whence);
参数 - fd:要调整位置偏移量的文件的描述符
- offset:新位置偏移量相对基准点的偏移
- whence:基准点
- SEEK_SET:文件开头处
- SEEK_CUR:当前位置
- SEEK_END:文件末尾处
返回值 成功:新文件位置偏移量
失败:-1
备注 只对普通文件有效,特殊文件是无法调整偏移量的

** 示例代码 **

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>int main() {// 打开文件int fd = open("example.txt", O_RDWR);if (fd < 0) {perror("open");exit(EXIT_FAILURE);}// 设置新的位置偏移量off_t new_offset = lseek(fd, 0, SEEK_SET);if (new_offset == -1) {perror("lseek");close(fd);exit(EXIT_FAILURE);}// 关闭文件close(fd);return 0;
}

ioctl fcntl

功能 文件控制
头文件 <sys/ioctl.h>
原型 int ioctl(int fd, int request, ...);
参数 - fd:要控制的文件描述符
- request:针对不同文件的各种控制命令字
- ...:根据不同的命令字而不同
返回值 成功:一般情况下是0,但有些特定的请求将返回非负整数
失败:-1
备注

request是一个由底层驱动提供的命令字, 一些通用的命令字被放置在头文件/usr/include,(不同的系统存放位置也许不同)中,后面的变参也由前面的request命令字决定。

ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情况下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。

ioctl 函数用于对设备进行控制操作,它是一种通用的设备 I/O 控制接口。通过 ioctl 函数,可以向设备发送控制命令,或者获取设备的状态信息。这些控制命令通常由预定义的常量表示,称为 IOCTL 命令字。

使用 ioctl 函数可以实现很多设备相关的功能,例如:

  • 设置设备参数:如串口的波特率、数据位、停止位等;
  • 控制设备行为:如开启或关闭设备的某些功能;
  • 查询设备状态:如获取设备的错误信息、缓冲区状态等。
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <linux/fb.h>int main(int argc, char const *argv[])
{//1.打开LCDint lcd_fd = open("/dev/fb0",O_RDWR);//2.利用ioctl函数获取LCD硬件参数struct fb_var_screeninfo lcd_vinfo;ioctl(lcd_fd,FBIOGET_VSCREENINFO,&lcd_vinfo);//3.输出LCD的宽、高、色深printf("lcd height= %-5d\n", lcd_vinfo.yres); 		//480   printf("lcd width= %-5d\n", lcd_vinfo.xres);  		//800printf("lcd bpp=%-5d\n",lcd_vinfo.bits_per_pixel);	//32return 0;
}
  • 通过 ioctl(lcd_fd, FBIOGET_VSCREENINFO, &lcd_vinfo) 调用,将请求发送给 LCD 设备文件描述符 lcd_fd,并将 LCD 设备的硬件参数保存在结构体 lcd_vinfo 中。

fcntl

功能 文件控制
头文件 <unistd.h>
<fcntl.h>
原型 int fcntl(int fd, int cmd, .../*arg*/);
参数 - fd:要控制的文件描述符
- cmd:控制命令字
- .../*arg*/:根据不同的命令字而不同
返回值 成功:根据不同的 cmd,返回值不同
失败:-1
备注

fcntl 函数用于对文件描述符进行各种控制操作,例如设置文件状态标志、获取文件状态标志、复制文件描述符等。其功能包括但不限于:

  • 修改已打开文件的属性,比如设置文件的状态标志(例如设置非阻塞模式)、文件描述符的标志、文件的读写锁等。
  • 复制文件描述符,使两个描述符指向相同的文件。
  • 获取已打开文件的各种属性信息,如获取文件状态标志、获取文件的读写锁信息等。

这些操作可以通过 cmd 参数来指定。cmd 参数决定了 fcntl 函数的具体行为,例如 F_GETFL 用于获取文件状态标志,F_SETFL 用于设置文件状态标志,F_DUPFD 用于复制文件描述符等。

总的来说,fcntl 函数是一个灵活的接口,用于实现对文件描述符的各种控制操作。

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>int main() {// 打开文件int fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("open");return 1;}// 获取当前文件状态标志int flags = fcntl(fd, F_GETFL);if (flags == -1) {perror("fcntl");return 1;}// 设置文件状态标志为非阻塞模式if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {perror("fcntl");return 1;}// 演示读取文件,此时设置了非阻塞模式char buffer[1024];ssize_t bytes_read = read(fd, buffer, sizeof(buffer));if (bytes_read == -1) {perror("read");} else {printf("Read %zd bytes: %s\n", bytes_read, buffer);}// 关闭文件close(fd);return 0;
}

在这个示例中,我们首先使用 open 函数打开一个文件,然后使用 fcntl 函数获取文件的状态标志,接着设置文件的状态标志为非阻塞模式,并最后读取文件。

mmap

功能 将文件或其他对象映射到内存
头文件 <sys/mman.h>
原型 void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数 - addr:指定映射区域的首地址,通常设为 NULL,让内核选择适当的地址
- length:映射区域的长度
- prot:保护映射区域的权限
- PROT_READ:允许读取映射区域的内容
- PROT_WRITE:允许写入映射区域的内容
- PROT_EXEC:允许执行映射区域的内容
- PROT_NONE:拒绝对映射区域的访问
- flags:控制映射区域的属性
- MAP_SHARED:共享映射,对映射区域的修改会影响到文件或其他映射该文件的进程
- MAP_PRIVATE:私有映射,对映射区域的修改不会影响到文件或其他映射该文件的进程
- MAP_ANONYMOUS:创建匿名映射,不与文件关联,常用于创建匿名内存
- fd:要映射的文件描述符,如果映射的是匿名内存,则为 -1
- offset:映射文件的偏移量,通常设为 0
返回值 成功:映射区域的起始地址
失败:MAP_FAILED--->(void*)-1
备注 - 如果映射的是文件,则 fd 参数指定文件描述符,offset 参数指定文件的偏移量;如果映射的是匿名内存,则 fd 参数为 -1offset 参数无效。
- 映射区域的长度必须是页面大小的整数倍,通常使用 sysconf(_SC_PAGE_SIZE) 获取页面大小。
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>int main() {// 打开文件int fd = open("example.txt", O_RDONLY);if (fd == -1) {perror("open");return 1;}// 获取文件大小struct stat file_stat;if (fstat(fd, &file_stat) == -1) {perror("fstat");close(fd);return 1;}off_t file_size = file_stat.st_size;// 映射文件到内存void *addr = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);if (addr == MAP_FAILED) {perror("mmap");close(fd);return 1;}// 读取映射区域的内容printf("Mapped content: %s\n", (char *)addr);// 解除内存映射if (munmap(addr, file_size) == -1) {perror("munmap");close(fd);return 1;}// 关闭文件close(fd);return 0;
}

标准IO

fopen

函数 fopen
功能 打开一个文件
头文件 <stdio.h>
原型 FILE *fopen(const char *path, const char *mode);
参数 - path:即将要打开的文件路径名
- mode
- "r": 以只读方式打开文件,要求文件必须存在。
- "r+": 以读写方式打开文件,要求文件必须存在。
- "w": 以只写方式打开文件,文件如果不存在将会创建新文件,如果存在将会将其内容清空。
- "w+": 以读写方式打开文件,文件如果不存在将会创建新文件,如果存在将会将其内容清空。
- "a": 以只写方式打开文件,文件如果不存在将会创建新文件,且文件位置偏移量被自动定位到文件末尾(即以追加方式写数据)。
- "a+": 以读写方式打开文件,文件如果不存在将会创建新文件,且文件位置偏移量被自动定位到文件末尾(即以追加方式写数据)。
返回值 成功:FILE * ----> 文件指针
失败:NULL
备注 最好要以二进制文件打开rb``wb
使用标准IO的时候,是不可以反复关闭相同的文件,因为释放已经被释放的堆内存,会导致段错误!

思考:fopen函数的返回值是一个指向被打开文件的FILE类型的指针,请问FILE类型是什么?

回答:FILE类型其实是一个结构体数据类型,它包含了标准 I/O 库函数为管理文件所需要的所有信息,比如包括用于实际I/O 的文件描述符、指向文件缓冲区的指针、缓冲区的长度、当前缓冲区中的字节数以及出错标志等。头文件stdio.h中有关于FILE类型的相关描述,

fclose

函数 fclose
功能 关闭一个文件
头文件 <stdio.h>
原型 int fclose(FILE *stream);
参数 stream:要关闭的文件流指针
返回值 成功:0
失败:非0
备注 不能对同一个文件重复关闭

image-20240514230440287

  • 使用标准IO函数处理文件的最大特点是,数据将会先存储在一个标准IO 缓冲区中,而后在一定条件下才被一并flush(冲洗,或称刷新)至内核缓冲区,而不是像系统IO那样,数据直接被flush至内核。 注意到,标准IO函数fopen()实质上是系统IO函数open()的封装,他们是一一对应的,每一次fopen()都会导致系统分配一个file{ }结构体和一个FILE{}来保存维护该文件的读写信息,每一次的打开和操作都可以不一样,是相对独立的,因此可以在多线程或者多进程中多次打开同一个文件,再利用文件空洞技术进行多点读写。

  • 默认打开的三个标准文件,标准输出入输出设备在系统IO是默认被打开的,在标准IO也是一样,在程序开始就已经拥有相应的文件指针

设备 文件描述符(int) 文件指针(FILE *) 标准输入设备(键盘)
标准输入设备 0 STDIN_FILENO stdin
标准输出设备 1 STDOUT_FILENO stdout
标准出错设备 2 STDERR_FILENO stderr

每次一个字符的读写标准IO函数接口

fgetc getc getchar

功能 获取指定文件的一个字符
头文件 <stdio.h>
原型 int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
参数 stream:文件指针
返回值 成功读取到的字符
失败:EOF
备注 当返回EOF时,文件stream可能已达末尾,或者遇到错误

fputc putc putchar

功能 将一个字符写入一个指定的文件
头文件 <stdio.h>
原型 int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
参数 c:要写入的字符
stream:写入的文件指针
返回值 成功写入到的字符
失败:EOF
备注

1,fgec()、getc()和getchar()返回值是int,而不是char,原因是因为他们在出错或者读到文件末尾的时候需要返回一个值为-1的EOF标记,而char型数据有可能因 为系统的差异而无法表示负整数。

2,当fgec()、getc()和getchar()返回EOF时,有可能是发生了错误,也有可能是读到了文件末尾,需要用 feof(),ferror()来判断。

feof ferror

功能 判断一个文件是否到达文件末尾
头文件 <stdio.h>
原型 int feof(FILE *stream);
int ferror(FILE *stream);
参数 stream:进行判断的文件指针
返回值 feof:如果文件已达末尾则返回真,否则返回假
ferror:如果文件遇到错误则返回真,否则返回假
备注

每次一行字符的读写标准IO函数接口

fgetc gets

功能 从指定文件读取最多一行数据
头文件 <stdio.h>
原型 char *fgets(char *s, int size, FILE *stream);
char *gets(char *s);
参数 s:自定义缓冲区指针
size:自定义缓冲区大小
stream:即将被读取数据的文件指针
返回值 成功:自定义缓冲区指针 s
失败:NULL
备注 1. gets() 缺省从文件 stdin 读入数据
2. 当返回 NULL 时,文件 stream 可能已达末尾,或者遇到错误

fputs puts

功能 将数据写入指定的文件
头文件 <stdio.h>
原型 int fputs(const char *s, FILE *stream);
int puts(const char *s);
参数 s:自定义缓冲区指针
stream:即将被写入数据的文件指针
返回值 成功:非负整数
失败:EOF
备注 puts() 缺省将数据写入文件 stdout
  1. fgets()fgetc()一样,当其返回NULL时并不能确定究竟是达到文件末尾还是 碰到错误,需要用feof()/ferror()来进一步判断。
  2. fgets()每次读取至多不超过size个字节的一行,所谓“一行”即数据至多包含一 个换行符’\n’。
  3. gets()是一个已经过时的接口,因为他没有指定自定义缓冲区s的大小,这样很容 易造成缓冲区溢出,导致程序段访问错误。
  4. fgets()fputs()gets()puts()一般成对使用,鉴于gets()的不安全性, 一般建议使用前者。

** 每次读写若干数据块的标准IO函数接口 **

fread fwrite

功能 从指定文件读取若干个数据块
头文件 <stdio.h>
原型 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数 ptr:自定义缓冲区指针
size:数据块大小
nmemb:数据块个数
stream:即将被读取数据的文件指针
返回值 成功:读取的数据块个数,等于 nmemb
失败:读取的数据块个数,小于 nmemb 或等于 0
备注 当返回小于 nmemb 时,文件 stream 可能已达末尾,或者遇到错误
功能 将若干块数据写入指定的文件
头文件 <stdio.h>
原型 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
参数 ptr:自定义缓冲区指针
size:数据块大小
nmemb:数据块个数
stream:即将被写入数据的文件指针
返回值 成功:写入的数据块个数,等于 nmemb
失败:写入的数据块个数,小于 nmemb 或等于 0
备注
  1. 如果fread()返回值小于nmemb时,则可能已达末尾,或者遇到错误,需要借于feof()/ferror()来加以进一步判断。
  2. 当发生上述第1种情况时,其返回值并不能真正反映其读取或者写入的数据块数, 而只是一个所谓的“截短值”,比如正常读取5个数据块,每个数据块100个字节,在执 行成功的情况下返回值是5,表示读到5个数据块总共500个字节,但是如果只读到499 个数据块,那么返回值就变成4,而如果读到99个字节,那么fread()会返回0。因此当 发生返回值小于nmemb时,需要仔细确定究竟读取了几个字节,而不能直接从返回值确定。

** 获取或设置文件当前位置偏移量 **

fseek ftell rewind

功能 设置指定文件的当前位置偏移量
头文件 <stdio.h>
原型 int fseek(FILE *stream, long offset, int whence);
参数 stream:需要设置位置偏移量的文件指针
offset:新位置偏移量相对基准点的偏移
whence:基准点
- SEEK_SET:文件开头处
- SEEK_CUR:当前位置
- SEEK_END:文件末尾处
返回值 成功:0
失败:-1
备注
功能 获取指定文件的当前位置偏移量
头文件 <stdio.h>
原型 long ftell(FILE *stream);
参数 stream:需要返回当前文件位置偏移量的文件指针
返回值 成功:当前文件位置偏移量
失败:-1
备注
功能 将指定文件的当前位置偏移量设置到文件开头处
头文件 <stdio.h>
原型 void rewind(FILE *stream);
参数 stream:需要设置位置偏移量的文件指针
返回值
备注 该函数的功能是将文件 stream 的位置偏移量置位到文件开头处

1,fseek()的用法基本上跟系统IO的lseek()是一致的。

2,rewind(fp)相等于fseek(fp,0L,SEEK_SE);

** 标准格式化IO函数 **

fprintf printf snprintf sprintf

功能 将格式化数据写入指定的文件或者内存
头文件 <stdio.h>
原型 int fprintf(FILE *restrict stream, const char *restrict format, ...);
int printf(const char *restrict format, ...);
int snprintf(char *restrict s, size_t n, const char *restrict format, ...);
int sprintf(char *restrict s, const char *restrict format, ...);
参数 stream:写入数据的文件指针
format:格式控制串
s:写入数据的自定义缓冲区
n:自定义缓冲区的大小
返回值 成功:成功写入的字节数
失败:-1
备注

fscanf scanf sscanf

功能 从指定的文件或者内存中读取格式化数据
头文件 <stdio.h>
原型 int fscanf(FILE *restrict stream, const char *restrict format, ...);
int scanf(const char *restrict format, ...);
int sscanf(const char *restrict s, const char *restrict format, ...);
参数 stream:读出数据的文件指针
format:格式控制串
s:读出数据的自定义缓冲区
返回值 成功:正确匹配且赋值的数据个数
失败:EOF
备注

1,fprintf()不仅可以像printf()一样向标准输出设备输出信息,也可以向由stream
指定的任何有相应权限的文件写入数据。
2,sprintf()snprintf()都是向一块自定义缓冲区写入数据,不同的是后者第二个参
数提供了这块缓冲区的大小,避免缓冲区溢出,因此应尽量使用后者,放弃使用前者。
3,fscanf()不仅可以像scanf()一样从标准输入设备读入信息,也可以从由stream
指定的任何有相应权限的文件读入数据。
4,sscanf()从一块由s指定的自定义缓冲区中读入数据。
5,最重要的一条:这些函数的读写都是带格式的,这些所谓的格式由下表规定:

格式控制符

格式控制符 含义 范例
%d 有符号十进制整型数 int a = 1; printf("%d", a);
%u 无符号十进制整型数 int a = 1; printf("%u", a);
%o 无符号八进制整型数 int a = 1; printf("%o", a);
%x 无符号十六进制整型数 int a = 1; printf("%x", a);
%c 字符 char a = 'd'; printf("%c", a);
%s 字符串 char *a = "xy"; printf("%s", a);
%f 计数法单精度浮点数 float a = 1.0; printf("%f", a);
%e 科学技术法单精度浮点数 float a = 1.0; printf("%e", a);
%p 指针 int *a; printf("%p", a);
%.5s 取字符串的前5个字符 char *a = "abcdefghijk"; printf("%.5s", a);
%.5f 取单精度浮点数小数点后5位小数 float a = 1.0; printf("%.5f", a);
%5d 位宽至少为5个字符,右对齐 int a = 1; printf("%5d", a);
%-5d 位宽至少为5个字符,左对齐 int a = 1; printf("%-5d", a);
%hd 半个有符号数十进制整型数 short a = 1; printf("%hd", a);
%hhd 半半个有符号数十进制整型数 char a = 1; printf("%hhd", a);
%lf 双精度浮点数 double a = 1.0; printf("%lf", a);
%Le 科学技术法双精度浮点数 double a = 1.0; printf("%Le", a);
%Lf 长双精度浮点数 long double a = 1.0; printf("%Lf", a);
%Le 科学技术法长双精度浮点数 long double a = 1.0; printf("%Le", a);

** 示例代码 **

  • 注意,这一组函数跟之前的标准IO最大的区别是带有格式控制,因此最适用于有格式 的文件处理,假设有一个文件存储了班级学生的姓名、性别、年龄和身高,如下:

image-20240515000814675

很明显这个文件是带有格式的,假如我们需要将这个文件读入程序,再将之打印到屏幕
显示出来,可以使用fscanf()fprintf()来实现:

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<unistd.h>
#include<string.h>
#include<strings.h>
#include<errno.h>
#include<sys/stat.h>
#include<sys/types.h>#define NAMELEN 20// 学生结构体
struct student {char name[NAMELEN];char sex;int age;float stature;struct student *next; // 用以形成链表
};// 初始化链表
struct student *init_list(void) {struct student *head = malloc(sizeof(struct student));head->next = NULL;return head;
}// 向链表中添加学生节点
void add_student(struct student *head, struct student *new) {struct student *tmp = head;while (tmp->next != NULL)tmp = tmp->next;tmp->next = new;
}// 显示链表中的所有学生信息
void show_student(struct student *head) {struct student *tmp = head->next;while (tmp != NULL) {fprintf(stdout, "%-5s %c %d %.1f\n",tmp->name, tmp->sex, tmp->age, tmp->stature);tmp = tmp->next;}
}int main(int argc, char **argv) {// 打开文件FILE *fp = fopen("format_data", "r");if (fp == NULL) {perror("Error opening file");return errno;}// 初始化链表struct student *head = init_list();int count = 0;// 从文件中读取数据并添加到链表中while (1) {struct student *new = malloc(sizeof(struct student));if (fscanf(fp, "%s %c %d %f", new->name, &(new->sex), &(new->age), &(new->stature)) == EOF) {break;}add_student(head, new);count++;}// 输出添加的学生数printf("%d students have been added.\n", count);// 显示所有学生信息show_student(head);// 关闭文件fclose(fp);return 0;
}

** 文件属性 **

--- 待补齐

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/31637.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

[sunj的科普系列 一] : 科技革命离我们又近一步

一、MESSAGES May 13, 2024,OpenAI重磅发布GPT-4o。 名字来源:omni是全能的意思。笔者对于AI领域了解一点点,单单看它的描述你就知道这次的升级多么爆炸了。 step towards much more natural human-computer interaction—it accepts as input any combination of text, aud…

PYTHON、R对小说文本挖掘正则表达式分析案例

原文链接:http://tecdat.cn/?p=5673 原文出处:拓端数据部落公众号《第二十二条军规》是美国作家约瑟夫海勒创作的长篇小说,该小说以第二次世界大战为背景,通过对驻扎在地中海一个名叫皮亚诺扎岛(此岛为作者所虚构)上的美国空军飞行大队所发生的一系列事件的描写,揭示了…

R语言线性判别分析(LDA),二次判别分析(QDA)和正则判别分析(RDA)

原文链接:http://tecdat.cn/?p=5689 原文出处:拓端数据部落公众号判别分析包括可用于分类和降维的方法。线性判别分析(LDA)特别受欢迎,因为它既是分类器又是降维技术。二次判别分析(QDA)是LDA的变体,允许数据的非线性分离。最后,正则化判别分析(RDA)是LDA和QDA之间…

【专题】2024中国医疗器械企业全球化发展报告合集PDF分享(附原数据表)

原文链接:https://tecdat.cn/?p=36180 原文出处:拓端数据部落公众号 中国医疗器械企业在国内市场面临同质化竞争和研发能力薄弱等挑战,而海外市场则展现出巨大的增长潜力和性价比优势。因此,全球化布局对于中国医疗器械企业至关重要。 该报告合集详细分析了这些市场的宏观…

最近几个SQL优化案例(水一波博客,当段子看)

某国产数据库原厂高级工程师找我优化SQL,以下是他给的三个案例。😼案例一: 慢SQL和执行计划:SELECT c.*FROM aaaaa aINNER JOIN bbbbbbbbbbb bON a.attend_rule_id = b.attend_rule_idINNER JOIN cccccccccc cON b.work_place_id = c.idINNER JOIN ddddddddddd dON a.atte…

敏捷冲刺-总结

敏捷冲刺-Day-08-阶段总结所属课程 软件工程2024作业要求 团队作业4—项目冲刺作业目标 完成 Scrum 冲刺总结冲刺日志集合贴 https://www.cnblogs.com/YXCS-cya/p/181788031.项目燃尽图 1.1 第八日-5月13日进度 项目收尾2.会议记录 2.1 会议主题 第 8 天 Scrum 冲刺-项目收尾 2…

TextMeshPro - 材质参数 - 描边,投影,外发光,内发光

有点类似photoshop中的图层样式,利用好也能制作出不错的艺术字效果。Face: 文字外观 color: 文字颜色softness: 羽化程度 dilate: 外扩(变粗)或内收(变细)Texture: 贴图填充在字形内Speed X: 贴图在x方向移动 Outline: 居中描边 color: 描边颜色 Thickness:描边粗细Texture: …

基于肤色模型的人脸识别FPGA实现,包含tb测试文件和MATLAB辅助验证

1.算法运行效果图预览 matlab2022a的测试结果如下:vivado2019.2的仿真结果如下:将数据导入到matlab中,系统的RTL结构图如下图所示:系统包括中值滤波,RGB转换为ycbcr,人脸检测三个模块2.算法运行软件版本 vivado2019.2matlab2022a3.算法理论概述肤色模型通常定义在特定的颜…