文件管理相关函数(c语言)

文件管理

fopen/fopen_s/fclose开关

fopen()

文件读写有开有关,只要输入了fopen(),先配套一个fclose(),以防忘记。

FILE* fopen(char* filename, char* mode);

第一个参数是文件名(可以包含路径),第二个参数是模式字符串。

fp = fopen("hello.txt", "r");//打开文件hello.txt,将返回的FILE指针赋值给fp
if (fp == NULL) {
  printf("文件打开失败!\n");
  exit(EXIT_FAILURE);
}

成功打开文件以后, fopen()返回一个 FILE 指针,其他函数可以用这个指针操作文件。

fopen() 的模式字符串,默认是以文本流读写。

如果添加b后缀(表示 binary),就会以“二进制流”进行读写。

模式字符串还有x后缀,表示独占模式(exclusive)。如果文件已经存在,则打开文件失败;如果文件不存在,则新建文件,打开后不再允许其他程序或线程访问当前文件。比如,wx表示以独占模式写入文件,如果文件已经存在,就会打开失败。

在这里插入图片描述

fopen_s()

到了VS studio2019版本后显示如下:
错误 C4996 ‘fopen’: This function or variable may be unsafe. Consider using fopen_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

这里fopen()被弃用,改为使用fopen_s();

errno_t fopen_s(FILE** pFile,const char *filename,const char *mode );

二者的区别:

fp = fopen("文件路径","模式"); 
fopen_s(&fp,"文件路径","模式");

fclose()

fclose()用来关闭已经使用fopen()打开的文件。

int fclose(FILE* stream);

它接受一个文件指针fp作为参数。如果成功关闭文件,fclose()函数返回整数0;如果操作失败(比如磁盘已满,或者出现 I/O 错误),则返回EOF(这个值一般是-1)。

读取写入文件

fputc/putc/fputs/fprintf写入

int fprintf(FILE* fp, const char* format, ...);
int fputc(int char, FILE *fp);
int putc(int char, FILE *fp);
int fputs(const char* Str, FILE *fp);

FILE* fp;
char s[10],c;
fp = fopen("test.txt","w+");
fprintf(fp, "%s%c", s, c);
fprintf(fp, "love you\n");
fputs(s, fp);
fputs("love you,too\n", fp);
fputc(c,fp);
putc(c,fp);

一般来说,fprintf()的通用性高于其他几种函数,涵盖fputc/fputs的功能。

但在输出上,它们所耗费的时间是:fprintf>fputs>fputc>putc

fputc()与putc()的用法一样,它们的区别是,putc()通常是使用宏来实现,而fputc()只作为函数来实现,所以理论上putc()的性能会好一点。

fputs()函数用于向文件写入字符串,和puts()函数只有一点不同,那就是它不会在字符串末尾添加换行符。这是因为fgets()保留了换行符,所以fputs()就不添加了。fputs()函数通常与fgets()配对使用。

fgetc/fgets/fscanf/fscanf_s读取

int fgetc( FILE * fp );
char *fgets( char *buf, int n, FILE *fp );
int fscanf( FILE *fp, const char *format, ...);

FILE *fp;
fp = fopen("test.txt","w+");
char buff[255];
int a;
buff[0]=fgetc(fp);
fgets(buff, 255, fp);
fscanf(fp, "%2d", &a);//读取一个二位数字,并赋值给a。
fscanf_s(fp,"%s",buff,255);//fscanf_s()在读取字符串时候需要比fscanf多传入一个参数,表示字符数组的长度。
fgetc()

fgetc()从 fp 所指向的输入文件中读取一个字符。返回值是读取的字符,如果发生错误则返回 EOF

fgets()

fgets()从fp中读取一个字符串。

函数 fgets() 从 fp 所指向的输入流中读取 n - 1 个字符。它会把读取的字符串复制到缓冲区 buff,并在最后追加一个 null 字符来终止字符串。

如果fgets()在读取最后一个字符之前就遇到换行符 '\n'或文件的末尾 EOF,则只会返回读取到的字符,包括换行符

fscanf()

fscanf()函数从文件中读取字符串,在遇到第一个空格和换行符时,它会停止读取。

fsacnf_s()

到了VS studio2019版本后显示如下:
错误 C4996 ‘fscanf’: This function or variable may be unsafe. Consider using fscanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.

改用fscanf_s

只在输入字符串的时候要加上字符数组的长度

fwrite/fread

fwrite()

fwrite()以二进制形式向文件写入一个数据块(不局限于文本文件)。

size_t fwrite(
    const void* buffer, //数组指针,不是数组的记得加&
    size_t size,//单位字节,每个数组成员占用的字节
    size_t count,//成员数量
    FILE* fp);
);
char buffer[10];
FILE *fp=fopen("test.txt","rb+");
fwrite(
    buffer,
    sizeof(buffer[0]),
    sizeof(buffer)/sizeof(buffer[0]),
    fp
);

