文件IO操作
文件操作
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
功能 | 文件控制 |
---|---|
头文件 | <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.打开LCD
int 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); //800
printf("lcd bpp=%-5d\n",lcd_vinfo.bits_per_pixel); //32
return 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 参数为 -1 ,offset 参数无效。- 映射区域的长度必须是页面大小的整数倍,通常使用 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 值 |
备注 | 不能对同一个文件重复关闭 |
-
使用标准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函数接口
fgets 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 |
fgets()
跟fgetc()
一样,当其返回NULL时并不能确定究竟是达到文件末尾还是 碰到错误,需要用feof()
/ferror()
来进一步判断。fgets()
每次读取至多不超过size个字节的一行,所谓“一行”即数据至多包含一 个换行符’\n’。gets()
是一个已经过时的接口,因为他没有指定自定义缓冲区s的大小,这样很容 易造成缓冲区溢出,导致程序段访问错误。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 |
|
备注 | 无 |
- 如果
fread()
返回值小于nmemb
时,则可能已达末尾,或者遇到错误,需要借于feof()
/ferror()
来加以进一步判断。- 当发生上述第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最大的区别是带有格式控制,因此最适用于有格式 的文件处理,假设有一个文件存储了班级学生的姓名、性别、年龄和身高,如下:
很明显这个文件是带有格式的,假如我们需要将这个文件读入程序,再将之打印到屏幕
显示出来,可以使用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;
}
** 文件属性 **
--- 待补齐
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)