C语⾔⽂件读写(1)-⽂本⽂件读操作
C语⾔⽂件读写——⽂本⽂件读操作
最近和⼏个初学C语⾔的朋友讨论⽂件读写,发现他们在使⽤C语⾔⽂件读写功能的时候遇到了不少问题,不是⽂件打开⽅式有问题,就是⽂件读写有问题,总是得不到⾃⼰想要的结果。
fgets和fgetc的区别C语⾔⽂件读写操作,既简单⼜复杂,要熟练使⽤每⼀项功能,也确实不易,既有⽂本⽂件操作,也有⼆进制⽂件的读写。本⽂先⽐较详细地介绍C语⾔的⽂本⽂件读操作,下⼀篇会详细介绍⽂本⽂件的写使⽤。
什么是⽂本⽂件
⽂本⽂件是由⼀⾏⼀⾏的字符的有序序列组成的。⼀⾏是由0个或者若⼲个字符加上⼀个⾏结束符'\n'(换⾏符)组成的,但是最后⼀⾏的最后⼀个字符是否是'\n'可能在不同的平台的实现不太⼀样,但是由于是最后⼀⾏的最后⼀个字符,所以并不会影响正常读写,但是我们在写⽂本⽂件的时候,最好也保证最有⼀个字符是⾏结束符'\n'。
在Windows上,为了⽅便⼈阅读⽂本⽂档,在写⼊'\n'的时候,系统会⾃动将'\n'转换为'\r\n'(回车换⾏),在读取的时候会⾃动
将'\r\n'转换为'\n'。在Linux上,并不会有这种转换,所以Windows上的⽂本⽂件在Linux上⽐较低的版本上进⾏读写的时候有时候需要进⾏⼀下转换,但是现在的Linux系统基本上已经能够正常处理'\r\n'了。
为了能够保证⽂本⽂件的正常读写,要求:
写⼊的字符是可打印字符,或者⼀些特殊的控制符,⽐如'\t'(TAB键),'\n'。
不要在空格字符后紧跟'\n'字符,否则可能在读取的时候空格会丢失。
最后⼀个字符是'\n'。
读⽂本⽂件⽰例
说了这么多,我们先直接来⼀个读取⽂本⽂件内容的例⼦,然后再做⼀下具体分析,这个⽂本⽂件的内容就是本⽂档的部分内容,我们把它读取出来,然后在控制台显⽰,我们以Windows系统为例,Linux下⾯的操作⽅式是类似的,读取⽂本⽂件的⽰例代码如下:
#include <stdio.h>
void read_text(const char* file_name)
{
char line[1024]={0};
FILE *file = fopen(file_name,"rt");
if(!file)
return;
while(1)
{
//⽂件读取结束
if(EOF == fscanf(file,"%s",line))
break;
printf("%s\n",line);
}
fclose(file);
}
int main(int argc, char* argv[])
{
read_text("");
return 0;
}
然后运⾏程序,看效果是否是我们期望的那样,⼀⾏⼀⾏地显⽰在控制台,如下图所⽰,上边是程序的输出,下边是⽂件的内容。
mode 含义说明
如果⽂件存在
如果⽂件不存在“r”读以读的⽅式打开⽂件,打开以后只能读成功打开,并从⽂件开始读打开失败"w"写创建⼀个⽂件进⾏写⽂件内容会被清空
创建⼀个新⽂件"a"
追加
追加内容到⽂件末尾
将⽂件内容追加到⽂件末尾
创建⼀个新⽂件
"r+"扩展读打开⽂件进⾏读写,可读可写从⽂件开始读打开失败"w+"扩展写创建⼀个⽂件进⾏读写⽂件内容会被清空创建⼀个新⽂件"a+"
扩展追加
打开⽂件进⾏读写
追加内容到⽂件末尾
创建⼀个新⽂件
读⽂本⽂件详解
C语⾔中打开⽂件,主要有两个函数,fopen和fopen_s,fopen_s是C11标准中新引⼊的,它们的函数申明是这样的:
(1)
FILE *fopen( const char *filename, const char *mode ); (until C99)
FILE *fopen( const char *restrict filename, const char *restrict mode );(since C99) (2)
errno_t fopen_s(FILE *restrict *restrict streamptr,                const char *restrict filename,                  const char *restrict mode);(since C11)
fopen_s会做⼀些额外的检查,更“安全”⼀些,C11标准后,有很多这样的_s的函数,都是为了更加安全,会做⼀些安全性的检查,⽐如函数传⼊的指针是否为空,传⼊的缓冲区是否会溢出等等。
返回值FILE*就代表了⼀个⽂件流对象,如果为NULL,则表⽰打开⽂件失败。重点看⼀下mode,即打开⽂件的⽅式,常⽤的mode主要有:
11啊
除了这些基本的打开模式之外,还可以附加⼀些别的模式,⽐如“t”,“b”等,例如“rb”,表⽰以⼆进制⽅式打开⽂件,"wt"以⽂本⽂件的⽅式创建⽂件,默认是⽂本⽂件⽅式,所以“t”⼀般可以省略。
从上⾯的表格我们可以简单总结出来,只要是有“r”标志,⽂件就要求存在,否则就会打开失败,其他的打开模式都不需要⽂件⼀定存在,如果⽂件不存在,则会创建⼀个新⽂件。所以,在进⾏⽂件读写之前,⼀定要明确⾃⼰的⽬的,是读⽂件,还是写⽂件,是从⽂件开始写,还是追加内容到⽂件末尾。
本节的主题是读⽂本⽂件,所以我们在使⽤fopen的时候,就使⽤"r"或者"rt"标志,不要使⽤别的模式。
说到读⽂本⽂件内容,不是⼀件简单事情,因为读⽂本⽂件内容也涉及到好些函数,如果选择不当,得到的结果也会不是我们期望的。
其实⽂本⽂件的内容虽然在前⾯介绍过,要作为⽂本⽂件是有要求的,⽐如前⾯提到的每⾏要求以'\n'结束等。但是⽂本⽂件内容本⾝⼜可以分为有格式的和没有格式的,⽐如我们前⾯的⽂件,就是没有格式的,就是纯⽂本⽂件,因为除了换⾏之外,没有任何别的格式,信息是杂乱的。如果我们查看⼀下⼀个普通的Excel⽂件,⾥⾯的数据就很有格式,⼀⾏⼀⾏,⼀列⼀列,⾮常整齐,这就是有格式的⽂本⽂件样⼦,我们看⼀个有格式的⽂本⽂件,这是⼀个简易的学⽣名单⽂件,
每⼀⾏包含的内容为学号,姓名,所属学院,成绩,如下图所⽰。
这个⽂件内容就是格式化的,即每⼀⾏的数据格式都是⼀致的,包含的信息都是类似的,即都包含学号,姓名,学院和分数,⽽且它们之间是⽤空格隔开的,这就是格式化的⽂件数据。
在读取⾮格式化和格式化数据的时候,需要⽤到的函数是不⼀样的,否则,⽤⾮格式化数据读取函数去读取格式化的数据,读取到的内容我们也不好利⽤,同样,⽤格式化数据读取函数去读取⾮格式化的数据,有时候也得不到正确结果。
⾮格式化⽂本⽂件读取
⾮格式⽂本⽂件内容读取主要有两个函数,fgetc和fgets,fgetc的函数原型为:
int fgetc( *stream );
每次从⽂件中读取⼀个字符,直到⽂件结尾。如果读到⽂件结尾,返回EOF。
fgetc不会对⽂件中的内容做任何处理,⼀次就读⼀个字符,'\n'也会想普通字符⼀样读取,我们来看⼀下使⽤fgetc读取我们前⾯提到的⽂件的效果,代码如下:
void read_text_by_getc(const char* file_name)
{
int ch = 0;
FILE *file = fopen(file_name,"rt");
if(!file)
return;
while(EOF != (ch = fgetc(file)))
{
//在屏幕上输出读到的每⼀个字符
putchar(ch);
}
fclose(file);
}
则执⾏效果下图所⽰。
fgetc对于读取⾮格式化的⽂本内容,就是最真实地反应了⽂本⽂件中的内容。
但是fgetc读取容易,处理起来却很⿇烦,如果只是在屏幕上显⽰⽂件内容的话,没有问题,如果还要想对读取的内容进⾏处理的话,就⿇烦⼀些。
我们再来看⼀下fgets函数。
fgets的函数原型为:
char *fgets( char *restrict str, int count, *restrict stream );(since C99)
每次从⽂件中读取指定长度的内容,读取的长度最多为count-1个字符。读到'\n'的时候,这⼀次读取就结束了,即使还没有读到count-1个字符。如果读取失败,返回NULL。
我们先看⼀下使⽤fgets读取⽂本⽂件的效果,代码如下:
void read_text_by_gets(const char* file_name)
{
char buffer[20]={0};
FILE *file = fopen(file_name,"rt");
if(!file)
return;
while(NULL != (fgets(buffer,sizeof(buffer),file)))
{
//显⽰每⼀次读取到的内容
printf("%s",buffer);
}
fclose(file);
}
然后运⾏,结果如下所⽰。
从图中可以看到,显⽰结果⼀塌糊涂,为什么呢?因为我们的缓冲区buffer⼤⼩是20,所以每次只读19个字符,有时候刚好得到半个汉字,就显⽰很凌乱了。
因此,如果我们想完整的⼀次读取⼀⾏的数据,这个缓冲区buffer必须⾜够⼤,⾄少要超过⽂件中最长的⼀⾏字符的长度,我们修改⼀下程序,我们知道我们的⽂件中每⾏都不会太长,所以我们把buffer的⼤⼩设置成128,看看效果如何,如下图所⽰。
因此,我们在对于⾮格式化⽂本进⾏读取的时候,要选择合适的函数来读取,如果想⼀⾏⼀⾏的读取⽂本⽂件中的内容,最好使⽤fgets,并且提供⾜够⼤的缓冲区。
格式化⽂本⽂件读取
格式化⽂本⽂件的读取主要⽤到函数fscanf和fscanf_s。
先看⼀下fscanf函数的原型。
int fscanf( *restrict stream, const char *restrict format, ... );(since C99)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。