C语言有很多文件操作函数,这里我们挑了一些重要的开始讲,首先说下这些函数都定义在stdio.h头文件中
目录
一.文件指针
二.文件处理函数
1.fopen(打开文件)
2.fclose(关闭文件)
3.getc和putc(从文件指针读取字符)
4.I/O工作原理
5.fprintf和fscanf函数
6.fgets()和fputs()
7.fseek()和ftell
8.fgetpos()和fsetpos()函数
9.ungetc
10.fflush
11.setvbuf()
12.fwrite和fread
13.fepf和ferror
一.文件指针
文件指针的类型是指向FILE的指针,FILE是一个定义在stdin.h的派生类型。文件指针并不是指向实际的文件,它指向一个包含文件信息的数据对象(fopen创建的输入缓冲区)
FILE * fp;
指向标准文件的指针
标准文件 | 文件指针 | 通常使用的设备 |
标准输入 | stdin | 键盘 |
标准输出 | stdout | 显示器 |
标准错误 | stderr | 显示器 |
这些文件指针都是指向FILE的指针,所以它们可用作标准I/O函数的参数,这些文件指针都可以在后面文件处理函数当中使用
二.文件处理函数
1.fopen(打开文件)
FILE* fopen(文件名称,文件模式)
文件名称如果只有一个文件名,则会在可执行文件文件夹位置查找。也可以使用绝对路径和相对路径
该函数用于打开一个文件。第一个参数是待打开文件的名称。第二个参数是一个字符串,指定待打印文件的模式
而模式分为很多种我们来看看
模式字符串 | 含义 |
"r" | 以读模式打开文件 |
"w" | 以写模式打开文件,把现有文件的长度截为0,如果文件不存在,则创建一个新文件 |
"a" | 以写模式打开文件,在现有文件末尾添加内容,如果文件不存在,则创建一个新文件,存在在现有文件末尾添加内容 |
"r+" | 以读写模式打开文件, |
"w+" | 以读写模式打开文件,把现有文件的长度截为0,如果文件不存在,则创建一个新文件 |
"a+" | 以读写模式打开文件,在现有文件末尾添加内容,如果文件不存在,则创建一个新文件,存在在现有文件末尾添加内容 |
"rb","wb","ab","rb+","r+b","wb+","w+b","ab+","a+b" | 与上一次模式类似,但是以二进制模式而不是文本模式打开文件 |
"wx","wbx","w+x","wb+x","w+bx" | (C11)类似非x模式,但是如果文件已存在或以独占模式打开文件,则打开文件失败 |
这里需要注意下,像UNIX和Linux这样只有一种文件类型的系统,带b字母的模式和不带b字母的模式相同
x是C11新增的,它的功能能让你以fopen()打开一个文件失败时,原文件的内容也不会被删除。还有x模式的独占特性使得其他程序或线程无法访问正在被打开的文件。
fopen()成功打开文件后,会返回一个文件指针
记住打开的时候记得把后缀名加上。此时如果我用w打开,文件内容就没了
fp此时指向的对象值是个垃圾值,因为文件数据都没有了,所以缓冲区内部的值还是原先的垃圾值
关于二进制模式的时候,会在后面讲解二进制文件操作函数的时候讲解的
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
FILE* fp;
char ch[40];
fp = fopen("test_1.txt", "w");
fgets(ch, 40, fp);
printf("文件内容: %s", ch);
fclose(fp);
return 0;
}
也可以使用绝对路径来操作其他位置的文件
记得\要变为\\,主要为了和转义字符的反斜杠区分开
2.fclose(关闭文件)
关闭指定的文件
fclose(fp)
如果fclose关闭文件成功返回0,否则返回EOF
这里记得结束一定要记得关文件,不然可以会影响其他程序使用文件。而一般磁盘满了或移动磁盘被移除或者I/O错误都会使fclose失败
3.getc和putc(从文件指针读取字符)
getc和putc函数与getchar和putchar函数类似。所不同的是,要告诉getc和putc函数要使用哪一个文件。
格式:
getc: 字符变量 = getc(文件指针)
putc: putc(字符变量,文件指针)
而getc,putc和getchar和putchar区别在于,后者规定要了文件指针
getc(stdin) == getchar()
Putc(ch,sdout) == putchar()
我们来看下实例
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
FILE* fp;
char ch;
fp = fopen("D:\\C语言练习\\test.txt", "r");
ch = getc(fp);
printf("文件内容: %c", ch);
fclose(fp);
return 0;
}
这里我们来看个有趣的例子
在讲解这个奇怪结果例子之前,我们先来学习一下
4.I/O工作原理
第一步:
fopen()函数打开一个文件,同时还创建了一个缓冲区(在读写模式下创建两个缓冲区)以及一个包含文件和缓冲区数据的结构。另外,fopen()返回一个指向该结构的指针,以便让其他函数知道如何找到该结构。这个结构包含一个指定流中当前位置的文件位置指示器,错误和文件结尾指示器,指向缓冲区开始的指针,文件标识符,计数(统计实际拷贝到缓冲区的字节数)
假设把该结构赋给一个指针变量fp,我们说fopen()函数打开一个流,如果该文本模式打开该文件,就获得一个文本流,如果以二进制模式打开文件,获得二进制流
第二步:
调用文件输入函数,文件中的缓冲大小数据块被拷贝到缓冲区可以使用setvbuf函数来创建缓冲区决定一次读取的字节)。最初调用函数,处理填充缓冲区外,还要设置指针所指向的结构中的值。当前的值从字节0开始。
在初始化结构和缓冲区后,输入函数按要求从缓冲区中读取数据。在它读取数据时,文件位置指示器被设置为指向刚读取字符的下一个字符。又因为stdio.h系列的所有输入函数都使用相同的缓冲区,所以调用任何一个函数都将从上一个函数结束调用的位置开始。
当输入函发现已读完缓冲区中的所有字符时,会请求把下一个缓冲大小的数据块从文件拷贝到该缓冲区中。直到读到结尾最后一个字符,把结尾指示器设为真,于是,下一次调用的输入函数将返回EOF。
输出也一样,也是初始化缓冲区,把值给到缓冲区,当缓冲区满了就会把值送到文件中
在这个基础上我们在来看程序
首先文件test_1原本的内容是helllo world。我们要实现将字符'a'添加到文件末尾,但我们非常意外的发现会出现最终终端打印的内容与我们设想的不一样。这主要因为。在C语言当中。是不允许同时进行读写操作的(现在可以现在容易出问题)。一个输入操作不能随后直接紧跟一个输出操作,同样的一个输出操作也不能直接跟一个输入操作。如果要这样做,必须插入一个fseek函数进行文件光标位置调整。想我们的例子插入后,文件光标已经到了文件结尾,这个时候在继续打印就会出现未定义的错误效果了。
这里我们添加了一个fseek(fp,0L,SEEK_SET)把文件光标定位到了开头,发现效果正确
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
FILE* fp;
char ch[40];
char c = 'a';
fp = fopen("test_1.txt", "r+");
fgets(ch, 40, fp);
printf("文件内容: %s\n", ch);
putc(c, fp);
fseek(fp, 0L, SEEK_SET);
fgets(ch, 40, fp);
printf("文件内容: %s\n", ch);
fclose(fp);
return 0;
}
5.fprintf和fscanf函数
文件I/O函数fprint()和fscanf()函数的工作方式和printf()和scanf()类似,区别在于前者需要用第一个参数指定待处理的文件
格式:
fprintf(文件指针,"字符串",占位符数值),pritnf是将内容直接输入到屏幕上,而fprintf将内容输入到指定文件中。
fprintf(stdout,"hello") == printf("hello")
fscanf(文件指针,"占位符",变量),scanf是通过键盘输入值,而fscanf通过文件输入值到变量中。
fscanf(stdint,"%d",&num) == scanf("%d",&num);
6.fgets()和fputs()
fgets(),用于将文件写入文件,第一个参数表示存储输入位置的地址,第二个参数是一个整数,第三个参数是一个文件指针,指定待读取的文件
fgets(buf,STLEN,fp)
buf是char类型数组的名称,STLEN是字符串的大小,fp是指向FILE的指针
fgets()函数读取输入知道第一个换行符的后面,或读到文件结尾,或者读取STLEN-1个字符。然后,fgets()在末尾添加一个空字符使之成为一个字符串。字符串的大小是其字符串加上一个空字符。如果fgets()在读到字符上限之前已读完一整行,它会把表示行结尾的换行符放在空字符前面。fgets()函数在遇到EOF时返回NULL值如果为遇到EOF则放回之前传给它的第一个参数地址。
fputs()函数接受两个参数,第一个是字符串的地址,第二个是文件指针。Fputs()在打印字符串时不会在其末尾添加换行符
fputs(buf,fp);
buf是字符串的地址,fp用于指定目标文件
7.fseek()和ftell
有了fseek()函数,便可把文件看作时数组,在fopen()打开的文件中直接移动到任意字节处。
Fseek()第一个参数是FILE指针,指向待查找的文件。第二个参数是偏移量,该函数表示从起始点开始要移动的距离。该参数必须是一个long类型的值,可以为正,负或0。第三个参数是模式,该参数确定起始点。下面我们俩看几个文件的起始点模式
模式 | 偏移量的起始点 |
SEEK_SET | 文件开始处 |
SEEK_CUR | 当前位置 |
SEEK_END | 文件末尾 |
Fseek(fp,0L,SEEK_SET); //定位至文件开始处
Fseek(fp,10L,SEEK_SET); //定位至文件中的第10个字节
Fseek(fp,2L,SEEK_CUR); //从文件当前位置前移2个字节
Fseek(fp,0L,SEEK_END); //定位到文件结尾
Fseek(fp,-10L,SEEK_END); //从文件结尾处回退10个字节
如果一切正常,fseek()的返回值为0,如果错误,则返回-1
ftell()函数的返回类型是Long,它返回的参数指向文件的当前位置据文件开始处的字节数。(文件的第一个字节到文件开始处的距离是0),对于MS-DOS,ftell()返回的值把\r\n当作一个字节激素
这个例子当中我们通过ftell获得了putc后文件光标的位置12,然后使用fseek函数移动光标后,在用ftell光标位置如我们设置的那样变到了位置0
演示代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
FILE* fp;
char ch[40];
char c = 'a';
fp = fopen("test_1.txt", "r+");
fgets(ch, 40, fp);
printf("文件内容: %s\n", ch);
putc(c, fp);
printf("文件当前光标位置: %ld\n",ftell(fp));
fseek(fp, 0L, SEEK_SET);
printf("文件当前光标位置: %ld\n",ftell(fp));
fgets(ch, 40, fp);
printf("文件内容: %s\n", ch);
fclose(fp);
return 0;
}
8.fgetpos()和fsetpos()函数
这两个参数在fseek和ftell的基础上增加了处理较大文件的功能。它们不使用long类型的值,用一种新类型fpos_t,fpos_t类型的变量和数据对象可以在文件中指定一个位置
Int fgetpos(FILE * restrict stream,fpos_t * restrict pos)
调用该函数时,它把fpos_t类型的值放在pos指定的位置上,该值描述了文件中的当前位置距文件开头的字节数。如果成功返回0,失败,返回非0
Int fsetpos(FILE * stream,const fpos_t *pos);
调用该函数时,使用pos指向位置上的fpos_t类型值来设置文件指针指向偏移该值后指定的位置。成功,返回0,失败,返回非0
9.ungetc
Int ungetc(int c,FILE *fp)
该函数把c指定的字符放回输入流中
10.fflush
Int fflush(FILE * fp);
该函数引起输出缓冲区中所有的未写入数据发送到fp指定的输出文件。这个过程我们也称为刷新缓冲区
11.setvbuf()
Int setvbuf(FILE * restrict fp,char * restrict buf,int mode,size_t size)
该函数创建一个供标准I/O函数替换使用的缓冲区
fp:指定待处理的流
buf:指定待使用的存储区,如果不是NULL,则必须创建一个缓冲区(为NULL,函数会为自动分配一个缓冲区)
mode:模式选择 _IOFBF 完成缓冲(在缓冲区满时刷新) ——IOLBF行缓冲(在缓冲区满时或写入一个换行符时)
_IONBF 无缓冲
size:大小
如果操作成功返回0,失败非零0
12.fwrite和fread
为了确保数值在存储前后一致,最精确的做法时使用与计算机相同的为组合来存储。如果以程序所用的表示法数据存储在文件中,则称以二进制形式存储数据。
实际上,所有的数据都是以二进制形式存储的,甚至连字符都以字符码的二进制表示来存储。如果文件中的所有数据都被解释成字符码,则称该文件包含文本数据。如果部分或所有的数据都被解释成二进制形式的数值数据,则称该文件包含二进制数据。
fwrite()原型
size_t fwrite(const void * restrict ptr,size_t size,size_t nmemb,FILE * restrict fp);
ptr:待写入数据块的地址
size:待写入数据块的大小(以字节为大小)
nmemb:待写入的数据块数量
fp:待写入的文件
fwrite(earnings,sizeof(double),10,fp);
把earnings数组中的数据写入文件,数据被分为10块,每块都是double的大小
fwrite()函数返回成功写入项的数量。
fread()原型
Size_t fread(void * restrict ptr,size_t size,size_t nmemb,FILE * restrict fp);
ptr:待读取文件数据在内存中的地址
fp:指定待读取的文件
fread(earnings,sizeof(double),10,fp);
调用10个double大小的值拷贝进earnings数组中
fread()函数返回成功读取项的数量
这里要知道,ANSI C把指向void得指针作为一种通用指针,用于指针指向不同类型得情况,void * restrict可以是指向一个double类型得数组指针,也可能是指向其他类型结构得指针
在这个例子中,我们将ch数组的字符串通过fwrite以12个字节为一次进行传递(传递一次就够了)到test_1.txt文本中。然后又通过fread读取了fp开始的12个字节到ch_t数组中,进行打印。结果最终正确。
演示代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
FILE* fp;
char ch[] = "hello world";
char ch_t[12];
fp = fopen("test_1.txt", "rb+");
fwrite(ch, strlen(ch) + 1, 1, fp);
rewind(fp);
fread(ch_t, 12, 1, fp);
printf("ch_t: %s", ch_t);
return 0;
}
13.fepf和ferror
Int feof(FILE * fp)
Int ferror(FILE *fp)
如果标准输入函数返回EOF,则通常表面函数已到达文件结尾。然而,出现读取错误时,函数也会返回EOF。
当上一次输入调用检测到文件结尾时,feof()函数会返回一个非零值,否则返回0
而当读或写出现错误,ferror()函数返回一个非零值,否则返回0
好了朋友们我们今天的内容到这就结束了,今天的内容到这里就结束了,如果有啥不会的朋友记得论坛里面提问哈~
如果朋友你感觉文章的内容对你有帮助,可以点赞,关注文章和专栏以及关注我哈,嘿嘿嘿我会定期更新文章的,谢谢朋友你的支持哈