c文件编程
文件流
文件流有两种方式:一是文本流,此时数据写入为字符,这些字符组织为数据行,每一行以换行符结束,对于像int或double这类数据需要先转换成字符,才能写入文件中。二是二进制流,二进制流就是写入实际的值,例如double类型的值在文件中占8个字节。不管是文本流还是二进制流,在文件中都是一连串的自己组成,关键在于我们怎么去解释这些数据,例如字符'a',在文件中存储的是其ASCII码值97,在读取这个文件时,我们可以当作字符'a',也可以当作整数97,所以还是看以什么样的方式去解释这些数据。
一、文本流
1. fopen
使用fopen函数打开文件,函数原型在stdio.h中定义,函数原型如下:
FILE *fopen(const char *path, const char *mode);
其中mode为文件打开的模式,打开模式有:
r 只读
r+ 可读可写
w 只写,如果文件不存在则创建,如果存在则会清除文件原有数据。
w+ 可读可写,如果文件不存在则会创建,同r+不同的是,打开时会将清除文件原有数据。
a 在追加方式打开只写文件,如果文件不存在则创建,默认打开时位置指针指向文件末尾。
a+ 以追加方式打开可读写文件。
如果操作成功,返回指向该文件的FILE指针,否则返回NULL。
注意:以可读可写方式打开文件时,不能在写入数据后马上就去读取数据,因为此时文件读写指针位置已经发生了改变,要改变当前读写位置,调用rewind函数,函数原型如下:
void rewind(FILE *stream);
rewind函数将文件读写指针设置到文件头处,同样也可以使用fseek函数,rewind函数等同于fseek(fp, 0, SEEK_SET)。
还有一个理由是,写入的数据并没有立即写入文件中,而是放在缓冲区里面的,在切换到读模式时,数据有可能没有写入文件中而造成数据的丢失,可以使用fflush函数,将缓冲区中的数据写入到文件中。
与写模式切换到读模式类似,读模式切换到写模式时,文件的当前读写位置可能和想像的不一样,导致覆盖原来的数据,同样要先修改读写位置。
2. fclose
fclose函数为关闭文件,函数原型如下:
int fclose(FILE *fp);
成功返回0,否则返回EOF。
EOF是一个特殊字符,称为文件结束字符,在stdio.h中定义。
3. rename
rename为文件重命名函数,函数原型如下:
int rename(const char *oldpath, const char *newpath);
oldpath为原文件名,newpath为新文件名,如果操作成功返回0,否则返回非0值,调用rename函数时,文件必须关闭,否则会操作失败。
4. remove
remove函数为删除文件,函数原型如下:
int remove(const char *pathname);
同样在调用remove函数时,文件也必须是关闭的,否则会操作失败。
5. fputc
fputc函数将一个字符写入文件中,如果操作成功,返回写入的字符,否则返回EOF,函数原型如下:
int fputc(int c, FILE *stream);
实际上,字符并不是一个一个地写入物理文件的,这样效率极低。写入字符是存放在内存中的一个缓冲区里面的,缓冲区中字符累积到一定的数量后,才一次性将他们写入文件中。
putc等同于fputc,它们的参数和返回值都相同,只是在标准库中putc实现为一个宏,fputc为一个函数。
6. fgetc
fgetc同fputc相反,fgetc从文件中读取一个字符,如果操作成功,返回读取到的字符,否则返回EOF,函数原型如下:
int fgetc(FILE *stream);
fgetc也不是每次都操作物理文件的,读取字符时是一次读取一整块数据到缓冲区中,然后fgets操作只是从缓冲区中读取一个字符,直到缓冲区为空,再读取一个块数据到缓冲区中。
同putc一样,getc等同于fgetc。
5. fputs
同puts类似,fputs将字符串写入文本文件中,只是puts是将字符串写入stdout中,函数原型如下:
int fputs(const char *s, FILE *stream);
如果操作成功返回0,否则返回EOF。
注意:fputs用于将字符串写入文件中,直到遇到'\0'为止,但是'\0'不会写入文件中。
6. fgets
fgets函数从文本文件中读取字符串,函数原型如下:
char *fgets(char *s, int size, FILE *stream);
注意:fgets从文件中读取字符串,直到读取到'\n'字符或读取到size-1个字符为止,如果读取到'\n'字符,它会将'\n'字符保留在字符串中,在字符串的末尾会附加上'\0'字符,如果操作成功,返回读取到的字符串指针,否则返回NULL。
7. fprintf
同printf类似,printf操作的对象是stdout,而fprintf接收一个FILE指针参数,函数原型如下:
int fprintf(FILE *stream, const char *format, ...);
8. fscanf
fscanf同scanf类似,scanf操作的对象是stdin,fscanf接收一个FILE指针参数,函数原型如下:
int fscanf(FILE *stream, const char *format, ...);
如果读取错误,返回EOF,成功返回读取到值的个数。
9. fflush
fflush函数将缓冲区中的数据写入文件中,函数原型如下:
int fflush(FILE *stream);
成功返回0,失败返回EOF。
10. 错误处理
一般将错误信息写入stderr中,而不是stdout,因为stdout可以重定向,例如:
FILE *fp = NULL;
if ((fp = fopen("test.txt", "r")) == NULL) {
fprintf(stderr, "Can't open file!!!\n");
exit(1);
}
二、二进制流
要指定以二进制模式读写文件,只需要在文件打开模式中加上b,例如rb、wb、rb+。
1. fwrite
函数原型如下:
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
fwrite函数并不检查文件是以文本方式还是二进制方式打开,当然也可以使用fwrite将数据写入文本文件中,但是结果会让人很难理解。fwrite实例如下:
FILE *fp = NULL;
int data = 10;
fp = fopen("test", wb);
if (fp != NULL) {
fwrite(&data, sizeof(int), 1, fp);
}
fwrite函数中的参数size为单个元素的字节数,nmemb为元素个数,所以写入的总字节数为size * nmemb。
2. fread
函数原型如下:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
3. 随机访问文件
很多时候,需要随机访问文件中的数据,而不是顺序的访问,这就涉及到修改文件当前的读写位置。需要注意的是,不能在追加模式下修改写操作的位置,因为数据总是写到文件的末尾,可以修改读操作的位置。
(1) ftell
ftell函数返回当前读写操作的位置,函数原型如下:
long ftell(FILE *stream);
(2) fgetpos
fgetpos同ftell函数是相同的作用,都是返回当前的读写操作位置,函数原型如下:
int fgetpos(FILE *stream, fpos_t *pos);
同ftell函数不同的是,读写的位置是存储在参数pos中的,成功返回0,失败返回非0值,例如:
fpos_t here = 0;
fgetpos(fp, &here);
(3) fsetpos
同fgetpos函数对应的是fsetpos,函数原型如下:
int fsetpos(FILE *stream, fpos_t *pos);
fsetpos只能和fgetpos配合使用,也就是说只能使用fgetpos得到的值作为其中的pos参数,例如:fsetpos(fp, &here);
同fgetpos一样,成功返回0,失败返回非0值。
(4) fseek
同ftell函数对应的是fseek,函数原型如下:
int fseek(FILE *stream, long offset, int whence);
第一个参数为文件指针,第二个参数为相对第三个参数的偏移,第三个参数为起始位置,取值有:SEEK_SET、SEEK_CUR和SEEK_END,分别表示文件的开头,当前位置和文件末尾。
补充:软件开发 , C语言 ,