目录
前言
以下函数均在头文件"stdio.h"中。
一、打开和关闭
1.fopen
fopen(),这个函数很简单,也不容易忘。两个参数一是文件名或者路径,二是打开方式。
如果文件就在当前路径下的话,那直接输入“文件名”即可,记得用双引号。如果不是当前路径,那就把目标的路径输进去。如下图:
(什么是当前路径?就是你创建源文件.c或者.cpp的那个文件夹)
打开当前路径下文件的方式:
打开其他路径下文件的方式:
这里列出常用的,注意打开方式也要用双引号括起来
细节
上述参数带不带加号有什么区别呢?带 +
的模式支持读写:
一句话总结
带 +
:允许读写,但不同模式对文件处理方式不同(r+
保留内容,w+
清空内容,a+
强制末尾追加)。
不带 +
:只能读或写,功能单一,部分模式会清空文件(如 w
)。
一打开文件后都要测试一些打开成功没,防止野指针操作
FILE * P;
//方法一
assert(P);
//方法二
if(!P)
{
printf("FILE ERROR");
return 1;
}
常用的如assert(P);这个函数的作用是检查括号内的值是否为真,否则就报错停止程序。但需要包上“assert.h”头文件。
当然if(!P)也是简单好用,如果打开文件失败,则P为NULL,!P就为真。
2.fclos
fclos( ),这个很简单,直接将指针放进去就好了,如fclos(P),一般是不会失败的
基本用法示例
#include<stdio.h>
#include<assert.h>
int main()
{
FILE* w = fopen("log.txt", "w");
assert(w);//检查w是否为空。如果为空则打印错误并停止运行
//你的过程
fclose(w);
return 0;
}
二、读写
1.fputc和fgetc
1)fputc
作用:向stream,目标文件流 写入单个字符。
参数有两个,一是int char,即要写入的字符,二是要写入文件的指针。
fputc写入成功时返回写入的字符的ASCII码,失败时返回EOF。
细节
由于fputc接受int型参数,故当把char类型传入时,其实发生了隐形的转换,也就是从char型转换为int型(该字符所对应的ASCII码)。
至于为什么将fgetc的参数设置为int型有以下几个原因:
①兼容与文件相关EOF,EOF为文件结束的标志,通常定义为-1;
②char
类型在不同平台上的符号性可能不同,使用 int
类型可以绕过符号性问题,确保所有 0
到 255
的字符值都能正确传递。
③整数提升规则:在 C 语言中,当 char
类型作为参数传递给函数时,会自动提升为 int
类型。
基本用法示例
void test1w()
{
FILE* w = fopen("log.txt", "w");
assert(w);
char* str = "hello ,this is a test\n";
char* tem = str;
for (int i = 5; i > 0; --i)
{
while (*str != '\0')
fputc(*str++, w);
str = tem;
}
//printf("%s\n", str);
fclose(w);
}
结果:
2)fgetc
作用:从文件、字符串或其他中读单字符。
函数参数就一个stream,目标文件流 文件指针。
读取成功返回该字符的ASCII码(int),失败则EOF,当读取到文件末尾返回EOF(-1),fgetc一般配合循环使用。
细节
①在从文件读取时判断结束的条件是EOF,但从字符串读取时,判断条件则为'\0'(字符串默认以'\0'结尾);
②注意缓冲区溢出问题,如下方示例中以char [200]数组sbuffer接受读取到的字符,接受的字符不能大于等于num(这里即199)——因为字符串默认以'\0'结尾,所以这里还要我们手动添加\0。
如果事先不知道文件中有多少字符,该如何设置缓冲区大小?
①可以用动态增容数组——malloc和realloc;
②可以循环打印,比如当缓冲区快满时,将缓冲区打印或者放到其他什么容器里吗,然后将下标置为0。
基本用法示例
void test1r()
{
FILE* r = fopen("log.txt", "r");
assert(r);
char sbuffer[200];
int t;
int i = 0;
while ((t=fgetc(r))!=EOF)
{
sbuffer[i++] = (char)t;
if (i == sizeof(sbuffer) - 1)break;
}
sbuffer[i] = '\0';
printf("%s", sbuffer);
fclose(r);
}
结果:
2.fputs和fgets
1)fputs
作用:写入字符串到文件或其他,按行写入。
参数:str,待写入的字符串(需以 \0
结尾)。stream,目标文件流 文件指针或stdout
。
返回值:成功返回非负整数,失败返回EOF。
细节
①fputs
不会自动添加换行符,如:
fputs("Line 1", fp); fputs("Line 2", fp);
文件写入的实际内容为Line 1Line 2。需要手动添加,如fputs("Line 1\n", fp)。
②fputs不会检查字符串是否以\0结尾,如果传入的字符串没有 \0
结尾,可能导致缓冲区溢出或写入乱码。需要手动添加\0,如char str[] = { 'h','h','h' ,'\0'};
基本用法示例
void test2w()
{
FILE* w = fopen("log.txt", "w");
assert(w);
char str[] = "hello\nworld\nC&C++\n";
printf("%d", fputs(str, w));
fclose(w);
}
结果:
2)fgets
作用:从文件或其他读取num-1个字符串到str缓冲区中。
参数:str,用于存储读取数据的字符数组(缓冲区);num,要读取的最大字符数;stream,输入流如 stdin
或文件指针。
返回值:成功返回str指针,失败返回NULL。
细节
1)fgets遇到以下情况会停止读取:
①读取了num-1个字符;
②遇到了换行符;
如果文件或者字符串中有很多\n应该怎么读完呢?——循环读取如下示例
③到达文件末尾 (EOF)。
2)读取的字符串会以 \0
结尾
3)为什么读取num-1个字符?因为要给\0留一个
如果事先不知道文件中有多少字符,该如何设置缓冲区大小?
①可以用动态增容数组——malloc和realloc;
②可以循环打印,比如当缓冲区快满时,将缓冲区打印或者放到其他什么容器里吗,然后将下标置为0。
基本用法示例
void test2r()
{
FILE* r = fopen("log.txt", "r");
assert(r);
char s[100];
char str[100];
int total = 0;
while (fgets(s, 100, r) != NULL && total + strlen(s) < 100)
{
strcpy(str+total, s);
total += strlen(s);
}
printf("%s", str);
fclose(r);
}
结果
3)puts的使用,以及为什么不推荐使用gets
作用:将字符串 str
输出到标准输出(通常是屏幕),并自动追加换行符 \n。
返回值:成功时返回非负值(通常是输出的字符数,包括换行符),失败返回 EOF
。
细节
传入的指针必须指向以 \0
结尾的字符串,否则会导致未定义行为(如输出乱码)。
基本用法示例
char str[] = "Hello World";
puts(str); // 输出 "Hello World" 并换行
不推荐使用gets的原因
缓冲区溢出风险。fgets相比,gets并没有指定读取多少字符,有缓冲区溢出的风险。
如:
char buffer[5];
gets(buffer); // 输入 "123456" 时,buffer 溢出(仅能容纳4字符+1个\0)
由上述原因,从C11标准开始,gets
被正式从C语言标准库中移除。若使用gets函数,编译器会给出警告,并推荐使用fges。
3.fscanf和fprintf
1)fscanf
首先明确的是fscanf与scanf非常相似,故使用方法上也会有相似之处。
作用:从文件读取格式化输入,即从文件中读取数据。
参数:stream,输入文件流(如 FILE *p
);format,格式化字符串(与 scanf
格式相同,类似于scanf的第一个参数);…表示format可以有多个。
返回值:成功,返回成功匹配和赋值的参数个数。失败或到文件尾返回EOF。
细节:
①fscanf与scanf类似都是从文件流中读取数值到某个容器中,故读取顺序不能更改否则类型不匹配;
②若用fscanf读取字符串,则需要留一位给\0,比如字符数组20,则只能读19——%19s;
fscanf和scanf的区别与联系
①fscanf可以从任意文件流中读取,而scanf只能从stdin标准输入流中读取;
②fscanf需要检查文件指针是否有效;
③scanf是fscanf的特例——fscanf(stdin,……);
基本用法示例
void test3r()
{
FILE* r = fopen("log.txt", "r");
int date, dat, ret;
char name[2][20];
ret=fscanf(r, "%19s %d %d %19s", &name[0], &date, &dat,&name[1]);
if (ret == 4)
printf("ret=%d, %d %d\n %s %s", ret, date, dat, name[0], name[1]);
else
printf("fscanf读取有误,匹配到:%d\n", ret);
fclose(r);
}
补充
一些方便的格式:
%[^ x]
:读取直到遇到x代表的符号,如%[ ^ ,]读取直到遇到' , ';
%*d
:跳过整数。
2)fprintf
作用:从文件写入格式化输出,即从文件中写入数据。
参数:stream,目标文件流(如文件指针或 stdout
/stderr
);format,format,格式化字符串(与 printf格式相同);…表示format可以有多个。
返回值:成功,返回写入的字符数。失败返回负数。
细节
①参数类型要匹配,即写入顺序不能更改;
②与fscanf不同的是,fprintf是朝文件写入,若只是写入字符串则可以不用加上后面的变量,如:
fprintf(p, "Hello World");//将字符串写入文件P;
③fprintf
可指定输出目标,printf
固定输出到 stdout。
基本用法示例
void test3w()
{
FILE* w = fopen("log.txt", "w");
assert(w);
char name[15] = "zhangsan";
int date1 = 1, date2 = 2, date3 = 3;
fprintf(w, "%s %d %d \n", name, date1, date2);
fprintf(w, "hello\nsee you");
fclose(w);
}
结果
4.sprintf和sscanf
1)sprintf
作用:sprintf
用于字符串格式化输入,将变量中的数据输入到字符串中,功能类似 printf
,但操作对象是字符串而非标准输入输出流。
参数:str,目标字符串(缓冲区);format,format,格式化字符串(与 printf格式相同);…表示format可以有多个。
返回值:成功写入的字符数,失败返回负数。
细节
①sprintf会自动为字符数组尾添加\0;
②sprintf 不会检查目标字符串 str
的长度,需手动确保足够空间。
③sprintf 支持printf的所有格式;
基本用法示例
void test4w()
{
char bufer[64];
double pi = 3.1415926;
int day = 20250510;
char str[] = "hello sprintf";
sprintf(bufer, "pi=%.7f,day=%d,str=%s", pi, day, str);
printf("%s", bufer);
}
结果
2)sscanf
作用:sprintf
用于字符串格式化输出,从字符串解析数据,功能类似scanf,但操作对象是字符串而非标准输入输出流。
参数:s,目标字符串(缓冲区);format,format,格式化字符串(与 printf格式相同);…表示format可以有多个。
返回值:成功匹配并赋值的参数个数,失败返回 EOF
。
细节
①可以用" * "跳过忽略选项,这个可以用来提取字符串中指定的数据
如:
char *str="total 5 $";
int num;
sscanf(str, "%*s %d", &num); // %*s 跳过 "total",然后读取 5
②用%[a-z]匹配小写字符串,用%[A-Z]匹配大写字符串;
如:
int ret=sscanf("ABC,abc,A", "%[A-Z],%[a-z],%c", a, b, &c);
printf("%s\n%s\n%c", a, b, c);
③%[^ 内容],意思是只匹配字符串中符合内容中的值
如:
sscanf(str, "%*[^0-9]%d", &num); // 跳过所有非数字字符,直到遇到数字
char str[20];
sscanf("hello,world", "%[^,]", str); // str="hello"
④%n
记录已读取的字符数
如:
int pos;
sscanf("12345", "%d%n", &num, &pos); // num=12345, pos=5
⑤使用宽度限定符防止溢出
如:
char name[10];
sscanf("abcdefghijklmn", "%9s", name); // 只读前9字符
基本用法示例
void test4r()
{
char str[] = "2025/5/10";
int year, mon, day;
char a[10], b[10], c;
int ret = sscanf(str, "%d/%d/%d", &year, &mon, &day);
printf("%d/%d/%d\n", year, mon, day);
sscanf("ABC,abc,A", "%[A-Z],%[a-z],%c", a, b, &c);
printf("%s\n%s\n%c", a, b, c);
}
输出:
总结
本文总结了有关文件操作的一些常用的函数。由于在日常较少使用这些函数,每每想用又不免查找,因此有了这篇文章。
希望对你有所帮助。
读完点赞,手留余香~