这里的思路和之前的‘Linux下文件的创建写入读取编程‘的思路是一样的,都是从介绍API的函数功能开始。都是在Linux下操作文件,但是前者是UNIX系统调用函数,后者是ANSIC标准中的C语言库函数。它们有着不同的适用范围,具体有不同,在概念上我就不细说了,大家可以看看这篇博客园的博客https://www.zybuluo.com/yiltoncent/note/87461。我会从函数的使用上给大家说一说区别。
之前我们写的文件操作相关函数都是UNIX系统调用函数,如open,write,read等,这里我们要用的是标准c库对文件下的函数,如fopen,fread,fwrite,fseek等。
一、API函数的介绍
打开文档
①FILE *fopen(const char *pathname, const char *mode);
’参数
pathname:文件路径名
mode:文件模式,有以下六种
种类 | 功能 |
r | 只读打开 |
r+ | 可读可写打开 |
w | 可写打开,写入文件同时清除原文件内容 |
w+ | 可读可写;如果文件存在,写入文件同时清除原文件;如果不存在创建文件 |
a | 可写打开,如果文件存在,在原文件尾部继续写;如果不存在创建文件 |
a+ | 可读可写打开,如果文件存在,在原文件尾部继续写;如果不存在创建文件 |
fopen() mode 与 open() flags的联系
fopen() mode | open() flags |
r | O_RDONLY |
w | O_WRONLY | O_CREAT | O_TRUNC |
a | O_WRONLY | O_CREAT | O_APPEND |
r+ | O_RDWR |
w+ | O_RDWR | O_CREAT | O_TRUNC |
a+ | O_RDWR | O_CREAT | O_APPEND |
返回值
打开成功,返回一个FILE类型指针;打开失败返回NULL
读文档
②size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数
ptr:存储读出的内容到缓冲区
size:一次读出的内容大小
nmemb:读几次
stream:指向文件结构体的指针,相当于open函数的fd,起到索引作用
返回值
读取成功,返回写入的字节数除以size(不是整数采用去尾法,如1.5,返回值就是1)
0,发生读取错误,应检查必须用feof()或ferror()来决定发生什么情况,看一下是读到了文件尾了或者有错误发生,
feof() 函数用来判断文件内部指针是否指向了文件末尾,它的原型是:
int feof (FILE * stream)
当指向文件末尾时返回非零值,否则返回零值。
ferror() 函数用来判断文件操作是否出错,它的原型是:
Int ferror (FILE* stream)
出错时返回非零值,否则返回零值。
写文档
③size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
参数
ptr:存储写入的内容到缓冲区
size:一次读写出的内容大小
nmemb:写几次
stream:指向文件结构体的指针,相当于open函数的fd,起到索引作用
成功写入,返回值为nmemb的大小;写入失败,返回值为0。
文件光标移动
④int fseek(FILE *stream, long offset, int whence);
参数
stream:指向文件结构体的指针,相当于open函数的fd,起到索引作用
offset:相对于whence偏移的位置。0,不偏移;正整数,向右偏移对应的大小;负整数,向左偏移对应的大小。
whence:光标起始位置,一共有三个选项,如下
SEET_SET 文件开头
SEET_CUR 文件当前位置
SEET_END 文件尾部
如果成功,则返回相对于文件头的偏移量。如果出现错误,则返回-1。如果我们设置为
fseek(fp,0,SEET_END)就可以利用他的返回值来表示文件的大小了。
关闭文档
⑤int fclose(FILE *stream);
参数
stream:指向文件结构体的指针,相当于open函数的fd,起到索引作用
返回值
成功完成后,返回0;否则,返回EOF
以字符的形式读取文件
⑥int fgetc(FILE *stream);
参数
stream:指向文件结构体的指针,相当于open函数的fd,起到索引作用
返回值
成功时返回读入的字节数。错误或文件尾时返回EOF;
以字符的形式写入文件
⑦int fputs(const char *s, FILE *stream);
参数
s:要写入的字符串
stream:指向文件结构体的指针,相当于open函数的fd,起到索引作用
返回值
成功时返回写入的字节数。错误时返回EOF;
二、代码验证
1.以w+的方式创建打开新文件,并写入内容在读出
原来并没有ydlt.txt的文件,如下图
现在我们通过程序创建并打开写入内容,并读取。看一下读取的内容与写入的内容是否一致,并查看fread和fwrite的返回值
验证代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
FILE *fp;
char*str="yangdong is handsome";
char readBuf[128]={0};
fp=fopen("./ydlt.txt","w+");
if(fp==NULL)
{
printf("read failed\n");
exit(-1);
}
int n_write=fwrite(str,sizeof(char),100,fp);
printf("n_write=%d\n",n_write);
fseek(fp,0,SEEK_SET);
int n_read=fread(readBuf,sizeof(char)*strlen(str),100,fp);
printf("n_read=%d\n",n_read);
if(n_read==0)//判断读取是否文件操作出错导致的
{
printf("打开文件错误\n");
if(ferror(fp))//判断读取出错是否是由文件
puts("读取出错\n");
if(feof(fp)) //判断读取出错是否是由文件光标到了文件尾部导致的
puts("达到文件尾部\n");
exit(-1);
}
printf("read %s\n",readBuf);
return 0;
}
执行结果如下图所示,可以看到的文件ydlt.txt已经创建,n_write=100,n_read=5,读出的内容是yangdong is handsome
在前面我们已经说过了,n_write作为fwrite的返回值,在写入无误后返回值是nmemb的大小。程序里nmemb是100,对应n_write=100。n_read作为fread的返回值,成功读出后,其大小应该是返回写入的字节数除以size,程序里我们写入的是总大小是sizeof(char)*100,也就是100。我们读取的时候,一次的大小是“yangdong is handsome”的长度,也就是20,所以n_read=100/20=5。如果我们改一下程序,比如将fwrite(str,sizeof(char),100,fp);的100改成99,那么n_read=99/20=4.95,我们知道n_read是int型,所以对于4.95会转化成4,也就是我们前面说的去尾法。
在程序里我们在fwrite后面加了一句fseek(fp,0,SEEK_SET),它的作用就是把文件光标回到文件开头。我们知道文件写入完成后文件光标回到文件末尾,如果我们不把文件光标回到文件开头,我们读取的时候就会发生错误,现在我们把fseek(fp,0,SEEK_SET)删除。看一看现象
我们可以看到当文件光标在文件末尾的时候我们去读,会发生错误,fread的返回值是0,且通过feof()和ferror()函数用来判断到底是哪个环节出错了,最后显示是因为文件光标在文件末尾导致的出错。
2.fputc和fgetc的验证
fputc的验证
#include<stdio.h>
#include<string.h>
int main()
{
FILE*fp;
char*str="yangdong is really handsome\n";
fp=fopen("./test.txt","w+");
int n_size=strlen(str);
for(int i=0;i<n_size;i++)
{
fputc(*str,fp);//一个字节一个字节的打印到文件里面
str++;
}
fclose(fp);
return 0;
}
可以看到已经成功的写入了我们想要的内容
fgetc的验证
#include <stdlib.h>
#include<stdio.h>
int main()
{
FILE *fp;
char ch;
//如果文件不存在,给出提示并退出
if( (fp=fopen("./test.txt","r")) == NULL )
{
puts("Fail to open file!");
exit(-1);
}
//每次读取一个字节,直到读取完毕
while( (ch=fgetc(fp)) != EOF ){
putchar(ch);
}
putchar('\n'); //输出换行符
if(ferror(fp))
{
puts("读取出错");
}
else
{
puts("读取成功");
}
fclose(fp);
return 0;
}
可以看到成功读取了test.txt的内容,同时虽然是EOF引起的循环结束,但是造成EOF的原因也很明显,是因为读取到了文件的末尾造成的,而不是读取出错造成的。