fwrite()函数的返回值是_成功写入的数组成员的数量_(注意不是字节数)。
正常情况下,该返回值就是第三个参数count,但如果出现写入错误,只写入了一部分成员,返回值会比count小。

fwrite(buffer, 
       sizeof(buffer[0]), 
       sizeof(buffer)/sizeof(buffer[0]),
       fp);
//可以改写为:
fwrite(buffer, sizeof(buffer), 1, fp);

任何类型的数据都可以看成是1字节数据组成的数组,或者是一个成员的数组,所以fwrite()实际上可以写入任何类型的数据,而不仅仅是数组。

fread()

fread()函数用于一次性从文件读取较大的数据块,主要用途是将文件内容读入一个数组,适合读取二进制数据。

 size_t fread(  
     const void* buffer, 
    size_t size,
    size_t count
     FILE* fp
 );//与fwrite相同
FILE* fp
fp = fopen("test.txt","rb+");
double a[10];
fread(a, sizeof(double), 10, fp);

fread()会从文件(第四个参数)里面读取数组长度(第二个参数)和每个成员的大小(第三个参数)的乘积大小的内容,然后将buffer(第一个参数)指向这些内容的内存地址。

fread()函数的返回值是成功读取的数组成员的数量。
正常情况下,该返回值就是第三个参数count,但如果出现读取错误或读到文件结尾,该返回值就会比count小。所以,检查**fread()**的返回值是非常重要的。

二者的区别

fscanf()和fpintf()是一对,fread()和fwrite()是一对,用fwrite()写的必须用fread()来读。

在程序终止之前,使用fwrite()将数据保存进文件,下次运行时再用fread()将数据还原进入内存

同样的数据,使用fprintf()和fwrite()写下去的是不一样的,但使用其相应的读命令读上来的数据会是一样的

fprintf与fwrite的区别:

  1. fprintf(fp, "%d", buffer); 是将格式化的数据写入文件

fwrite(&buffer, sizeof(int),1, fp);是以二进位方式写入文件

  1. fprintf()写文本文件,可以用记事本打开查看;fwrite()写二进制文件

如果要让生成的文件自己可以看得懂,那么就选择fprintf(),如果想看不懂,那就用fwrite(),两者只是写入方式不同,生成的文件倒都是计算机可读的

feof/fseek/ftell/rewind文件读写位置定位

int feof(FILE *fp);
void rewind(file* stream);
int fseek(FILE* stream, long int offset, int whence);
long int ftell(FILE* stream);

int num;
char name[50];
FILE* fp = fopen("clients.txt", "r");
while (!feof(fp)) {//判断feof()是否读到文件结尾,否,feof返回0
  fscanf(fp, "%d%s\n", &num, name);
  printf("%d %s\n", num, name);
}
fclose(fp);


// 定位到文件开始处
rewind(fp);
fseek(fp, 0L, SEEK_SET);//第二个参数为 long 类型,所以移动距离必须加上后缀L,将其转为 long 类型。

//返回文件内部指示器的当前位置
long file_pos = ftell(fp);

// 定位到文件第10个字节
fseek(fp, 10L, SEEK_SET);
// 定位到文件末尾
fseek(fp, 0L, SEEK_END);
// 定位到文件倒数第10个字节
fseek(fp, -10L, SEEK_END);
// 从当前位置后移2个字节
fseek(fp, 2L, SEEK_CUR);

feof()

feof()函数用于判断文件的内部指针是否指向文件结尾。如果已经到达文件结尾,会返回一个非零值(表 true),否则返回0(表 false)。

feof()为真时,可以通过fseek()、rewind()、fsetpos()函数改变文件内部读写位置的指示器。

每个文件指针都有一个内部指示器(内部指针),类似于电脑的光标,记录当前打开的文件的读写位置。

文件操作函数(比如getc()、fgets()、fscanf()fread()等)都从这个指示器指定的位置开始按顺序读写文件。

rewind()

rewind()函数可以让文件的内部指示器回到文件开始。

rewind(fp)基本等价于fseek(fp, 0l, seek_set),唯一的区别是rewind()没有返回值,而且会清除当前文件的错误指示器。

fseek()

fseek()可以让指示器移到文件的指定位置,它接受3个参数。

  • stream:文件指针。
  • offset:距离基准(第三个参数)的字节数。类型为 long int,可以为正值(向文件末尾移动)、负值(向文件开始处移动)或 0(保持不动)。
  • whence:位置基准,用来确定计算起点。它的值是以下三个宏(定义在stdio.h):SEEK_SET(文件开始处)、SEEK_CUR(内部指针的当前位置)、SEEK_END(文件末尾)

注意,fseek()最好只用来操作二进制文件,不要用来读取文本文件。因为文本文件的字符有不同的编码,某个位置的准确字节位置不容易确定。

