文件的打开和关闭
要想实现该操作,我们要先了解两个函数,分别是fopen函数和fclose函数
fopen(打开文件)
fopen函数的声明
FILE * fopen ( const char * filename, const char * mode );
filename表示要打开的文件名称(可包含路径);mode 表示打开方式。
fopen函数的返回值
如果文件成功打开,该函数将返回指向 FILE 对象的指针,该对象可用于在将来的操作中标识流。
否则,将返回空指针
fclose(关闭文件)
fclose函数的声明
int fclose ( FILE * stream );
stream 指向指定要关闭的流的 FILE 对象的指针。
fclose函数的返回值
如果流成功关闭,则返回零值。
失败时,将返回 EOF。
绝对路径和相对路径
在进行文件操作时,我们先了解两个概念:绝对路径和相对路径。
绝对路径
假设有个文本文档的名称为“test.txt”,其存放在C盘的code文件夹中,即其路径为“C:\code\test.txt”,这个“C:\code\test.txt”就叫做绝对路径;
相对路径
若该文本文档与当前的项目在同一个目录中,那么其相对路径就是"test.txt";若该文本文档在该项目的上一个目录中,我们可以这样表示:“…/test.txt”,两个点表示上一个目录,三个点表示上上个目录,依次类推。
要注意,不同操作系统使用的斜杠不同,Linux系统是/,window系统是\
打开方式
这是fopen函数的第二个参数,如下
“r”(只读) 为了输入数据,打开一个已经存在的文本文件,如果指定文件不存在则出错
“w”(只写) 为了输出数据,打开一个文本文件,如果指定文件不存在则建立一个新的文件
“a”(追加) 向文本文件尾添加数据,如果指定文件不存在则建立一个新的文件
“rb”(只读) 为了输入数据,打开一个二进制文件,如果指定文件不存在则出错
“wb”(只写) 为了输出数据,打开一个二进制文件,如果指定文件不存在则建立一个新的文件
“ab”(追加) 向一个二进制文件尾添加数据,如果指定文件不存在则出错
“r+”(读写) 为了读和写,打开一个文本文件,如果指定文件不存在则出错
“w+”(读写) 为了读和写,建议一个新的文件,如果指定文件不存在则建立一个新的文件
“a+”(读写) 打开一个文件,在文件尾进行读写,如果指定文件不存在则建立一个新的文件
“rb+”(读写) 为了读和写打开一个二进制文件,如果指定文件不存在则出错
“wb+”(读写) 为了读和写,新建一个新的二进制文件,如果指定文件不存在则建立一个新的文件
“ab+”(读写) 打开一个二进制文件,在文件尾进行读和写,如果指定文件不存在则建立一个新的文件
代码实现
int main()
{
//打开文件
//FILE* pf = fopen("C:\\code\\test.txt", "r");
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");//如果错误则打印错误信息
return 1;
}
else
{
printf("打开文件成功\n");
}
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
鼠标右击该项目的源文件,打开所在文件夹,在里面创建一个文本文档命名为"test.txt"
运行该代码,若打开文件成功,则有如下界面;若打开失败则会显示错误信息。
文件的顺序读写
同样的,在这之前我们先认识几个函数
fgetc(字符输入函数)
fgetc函数的声明
int fgetc ( FILE * stream );
stream指向标识输入流的 FILE 对象的指针
fgetc函数的返回值
成功后,将返回字符读取(提升为 int 值)。
返回类型为 int 以适应特殊值 EOF,该值表示失败
通过fgetc函数写文件
int main()
{
//以只写的方式打开文件
FILE* pf = fopen("test.txt", "w");
//判断返回值
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写文件
fputc('a', pf);
fputc('b', pf);
fputc('c', pf);
fputc('d', pf);
fputc('e', pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
我们可以打开记事本观察,如下显示:
这是运行前的结果
这是运行后的结果
可以看到我们已经成功往文件中写入了文件。
通过fgetc函数读文件
int main()
{
//以只读的方式打开文件
FILE* pf = fopen("test.txt", "r");
//判断返回值
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
int ch = fgetc(pf);
printf("%c ", ch);
ch = fgetc(pf);
printf("%c ", ch);
ch = fgetc(pf);
printf("%c ", ch);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
该文件里存放着abcde五个字符,我们读了三次,每次打印一个字符,打印出来的恰好就是abc
知道了写文件和读文件之后,我们再来看看如何写一行文件和读一行文件,同样的认识一下几个函数。
fputs(文本行输出函数)
fputs函数的声明
int fputs ( const char * str, FILE * stream );
str包含要写入流的内容的 C 字符串;stream指向标识输出流的 FILE 对象的指针。
fputs的返回值
成功时,将返回非负值。
出错时,该函数返回 EOF 并设置错误指示器(ferror)。
通过fputs函数写一行文件
int main()
{
//以只写的方式打开文件
FILE* pf = fopen("test.txt", "w");
//判断返回值
if (pf == NULL)
{
perror("fopen");
return 1;
}
//写一行文件
fputs("hello world\n", pf);
fputs("abcdef\n", pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
运行后记事本的内容如下,可以看到内容已经成功写入
fgets(文本行输入函数)
fgets函数的声明
char * fgets ( char * str, int num, FILE * stream );
str指向在其中复制字符串读取的字符数组的指针;
num表示要复制到 str 的最大字符数(包括终止空字符);
stream指向标识输入流的 FILE 对象的指针。
fgets函数的返回值
成功后,函数返回 str。
如果在尝试读取字符时遇到文件末尾,则设置 eof 指示器 (feof)。如果在读取任何字符之前发生这种情况,则返回的指针为空指针(str 的内容保持不变)。
如果发生读取错误,则设置错误指示器(ferror),并返回空指针(但str指向的内容可能已更改)。
通过fgets函数读一行文件
int main()
{
//以只读的方式打开文件
FILE* pf = fopen("test.txt", "r");
//判断返回值
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读一行文件
char buf[20] = { 0 };
fgets(buf, 20, pf);
printf("%s", buf);
fgets(buf, 20, pf);
printf("%s", buf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
运行结果如下,可以看到文件的内容成功地被输出到了屏幕上
二进制的输入和输出
主要包括以二进制地方式写入和读取文件,还是需要通过几个函数来实现
fwrite(写入要流式传输的数据块)
fwrite函数的声明
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );
ptr指向要写入的元素数组的指针;
size表示要写入的每个元素的大小(以字节为单位);
count表示元素数,每个元素的大小为字节大小;
stream指向指定输出流的 FILE 对象的指针。
fwrite函数的返回值
返回成功写入的元素总数。
如果此数字与 count 参数不同,则写入错误阻止函数完成。在这种情况下,将为流设置错误指示器(ferror)。
如果大小或计数为零,则该函数返回零,错误指示器保持不变。
通过fwrite以二进制的方式写文件
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan", 20, 95.5 };
FILE* pf = fopen("test.txt", "wb");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//以二进制的方式写文件
fwrite(&s, sizeof(struct S), 1, pf);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
运行后我们打开文本文档查看,结果是这样
其实这很正常,以为是以二进制的方式写入,我们可以这样查看:
首先在源文件处添加现有项
然后添加该文本文档
右击该新建项选择打开方式,接着
我们就可以看到二进制的编辑结果了
fread(从流中读取数据块)
fread函数的声明
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
ptr指向大小至少为 (size*count) 字节的内存块的指针;
size表示要读取的每个元素的大小(以字节为单位);
count表示元素数,每个元素的大小为字节大小;
stream指向指定输入流的 FILE 对象的指针。
fread函数的返回值
返回成功读取的元素总数。
如果此数字与 count 参数不同,则表示读取时发生读取错误或到达文件末尾。在这两种情况下,都会设置正确的指标,可以分别用 ferror 和 feof 进行检查。
如果大小或计数为零,则该函数返回零,并且流状态和 ptr 指向的内容保持不变。
通过fread以二进制的方式读文件
struct S
{
char name[20];
int age;
float score;
};
int main()
{
struct S s = { "zhangsan", 20, 95.5 };
FILE* pf = fopen("test.txt", "rb");
if (NULL == pf)
{
perror("fopen");
return 1;
}
//以二进制的方式读文件
fread(&s, sizeof(struct S), 1, pf);
printf("%s %d %f", s.name, s.age, s.score);
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
结果如下
与我们写入文件中的结果一致。
文件的随机读写
fseek函数(重新定位流位置指示器)
fseek函数的声明
int fseek ( FILE * stream, long int offset, int origin );
stream指向标识流的 FILE 对象的指针;
offset表示要从源偏移的字节数;
origin用作偏移参考的位置;
其中origin由< cstdio >中定义的以下常量之一指定,专门用作此函数的参数
origin | 参考位置 |
---|---|
SEEK_SET | 文件开头 |
SEEK_CUR | 文件指针的当前位置 |
SEEK_END | 文件结尾 |
fseek函数的返回值
如果成功,该函数将返回零。
否则,它将返回非零值。
如果发生读取或写入错误,则设置错误指示器(ferror)。
代码实现
我们先把文本文档里写入abcde的内容,如图
当我们关掉该文档再打开时,就变成了这样
它两的区别在于光标,其实fseek说白了就是控制该光标的位置
int main()
{
FILE*pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
}
else
{
int ch = fgetc(pf);
printf("%c\n", ch);//a
ch = fgetc(pf);
printf("%c\n", ch);//b
ch = fgetc(pf);
printf("%c\n", ch);//c
}
return 0;
}
打印出来的结果如下
每次fgets后光标默认向后移动一个,如果继续往下读,必然是打印d,但是我们可以通过fseek函数控制它,让它去打印b,我们可以这样写
fseek(pf, -2, SEEK_CUR);//表示从当前位置向左偏移两位
也就是从
变成
从而打印b,或者我们也可以这样写
fseek(pf, 1, SEEK_SET);//表示从起始位置向右偏移一位
同样是打印b
有时候我们不知道偏移量是多少,我们可以通过ftell函数计算偏移量,用法很简单;还可以通过rewind函数将文件指针恢复到起始位置,用法同样很简单。
int main()
{
FILE*pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
}
else
{
int ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
ch = fgetc(pf);
printf("%c\n", ch);
fseek(pf, 1, SEEK_SET);
ch = fgetc(pf);
printf("%c\n", ch);
printf("%d\n", ftell(pf));
rewind(pf);
ch = fgetc(pf);
printf("%c\n", ch);
}
return 0;
}
以上代码运行的结果如下,首先打印abc,调整偏移量后打印b,然后计算此时的偏移量为2,最后通过rewind函数将文件指针恢复到起始位置,从而打印a。