标准IO及相关函数介绍
一、标准IO 介绍
1、概念
标准I/O:是指标准I/O库,又称带缓存的I/O,属于ISO实现的输入输出的标准库函数,内部调用的是底层的系统调用,是在文件IO的基础上封装出来的函数接口,也就是说基于低级IO的一个封装和抽象
标准IO是基于低级IO进行设计的带缓存的IO机制。提供了一系列的库函数用于完成输入和输出相关操作。
2、标准IO 缓存机制
(1)概念
C语言的标准IO提供了缓冲机制,这是为了提高文件IO操作的效率。缓冲区是内存中的一块区域,用于临时存储输入或输出数据.
缓冲区一共有以下几种模式进行操作
-
全缓冲(Fully Buffered) 当流是全缓冲的,数据会被积累在缓冲区中,直到缓冲区满了或者你手动刷新缓冲区(比如使用 fflush 函数),才会进行实际的IO操作。这种缓冲方式常用于对文件的操作,因为它可以减少对磁盘的读写次数。
-
行缓冲(Line Buffered) 行缓冲意味着当输入或输出中遇到换行符时,缓冲区的内容才会被写出。标准输出stdout(向终端输出)通常是行缓冲的,这样可以在写出每行之后立即看到结果。
-
无缓冲(Unbuffered) 如果一个流是无缓冲的,那么每个输入或输出操作都是直接执行的。标准错误输出stderr通常是无缓冲的,以保证错误信息可以立即输出。5、文件流指针和结构体
(1)文件流指针
文件流指针是指指向某个文件结构体(FILE:c语言内置结构体)的指针。在C语言中,文件流是一个用于文件输入/输出的抽象数据类型。文件流指针通常使用FILE类型的指针来表示,通过使用文件流指针,我们可以对文件进行读写操作。
总结:文件流指针是一个文件在c语言程序里的表现形式。每一个文件都有自己的文件流指针。开发者就可以利用文件流指针以及对应的读取和写入函数来完成文件数据的读取和写入。
例如,可以使用fopen()函数打开一个文件并返回一个指向该文件的FILE类型的指针。然后,可以使用该指针以及函数来读取或写入文件里的数据,比如使用fscanf()函数从文件中读取数据或将使用fprintf将数据写入文件中。最后,可以使用fclose()函数关闭文件流并释放资源。
文件流指针指向一个包含文件名、文件状态和文件的当前信息等数据结构的结构体的地址。这个结构体通常包含一个缓冲区,用于存储从文件中读取的数据或写入文件的数据。文件流指针还包含一些控制标志位,例如读写状态标志位、错误标志位等。
在使用文件流指针时,我们可以打开一个文件并创建一个指向该文件的文件流指针。然后,可以使用文件流指针来读取或写入文件。当完成文件操作后,需要关闭文件流以释放资源。
我们通过文件流指针对应结构体来指向某一个文件,作用类似于低级IO里的文件描述符
(2)File类型
FILE结构体:该结构体中包含以下内容:
-
文件描述符:操作系统提供的底层文件标识符(低级io使用的fd)。
-
缓冲区指针:指向流的缓冲区,用于临时存储输入或输出的数据。
-
错误标识:标记是否在流的操作中发生了错误。
-
文件位置指示器:表示文件内当前操作的位置。,俗称文件位置光标
-
文件状态标志:存储文件的状态,例如读写模式。
-
缓冲区大小:指示缓冲区的大小。
-
结束-of-文件(EOF)标识:标记是否到达了文件末尾。一般是-1(end of file)
FILE结构体一般是在使用fopen函数时会生成并指向一个FILE类型的结构体。当你调用 fopen()
函数时,会返回一个指向 FILE
结构体的指针。这个结构体内部维护了文件的操作状态,包括当前读写的位置、是否遇到错误等。在使用例如 fread
、fwrite
、fprintf
、fgetc
等函数进行文件操作时,这些函数会通过 FILE
指针访问这些状态信息来执行相应的操作。
注意,由于FILE
结构是内部实现的一部分,你作为程序员通常不需要直接操作这个结构体,而是通过标准I/O函数利用 FILE
指针来进行文件操作
(3)IO流
IO流(Input/Output Stream)是一个表示输入源或输出目的地的抽象概念。可以把IO流想象成水流,数据就像是流水中的水一样,从一个地方流向另一个地方。
-
输入流:这就像是水管中的水源,水(数据)从水源(输入设备或文件)流入程序中。输入流用于从某个来源(可以是文件、终端或其他输入设备)读取数据。
-
输出流:这像是水流的出口,水(数据)从程序流到目的地(输出设备或文件)。输出流用于将数据写出到某个目的地(文件、终端或其他输出设备等)。
总结:IO流包含输入流和输出流,本身是一个抽象概念只是用于方便描述数据的流向,输入流指的是将设备或文件里的数据流向程序。输出流就相反。io流是抽象概念
在程序内部我们是通过文件流指针(指向的是FILE结构体:包含文件的相关信息)表示要操作的某个文件,作用类似于文件描述符。可以通过调用标准io函数以及给函数传入文件流指针来完成输入流或输出流的功能。
学习标准IO函数:利用文件流指针以及标准io函数实现数据的输入和输出(程序->文件和设备(终端),输出流:文件和设备->程序)
在编程语境中,使用IO流可以无需关心数据的来源和去向,提供了一种统一的方法来读取或写入数据,使得程序代码可以通用于不同的数据源和目的地。例如,无论是读取来自硬盘上的文件还是网络上的消息,都可以使用相同的代码结构,这是因为都被抽象成了流的形式。这样的抽象屏蔽了背后不同硬件和协议的复杂性,让程序员可以通过统一的接口与数据交互。也就是说不同的编程语言针对于IO流都有不同的实现,但对于输入和输出都有自己的实现。比如c语言中我们就可以通过文件指针以及fopen等io函数来使用io流。比如fopen函数就是打开一个文件并创建一个该文件对应的流,该函数返回的FILE指针其实就可以理解为该文件的一个io流,通过这个io流就可以使用其他的函数来对文件进行处理。
二、标准IO函数使用
标准 I/O 库的常用函数
功能分类 | 函数示例 | 说明 |
---|---|---|
文件操作 | fopen() , fclose() , freopen() | 打开、关闭、重定向文件流。 |
格式化 I/O | printf() , scanf() , fprintf() , fscanf() | 格式化输出 / 输入到标准流或文件。 |
字符 I/O | getchar() , putchar() , fgetc() , fputc() | 单个字符的读写。 |
字符串 I/O | gets() , puts() , fgets() , fputs() | 字符串的读写(注意gets() 已弃用,推荐fgets() )。 |
二进制 I/O | fread() , fwrite() | 块数据的读写(如结构体、数组)。 |
定位操作 | fseek() , ftell() , rewind() | 移动文件指针位置。 |
错误处理 | ferror() , feof() , clearerr() | 检查错误和文件结束状态。 |
1:标准IO函数大全
类别 | 名称 | 作用 |
---|---|---|
标准输入 | scanf | 从标准输入流读取格式化输入。 |
getchar | 从标准输入读取下一个字符。 | |
gets | 从标准输入读取一个字符串,直到遇到换行符或指定字符数。 | |
*fscanf | 当使用stdin作为参数时,从标准输入读取格式化输入。 | |
getc | 当使用stdin作为参数时,从标准输入读取下一个字符。 | |
fgetc | 当使用stdin作为参数时,从标准输入读取一个字符。 | |
标准输出 | printf | 向标准输出流写入格式化输出。 |
putchar | 向标准输出写入一个字符。 | |
*puts | 向标准输出写入一个字符串,并自动追加换行符。 | |
*fprintf | 当使用stdout作为参数时,向标准输出写入格式化输出。 | |
putc | 当使用stdout作为参数时,向标准输出写入一个字符。 | |
*sprintf | 将格式化的数据写入字符串中。 | |
fputc | 当使用stdout作为参数时,向标准输出写入一个字符。 | |
标准错误 | perror | 打印错误消息,并将上一个函数错误原因输出到标准错误流。 |
文件处理 | fopen | 打开文件,返回FILE 指针。 |
freopen | 重新打开文件,与已有的FILE 指针关联。 | |
*fclose | 关闭一个打开的文件流。 | |
读 | fread | 从文件流读取数据到数组中。 |
fwrite | 将数组中的数据写入文件流。 | |
*fscanf | 从文件流中读取格式化输入。 | |
*fprintf | 向文件流中写入格式化输出。 | |
getc | 从文件流中读取下一个字符。 | |
fgetc | 从文件流中读取一个字符。 | |
写 | putc | 向指定的文件流写入一个字符。 |
fputc | 向指定的文件流写入一个字符。 | |
*fgets | 从文件流中读取一行。 | |
*fputs | 向指定的文件流写入一个字符串。 | |
printf | 格式化数据并写入到标准输出。 | |
*sprintf | 格式化数据并写入到字符串中。 | |
snprintf | 格式化数据并写入到指定大小的缓冲区。 | |
文件缓存 | fflush | 刷新输出缓冲区。 |
setbuf | 设置文件流的缓冲区。 | |
setvbuf | 设置文件流的缓冲区策略。 | |
文件定位 | *ftell | 返回当前文件位置指示器的值。 |
*fseek | 设置文件位置指示器到指定位置。 | |
fgetpos | 获取当前文件位置指示器的值。 | |
fsetpos | 设置当前文件位置指示器的值。 |
1、fopen
(1)概念
用于尝试打开一个文件并返回该文件的FILE类型的指针,方便用其他函数进行文件处理
(2)语法
FILE * fopen(const char *filename, const char *mode);
-
pathname: 要打开的文件的路径及名称
-
mode: 打开文件的方式
具体解释 | open() flag | |
---|---|---|
r | 以只读的方式打开一个文件,文件必须存在 | O_RDONLY |
r+ | 以读写的方式打开这个文件,文件必须存在。写入操作在读取之后的位置执行 | O_RDWR |
w | 如果文件存在则清空再写,如果文件不存在则创建写入 | O_WRONLY|O_CREAT|O_TRUNC |
w+ | 如果文件存在则清空读写,如果文件不存在则创建读写 | O_RDWR|O_CREAT|O_TRUNC |
a | 如果文件存在则追加写入,如果文件不存在则先创建在写入 | O_WEONLY|O_CREAT|O_APPEND |
a+ | 如果文件不存在则创建文件并追加写入和读取。 读取时从文件开头开始读取,写入时从文件的末尾开始写入 lseek偏移后不能进行写入操作,如果进入写入操作,则追加在末尾写入(不管写之前进行了 | |
b | "b" 表示“二进制(binary)”:在打开文件时,加上这个字符标志,表示文件以二进制模式打开,不对文件内容进行任何新行转换。在处理文本文件时,通常不需要使用"b" ,但在处理二进制文件(比如图像、声音、编译好的程序等)时,必须使用"b" 来确保数据正确读写.比如rb 或wb |
-
返回值:
-
成功: 返回一个文件的流指针
-
失败: 返回NULL
-
(3)例子
char *filename = "example.txt"; FILE *fp = fopen(filename, "r"); // 打开名为"example.txt"的文件进行读取 if (fp == NULL) {// 出错处理 }
1. 基础模式(3 个核心)
决定文件的核心操作(读 / 写 / 追加),是所有模式的基础:
-
r
(read):只读。文件必须存在(否则打开失败)。 典型场景:读取配置文件、读取已存在的日志。 -
w
(write):只写。文件若存在则清空内容(截断),不存在则创建。 典型场景:覆盖写入新数据(如生成临时文件)。 -
a
(append):追加写。文件若存在则从末尾写入(不影响原有内容),不存在则创建。 典型场景:日志追加(如记录程序运行状态)。
2. 扩展标志(2 个补充)
在基础模式上叠加功能:
-
+
:允许双向操作(读 + 写)。 例如:r+
(可读可写,文件必须存在)、w+
(可读可写,但会先清空文件)、a+
(可读可写,但写操作始终在末尾)。 注意:a+
比较特殊,写入时强制追加到末尾(即使通过lseek
调整了位置),但读取时可以从任意位置开始。 -
b
:二进制模式。 处理二进制文件(如图像、音频、可执行文件)时必须加b
,避免系统自动转换换行符(如 Windows 会将\n
转为\r\n
)。 示例:rb
(二进制读)、wb
(二进制写)、ab
(二进制追加)。
三、关键注意点
-
r
模式:文件必须存在,否则fopen
返回NULL
(避免误删文件)。 -
w
模式:文件存在会清空内容(谨慎使用,可能导致数据丢失)。 -
a
模式:写操作始终在末尾(即使fseek
调整了位置),适合 “只追加” 场景。 -
+
模式:r+
/w+
/a+
允许读写,但w+
会先清空文件(小心覆盖),a+
写操作仍强制追加。
2、fclose
(1)概念
根据文件流指针进行刷新缓冲区以及关闭该文件的io流,释放对应资源。
(2)语法
int fclose(FILE *stream);
(3)例子
fclose(fp); // 关闭文件流 if (fclose(fp) != 0) {// 出错处理 }
-
成功:返回 0
-
失败:返回 EOF == -1
3、freopen
(1)概念 用于打开一个新的文件或重新打开当前文件
(2)语法
FILE *freopen(const char *filename, const char *mode, FILE *stream);
(3)参数
-
filename:想要打开的文件路径字符串地址。如果是打开当前文件,则传入NULL
-
mode:打开模式。如
w+
、r
等 -
stream:指向 一个旧的
FILE
结构的指针,表示要重新打开的文件流
(4)返回值
-
新的
FILE
结构的指针,当freopen执行成功后,原文件流指针将会失效,应使用新的指针来操作文件
示例代码:
#include <stdio.h> #include <stdbool.h> #include <string.h> // 注意要引入相关的文件 #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> int main() { // 1:-----------------------------------------// 只读方式打开FILE *fp = fopen("test.txt","r"); // 重新打开fp = freopen("test.txt","a+",fp);// 重新打开后,可以追加数据.char str[] = "晚上好";fwrite(str,sizeof(char),strlen(str),fp); //2:-----------------------------------------// 默认情况下,printf 输出到控制台 printf("这条消息将输出到控制台。\n"); // 使用 freopen() 将 stdout 重定向到文件 output.txt FILE *new_stdout = freopen("output.txt", "w", stdout); if (new_stdout == NULL) { perror("freopen 失败"); return 1; } // 现在的 printf 输出将写入到 output.txt 文件 printf("这条消息将输出到 output.txt 文件。\n"); // 为了确保所有数据都被写入文件,我们可以显式地刷新 stdout fflush(stdout); fclose(fp);return 0; }
3、fgetc
(1)概念
从指定文件流指针中获取一个字符
(2)语法
int fgetc(FILE *stream);
-
参数
-
文件流指针
-
-
返回值: 成功: 返回获取到的字符转化成的int类型的数据。char转int可以参考ASCII码 失败: 返回 EOF ,到达文件末尾 EOF。EOF表示到达文件末尾,可以直接用在判断逻辑语句中
-
注意
-
这个函数读取一个字符(无论是文本字符还是其他如换行符等特殊字符),并将文件位置指针向后移动一个字符。如果读取成功,返回的是读取到的字符的ASCII值;如果读取失败或者到达文件末尾,则返回EOF(通常是-1)
-
(3)例子
#include <stdio.h> #include <stdbool.h> #include <string.h> // 注意要引入相关的文件 #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> int main() { //从指定文件流指针中获取一个字符 // 只读方式打开FILE *fp = fopen("test.txt","r"); int counts = 0;int ch;//必须用int,不能用char,否则无法正确处理EOF(-1), char可能无法表示负数(取决于编译器是否支持有符号 char)。while( (ch =fgetc(fp)) != EOF ){//printf("%c",ch); // 输出一个字符putchar(ch); // 输出一个字符counts++;} fclose(fp); return 0; }
4、fgets
(1)概念
读取文件里的一行内容
(2)语法
char *fgets(char *s, int size, FILE *stream);
-
参数
-
s:读取到的数据存放的地址
-
size:预期读取的字节个数
-
当'\n'之前的字节个数小于size,遇到换行符结束读取,但是换行符也会被当做一个字符被读到内存之中,并且会在最后一个字符后添加'\0'.
-
实际:如果读取换行符时不足size,那么后续的字符就是null。
-
-
当'\n'之前的字节个数大于size,会读到size个大小的字节时停止读取。会在末尾加上一个'\0'。会将最后一个字节的字符给覆盖掉。机制会在读取完之后读写指针向前偏移一个字节。
-
例子:给的size为5,实际文件一行有10个。那么会实际读取4个数据,第5个为\0.下一次读取会继续从文件的第5个字节继续读
-
实际:如果读取的内容大于size,那么保存 字符串的最后一个会是\0,
-
-
stream:文件流指针
-
-
-
返回值
-
成功:返回读到的字符串的首地址
-
失败:返回NULL(读到文件末尾也返回NULL)
-
(3)例子
#include <stdio.h> #include <stdbool.h> #include <string.h> // 注意要引入相关的文件 #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> int main() { //从指定文件流指针中获取一个字符 // 只读方式打开FILE *fp = fopen("test.txt","r"); char str[64];while( fgets(str,60,fp) != NULL ){//printf("%s",str); // 输出字符串puts(str); // 输出字符串 (会自动在尾部加上换行)}printf("\n");fclose(fp);return 0; }
-
代码截图:
-
从键盘得到一行输入(含空格)
#include <stdio.h> #include <stdbool.h> #include <string.h> int main(int argc, char *argv[]) {// 从键盘读取到一行信息:char str[128] = {0};puts("请输入:");fgets(str, sizeof(str) -1 , stdin );printf("你输入的是:%s \n", str);puts("--------------------------------");// fgets会把换行读到缓冲区,可以用以下代码替换掉换行符char *newline = strchr(str, '\n');//strchr 是 C 标准库函数,用于在字符串中查找某个字符第一次出现的位置。//如果找到,返回指向该字符的指针(即该字符在字符串中的地址)。//如果没找到,返回 NULL。//只有当字符串中确实存在 \n 时,才执行替换操作。两种情况会导致找不到 \n://用户输入超长(超过 fgets 的缓冲区大小),此时 \n 会被截断,字符串末尾是 \0。//文件内容本身不含 \n(如二进制文件)。if (newline != NULL) {*newline = '\0';}//或者if (str[strlen(str) - 1] == '\n'){str[strlen(str) - 1] = '\0'; // 用 \0 覆盖 \n}printf("你输入的信息长度(不包含换行符)是:%zu\n", strlen(str));printf("你输入的信息是:%s\n", str);return 0; }
-
功能:从
stream
读取最多n-1
个字符,并在末尾添加\0
终止符。 -
关键点:
-
最多读取
n-1
个字符:为\0
预留位置。 -
遇到换行符
\n
时停止:并将\n
作为字符串的一部分(不同于scanf
)。 -
自动添加
\0
:无论是否读取到\n
,fgets
都会在读取的内容后添加\0
。
-
5、fputc
(1)概念
写入一个字符到文件中
(2)语法
int fputc(int char, FILE *stream);
-
参数
-
int char
: 要写入的字符。尽管参数名是char
,但传递的参数应该是一个int
类型的值,以便能够传递所有可能的字符和EOF
。 -
FILE *stream
: 目标文件指针,该指针应指向你想要写入字符的文件。
-
-
返回值
-
成功: 返回写入的字符。
-
失败: 返回
EOF
。如果发生错误,还可以设置错误指示器,可以用 `ferror
-
6、fputs
(1)概念
向一个文件中写入一个字符串
(2)语法
int fputs(const char *str, FILE *stream);
-
参数
-
str: 想要写入的数据的地址。如果需要写入后换行需要字符串里有
\n
-
stream: 想要写入哪个文件
-
-
返回值
-
成功: 返回一个非负数
-
失败: 返回EOF(-1)
-
(3)fputc 和 fputs的示例代码:
#include <stdio.h> #include <stdbool.h> #include <string.h> // 注意要引入相关的文件 #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> void testPutc(){//从指定文件流指针中输出一个字符 char c = 'A';FILE *fp = fopen("test.txt","a+"); int result = fputc(c, fp);if(result == EOF){printf("写入失败!");} fclose(fp); } void testPuts(){//从指定文件流指针中输出一字符串char str[] = "大家晚上好.要吃晚饭了!";FILE *fp = fopen("test.txt","a+"); int result = fputs(str,fp);if(result == EOF){printf("写入失败!");} fclose(fp); } int main() { //testPutc(); testPuts(); return 0; }
7、fscanf
(1)概念
用于在指定文件流中查找满足指定格式的数据,并将数据保存在对应的变量中。即能够从文件中提取需要的数据并保存到变量中
(2)语法( fgets + strtok )
int fscanf(FILE *stream, const char *format, ...);
-
参数
-
stream
:指向FILE
对象的指针,该FILE
对象代表一个输入流。 -
format
:一个 C 字符串,包含了一个或多个以下项:普通字符 (除了%
), 和格式化指令。格式化指令识别输入项,并按照给定的形式将其转换和存储到提供的参数中。 -
...
:额外的参数,每个参数都是一个指针,指向用来存储转换后的值的变量。
-
-
返回值
-
成功读取的项数:返回成功匹配并赋值的输入项数。
-
文件结束(EOF):如果在读取任何数据之前遇到文件结尾,返回
EOF
。 -
错误:如果读取过程中发生错误,也返回
EOF
。
-
-
常用示例: #include <stdio.h> #include <stdbool.h> #include <string.h> // 注意要引入相关的文件 #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> int main() { FILE *fp = fopen("User.txt","r"); char name[32]; char pass[32]; // 读取单条数据 //int result = fscanf(fp,"%s %s\n",name,pass); //printf("姓名:%s , 密码:%s \n",name, pass); // 循环读出所有数据 int result; while((result = fscanf(fp,"%s %s\n",name,pass)) != EOF){ printf("姓名:%s , 密码:%s \n",name, pass); } return 0; }
-
特殊示例:
-
假如文本里的信息如下:
wangze-123-123-男 lili-456-18-女 zhangfei-789-99-女 打印出用户名:wangze,密码:123,年龄:123,性别:男 ...
-
核心思路:
-
用
%[]
读取用户名:%[^-]
表示读取除-
外的所有字符(即用户名)。 -
跳过
-
分隔符:直接在格式字符串中写-
。 -
读取密码、年龄、性别:分别用
%d
、%d
、%c
匹配。 -
在 fscanf 的格式字符串中,非格式说明符(如 - 和 \n)的作用是 “匹配并消耗输入中的对应字符”。
-
-
:表示 “匹配并消耗输入中的-
字符”。 -
\n
:在fscanf
中表示 “匹配并消耗任意数量的空白字符(包括换行符、空格、制表符)”。
-
#include <stdio.h> #include <stdbool.h> #include <string.h> #include <stdlib.h>#include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> int main(int argc, char *argv[]) {FILE * fp1 = fopen("fscanf1.txt","a+");FILE * fp2 = fopen("fscanf2.txt","a+");char name[32];char password[32];char sex[12];int age;//读wangze-123-22-男while(fscanf(fp1,"%[^-]-%[^-]-%d-%[^\n]\n",name,password,&age,sex)!= -1){printf("姓名:%s, 密码:%s, 年龄:%d, 性别:%s\n",name,password,age,sex);}printf("---------------------\n");//读wangze-123-男-22while(fscanf(fp2,"%[^-]-%[^-]-%[^-]-%d\n",name,password,sex,&age) != -1){printf("姓名:%s, 密码:%s, 性别:%s, 年龄:%d\n",name,password,sex,age);}fclose(fp1);fclose(fp2);return 0; } //在 fscanf 的格式字符串中,非格式说明符(如 - 和 \n)的作用是 “匹配并消耗输入中的对应字符”。 //在 fscanf 的格式字符串中,空格字符(包括 ' '、\n、\t)的行为是等价的,都会匹配并消耗任意数量的空白字符。
-
8、fread
(1)概念
以字节方式读取文件数据
(2)语法
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
-
参数说明:
-
void *ptr
-
类型:指向某内存区域的指针,类型为
void*
。 -
作用:
ptr
指向的内存区域用于存储从文件中读取的数据块。在调用fread
之前,必须确保该内存区域足够大,以容纳要读取的数据。
-
-
size_t size
-
类型:
size_t
是一个无符号整型类型。 -
作用:
size
指定了要读取的每个数据项的大小(以字节为单位)。例如,如果要读取整数数组,size
应该是sizeof(int)
。
-
-
size_t count
-
类型:同样为
size_t
。 -
作用:
count
指定了要读取的数据项的个数。例如,如果要读取 10 个整数,count
应该是 10。
-
-
FILE *stream
-
类型:
FILE*
是一个指向FILE
对象的指针,该对象表示一个文件流。 -
作用:
stream
是fread
函数要读取的流,它应该是之前通过fopen
函数打开的文件流的返回值。如果stream
是NULL
,则fread
的行为是未定义的。
-
-
-
返回值
-
返回实际读取的数据项个数,如果这个数小于
count
,可能是发生了错误或者达到了文件末尾。 -
示例代码:
// 读取 ------ void testRead(){FILE *fp = fopen("User.txt","r");if(fp == NULL){printf("文件读取失败!");return ;} char buffer[32]= {0}; int result ;while((result = fread(buffer,sizeof(char), 31, fp)) > 0){printf("%s",buffer); // 每次读取后重置缓冲区,防止上次读取的残留数据影响下次输出。memset(buffer,0 , sizeof(buffer) / sizeof(buffer[0]));} fclose(fp); }
9、fwrite
(1)概念
用字节方式写入数据到文件
(2)语法
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
-
参数
-
ptr
:指向一块内存的指针,里面包含了要写入的数据。 -
size
:每个数据项的大小,以字节为单位。如果写出的是字符:就是 sizeof(char) -
count
:要写入的数据项个数。 -
stream
:指向FILE
对象的指针,代表一个打开的文件。
-
-
返回值
-
返回实际写入的数据项个数,如果这个数小于
count
,可能是发生了错误或者达到了文件末尾。
-
(3)例子
// 写出 ------ void testWrite(){FILE *fp = fopen("User.txt","a+");if(fp == NULL){printf("文件读取失败!");return ;} char buffer[]= "李四 abcde";size_t len = strlen(buffer); int result = fwrite(buffer, sizeof(char), strlen(buffer), fp); if( result == len){printf("写入数据成功\n");} }
8、fprintf
(1)概念
用于将按照指定格式书写的字符串写入到文件中,printf
是 fprintf
的语法糖,专门用于终端输出。当需要灵活控制输出目标(如文件、网络套接字)时,fprintf
是更通用的选择。两者格式化规则完全一致,可根据场景互换。
(2)语法
int fprintf(FILE *stream, const char *format, ...);
-
参数
-
stream
:指向FILE
对象的指针,该FILE
对象代表一个输出流,用于接收格式化的输出内容。 -
const char *format:
-
这是一个字符串,包含了要被写入到流
stream
中的文本。 -
它可以包含嵌入的格式标签(例如
%d
、%f
、%s
等),这些标签会被随后提供的参数值替换。
-
-
...
:根据格式字符串中的格式说明符,依次提供相应的附加参数,它们的数目和类型必须与格式字符串中的格式说明符一一对应。
-
-
返回值
-
成功写入的字符数:返回成功写入到流中的字符总数。
-
错误:如果发生输出错误,返回一个负数。
-
-
格式符号说明:
-
format
字符串中的格式标签用于指定如何格式化输出数据。以下是一些常见的格式标签说明符:-
%d
或%i
:十进制有符号整数。 -
%u
:十进制无符号整数。 -
%f
:浮点数。 -
%s
:字符串。 -
%c
:单个字符。 -
%p
:指针的值(通常以十六进制表示)。 -
%e
或%E
:指数形式的浮点数(例如1.23e+02
或1.23E+02
)。 -
%x
或%X
:无符号以小写或大写十六进制表示的整数。 -
%o
:无符号以八进制表示的整数。 -
%g
或%G
:自动选择合适的表示法(%e
/%E
或%f
)来打印浮点数。 -
%%
:输出一个百分号字符(%
)。
-
-
-
示例代码:
void testPrintf(){FILE *fp = fopen("User.txt","a+");if(fp == NULL){printf("文件读取失败!");return ;}int age = 25; float height = 1.8; char name[] = "Alice"; // 使用 fprintf() 将格式化的数据写入文件 fprintf(fp, "姓名: %s\n", name); fprintf(fp, "年龄: %d\n", age); fprintf(fp, "身高: %.1f 米\n", height); fprintf(fp, "信息:姓名%s , 年龄:%d , 身高:%.1f米\n", name, age, height); // 关闭文件 fclose(fp); printf("写入完成.\n"); }
10、fseek
(1)概念
设置文件光标进行读写的位置
(2)语法
int fseek(FILE *stream, long offset, int whence);
-
参数
-
stream: 目标文件流指针
-
offset:
-
该数为负数,向前进行偏移,如果偏移出了文件的开头,会报错返回如果
-
该数为正数,向后进行偏移,如果偏移出了文件的末尾,会扩大文件,用'\0'来做填充。那么此类的文件被称为 空洞文件
-
注意:如果偏移后没有对其进行任何写入操作,内核认为该偏移无效,不会扩大文件大小。
-
-
whence: 基准位置-----根据哪一个位置进行偏移
-
0:即SEEK_SET 根据文件开头进行偏移
-
1:即SEEK_CUR: 根据用户当前位置进行偏移
-
2:即SEEK_END: 根据文件末尾进行偏移
-
-
-
返回值
-
成功: 返回 0
-
失败: 返回 -1
-
(3)例子 和 lseek() 的使用方式是一样的。
fseek(file, 0, SEEK_SET);
11、ftell
(1)概念
获取当前读写光标的偏移量
(2)语法
long ftell(FILE *stream);
-
参数:
-
stream 目标文件流指针
-
-
返回值:
-
成功: 返回偏移量(是根据文件开头来计算的)
-
失败: 返回-1
-
(3)例子
int main() { FILE *fp = fopen("test.txt","r");if(fp == NULL){printf("文件打开失败!");return 0;}// 文件位置移到最后 fseek(fp, 0, SEEK_END );// 得到最后的位置数据long position = ftell(fp);printf("文件的大小%ld \n", position );return 0; }
注意:如果要测试文件大小,要注意,有可能测试的时候测试数据会比实际大小要多1,因为有个换行符(linux系统自动添加)
12、fflush
(1)概念
它通常用于刷新输出缓冲区,确保所有缓存的输出数据被写入其对应的文件或终端中
(2)语法
#include <stdio.h> int fflush(FILE *stream);
-
返回值:成功返回0,失败为EOF,即-1
(3)例子
FILE *file = fopen("example.txt", "w"); if (file != NULL) {fputs("这是一个测试字符串。", file);// 刷新文件缓冲区,确保内容已经被写入文件fflush(file);fclose(file); }
13、rewind
(1)概念
-
设置文件读写位置为开头
(2)语法
void rewind(FILE *filestream);
-
参数:需要设置位置的文件流指针
-
返回值:void没有返回值。可以通过ftell获取位置来判断是否设置成功,或者使用ferror来读取该文件流指针状态码是否为非0也可以查看是否设置成功。
(3)代码
#include <stdio.h> int main() {FILE *fp = fopen("test.txt", "r");if (fp == NULL) {perror("文件打开失败");return 1;} // 将文件指针移到文件末尾if (fseek(fp, 0, SEEK_END) != 0) {perror("定位文件末尾失败");fclose(fp);return 1;} // 获取文件大小(当前位置即为文件大小)long position = ftell(fp);if (position == -1) {perror("获取文件位置失败");fclose(fp);return 1;}printf("文件的大小: %ld 字节\n", position); // 重置文件指针到文件开头rewind(fp);long p2 = ftell(fp);if (p2 != 0) {perror("重置文件位置失败");fclose(fp);return 1;}printf("重新设置后的文件位置: %ld\n", p2); // 关闭文件fclose(fp);return 0; }
13、puts
(1)概念
用于将字符串输出到标准输出(stdout),并在末尾自动添加换行符。
(2)语法
int puts(const char *s);
-
参数
-
s
: 指向要输出字符串的指针
-
-
返回值
-
成功:返回一个非负值。
-
错误:返回EOF。
-
14、putchar
(1)概念
用于将单个字符输出到标准输出(stdout)
(2)语法
int putchar(int char);
-
参数
-
char
: 要输出的字符。
-
-
返回值
-
成功:返回输出的字符。
-
-
错误:返回EOF
17、putc
(1)概念
用于将一个字符写入指定的输出流。
(2)语法
int putc(int char, FILE *stream);
参数
-
char
: 要写入的字符。 -
stream
: 要写入字符的输出文件流。
返回值
-
成功:返回写入的字符。
-
错误:返回EOF。
示例代码:
puts() , putchar(), putc()
#include <stdio.h> #include <stdbool.h> #include <string.h> // 注意要引入相关的文件 #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> // puts 将字符输出到标准输出流(stdout),并自动换行 void one(){char str[] = "大家好,这是一条信息."; puts(str); } // putchar 将单个字符输出到标准输出(stdout),不会自动换行 void two(){char ch = 'Y';putchar(ch); } // putc 将一个字符,输出到指定的输出流 void three(){ FILE *fp = fopen("test.txt", "a+"); if (fp == NULL) { perror("文件打开失败"); return; } // ----------- 写入英文字符, 中文会有问题 ----------------------- char ch = 'Y'; if (putc(ch, fp) == EOF) { perror("写入失败"); } } int main() { //one();//two();three(); return 0; }
15、getchar
(1)概念
用于从标准输入(stdin)的缓冲区里读取并返回一个字符。
(2)语法
int getchar(void);
-
返回值
-
成功:返回读取的字符,int类型数据。
-
错误或文件结束:返回EOF。
-
16、getc
(1)概念
用于从指定的输入流读取并返回下一个字符
(2)语法
int getc(FILE *stream);
参数
-
stream
: 要读取字符的输入文件流。
返回值
-
成功:返回读取的字符。
-
错误或文件结束:返回EOF。
跟fgetc的对比:推荐使用fgetc,getc不同的编译器可能是一个宏,也可能是一个函数,不稳定。
-
getchar和getc使用示例:
// getchar 从标准输入(stdin)的缓冲区里读取并返回一个字符 void one(){puts("请输入一个字符:"); char ch = getchar(); printf("得到的字符是:%c\n",ch); } // getc 从指定的输入流读取并返回下一个字符 void two(){FILE *fp = fopen("test.txt","r");if(fp == NULL){puts("打开文件失败");}char temp; while((temp = getc(fp))!= EOF){printf("%c",temp);}fclose(fp); }
18、sprintf/snprintf
(1)概念
用于将格式化数据写入字符串。snprintf会额外的增加对输入字符串的长度限制,其他跟sprintf一样
(2)语法
int sprintf(char *str, const char *format, ...); int snprintf(char *str, size_t size, const char *format, ...);
参数
-
str
: 目标字符串的数组。 -
format
: 格式化字符串。 -
...
: 变长参数列表,对应于格式化字符串。 -
size
: 要写入的最大字符数(包括终止空字符)。
返回值
-
成功:返回写入字符串的字符总数(不包括终止空字符)。如果是snprintf,那么如果这个值大于
size
,实际上不会将这么多的字符写入str
-
错误:如果编码错误则返回负数。
注意:
-
这两个函数都会自动填充'\0',所以建议在使用时尽量比预期的字节数+1,用于保存结束字符
示例:
int main() { char name[] = "jack";char pass[] = "1234";int age = 22;char gender[] = "male"; //char buffer[10];char buffer[100];// 将多个数据,拼接成字符串,并保存到buffer中// 返回值是成功写入的字符数,不包含终止空字符//int result = sprintf(buffer,"%s %s %d %s",name,pass,age,gender); // snprintf 多了一个size,用来限制写入的字符长度int result = snprintf(buffer, sizeof(buffer)/sizeof(buffer[0]), "%s %s %d %s",name,pass, age,gender); printf("写入的字符总数:%d\n",result); // 是17,因为中间有三个空格 printf("%s\n",buffer); return 0; }
2.1 IO函数的一般使用流程
-
1.使用fopen打开要操作的一个文件(得到其文件流指针)
-
2.使用文件流指针以及配套的函数来实现文件内容的读取或写入
-
3.操作结束后调用fclose关闭文件(通过文件流指针)
三、常用标准IO函数总结
类别 | 函数名 | 说明 |
---|---|---|
文件读 | fgets | 一行一行的读 |
fread | 一般读取整个文件,适合二进制文件,比如音频、视频,文档等 | |
fgetc | 一个一个字符的读 | |
fscanf | 按照指定格式读取数据, 相当于是 fgets() 和 strtok() 函数的给合体。 | |
文件写 | fputs | 一行一行的写 |
fwrite | 一次性写人到整个文件:适合二进制文件,比如音频、视频,文档等 | |
fputc | 一个一个字符的写 | |
fprintf | 按照指定格式将数据写入到文件中 | |
标准输入 | scanf | 输入指定类型的数据 |
getc | 从标准输入获取一个字符 | |
getchar | 从标准输入获取一个字符 | |
标准输出 | puts | 输出一个字符串 |
putchar | 输出一个字符 | |
putc | 输出一个字符 | |
printf | 输出指定格式的数据 |