正常情况下,fseek()的返回值为0。如果发生错误(如移动的距离超出文件的范围),返回值为非零值(比如-1)。

ftell()

ftell()函数返回文件内部指示器的当前位置。

ftell()可以跟fseek()配合使用,先记录内部指针的位置,一系列操作过后,再用fseek()返回原来的位置。

long file_pos = ftell(fp);
// 一系列文件操作之后fseek(fp, file_pos, SEEK_SET);

下面的例子先将指示器定位到文件结尾,然后得到文件开始处到结尾的字节数。

fseek(fp, 0L, SEEK_END);
size = ftell(fp);

fgetpos/fsetpos文件定位

fseek()和ftell()有一个潜在的问题,它们都把文件大小限制在 long int 类型能表示的范围内。在32位的计算机上,long int 的长度为4个字节,能够表示的范围最大只有4GB。而不少文件也往往会超出这个范围。鉴于此,C 语言新增了两个处理大文件的新定位函数:fgetpos()fsetpos()

int fgetpos(FILE* stream, fpos_t* pos);
int fsetpos(FILE* stream, const fpos_t* pos);

fpos_t file_pos;
//将文件内部指示器的当前位置返回指针变量file_pos
fgetpos(fp, &file_pos);

// 一系列文件操作之后
//将文件内部指示器的位置,移动到指针变量`file_pos`指定的地址
fsetpos(fp, &file_pos);

上面示例中,先用fgetpos()获取内部指针的位置,后面再用fsetpos()恢复指针的位置。执行成功时,fgetpos()和fsetpos()都会返回0,否则返回非零值。

fgetpos()函数会将文件内部指示器的当前位置,存储在指针变量file_pos。该函数接受两个参数,第一个是文件指针,第二个存储指示器位置的变量。

fsetpos()函数会将文件内部指示器的位置,移动到指针变量file_pos指定的地址。注意,变量file_pos必须是通过调用fgetpos()方法获得的。fsetpos()的两个参数与fgetpos()必须是一样的。

记录文件内部指示器位置的指针变量file_pos,类型为fpos_t*(file position type 的缩写,意为文件定位类型)。它不一定是整数,也可能是一个 Struct 结构。

ferror/clearerr错误

所有的文件操作函数如果执行失败,都会在文件指针里面记录错误状态。后面的操作只要读取错误指示器,就知道前面的操作出错了。

ferror()

int ferror(FILE *stream);

ferror()函数用来返回错误指示器的状态。可以通过这个函数,判断前面的文件操作是否成功。

它接受一个文件指针作为参数。如果前面的操作出现错误,ferror()就会返回一个非零整数(表示 true),否则返回0

clearerr()

clearerr()函数用来重置出错指示器。它接受一个文件指针作为参数,没有返回值。

void clearerr(FILE* fp);

FILE* fp = fopen("file.txt", "w");
char c = fgetc(fp);
if (ferror(fp)) {
    printf("读取文件:file.txt 时发生错误\n");
}
clearerr(fp);

上面示例中,fgetc()尝试读取一个以”写模式“打开的文件,读取失败就会返回 EOF
这时调用ferror()就可以知道上一步操作出错了。处理完以后,再用clearerr()清除出错状态。

文件操作函数如果正常执行,ferror()和feof()都会返回0。如果执行不正常,就要判断到底是哪里出了问题。

if (fscanf(fp, "%d", &n) != 1) {
if (ferror(fp)) {
printf("io error\n");
}
if (feof(fp)) {
printf("end of file\n");
}
clearerr(fp);
fclose(fp);}

当fscanf()函数报错时,通过检查ferror()和feof(),确定到底发生什么问题。这两个指示器改变状态后,会保持不变,所以要用clearerr()清除它们,clearerr()可以同时清除两个指示器。

remove

remove()函数用于删除指定文件。它接受文件名作为参数。如果删除成功,remove()返回0,否则返回非零值。

int remove(const char* filename);

if(!remove("1.txt")){
    printf("操作失败");
    exit(-1);
}

上面示例删除了1.txt文件。

注意,删除文件必须是在文件关闭的状态下。如果是用fopen()打开的文件,必须先用fclose()关闭后再删除。

rename

rename()函数用于文件改名,也用于移动文件。

它接受两个参数,第一个参数是现在的文件名,第二个参数是新的文件名。如果改名成功,rename()返回0,否则返回非零值。

int rename(const char* old_filename, const char* new_filename);

//将foo.txt改名为bar.txt
rename("foo.txt", "bar.txt");
//移动文件
rename("/tmp/evidence.txt", "/home/beej/nothing.txt");

注意,改名后的文件不能与现有文件同名。另外,如果要改名的文件已经打开了,必须先关闭,然后再改名,对打开的文件进行改名会失败

中文字符读取

GBK编码格式中文字符占2个字节,UTF-8编码格式中文字符占3个字节

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值