在最近的编程练习和写东西的过程中,常常用到了fopen和fread两个函数来读取本地文件。之前使用这两个函数时,一直没有出现过什么问题。也是因为没有出现问题,对这两个函数的用法的一些细节没有很了解,所以导致这次使用出现了问题。

  这次在尝试写一个简单C编译器的过程中,第一步就是需要从本地读取需要编译的源码文件,于是自然又想到了这两个函数。但是在使用过程中,出现了一些奇怪的问题。具体问题就是:将本地文件读取到内存中,在控制台输出读到的文件内容,结果发现控制台输出中读到的数据不对,末尾数据多了部分字符。

  程序源码如下:

 #include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#define MAX_SIZE 256*1024 char src[MAX_SIZE]; int main(int argc ,char** argv)
{
char file_name[];
printf("input file name:");
scanf("%s",file_name);
int line=;
FILE * fp=NULL;
int i=;
if((fp=fopen(file_name,"r"))<)
{
printf("open file fail!\n");
return -;
}
memset(src,,sizeof(MAX_SIZE));
if ((i=fread(src,,MAX_SIZE-,fp))&&i<)
{
printf("read return %d\n",i);
}
printf("%s",src);
system("pause");
return ;
}

  需要读取的文件是test.cpp(放在工程源码目录下),文件中的内容如下:

 #include <iostream>
using namespace std; int main()
{
cout<<"hello world"<<endl;
return ;
}

  在VS或者Dev中编译运行源码,结果如下图所示:

  其中画红框部分即为多读的数据。为什么会出现这样的问题?

  源码的逻辑过程很简单没有什么错误,那就是在函数的使用上有问题。这里与文件读取有关的就是fopen和fread这两个函数。先来看看fopen函数的原型及参数:

FILE * fopen(const char * path,const char * mode);

  第一个参数显然是需要打开的文件路径。第二个参数是文件打开的方式,具体就是控制打开的文件的读取权限和文本方式打开还是二进制文件打开。而该函数的返回值是一个FILE结构体指针。

  关于上面产生的错误,其实就是由于文件打开方式不适造成的,这个可能也是很多人容易忽略的。因为觉得只要调用了fopen函数并且加个if判断文件打开成功就没问题了。但是其实第二个参数也会带来问题。回头看看源码中fopen的调用方法,发现第二个参数是"r",即以只读方式打开文件(更多参数自行百度),但是并没有指明是以文本方式打开还是以二进制方式打开。其实,当没有显示声明文件打开方式的时候,该函数默认使用文本方式打开文件,即参数"r"其实等同于"rt"(r:read的缩写、t:txt的缩写)。也正是因为这个原因,造成了上面的错误。在这里,只要把fopen的第二个参数改成"rb",即以二进制文件打开文件,就不会出现上面的错误了。而以文本方式打开文件,可能是因为做了一些字符替换,于是导致了上述的错误。

  在这里,可以去了解一下什么是文本文件,什么是二进制文件。文件以文本方式打开和以二进制方式打开有什么区别?

  再来看看fread函数。fread从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读取到的元素个数,如果不成功或读到文件末尾返回 0。其函数原型及参数如下:

size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;

  buffer:接收数据的内存地址

  size:每个数据项的字节数,单位是字节。

  count:要读取count个数据项,每个数据项size个字节。

  stream:输入流

  返回值是size_t类型,这也是一个值得留意的地方。

  看完上面的参数,来稍微解释一下fread是怎么进行的。fread最多读取size*count个字节,但是不是一次读取完,而是每次读取size个字节,一共读count次。当某次读取不足size字节时,则读到了文件末尾,并返回一共成功读了几次size字节。下面举个例子说明:假设fp指向一个5个字节的文件,调用fread函数将文件内容读到buffer:

int i=fread(buffer,,,fp);

  上面的调用表示最多读取10次,每次读取2个字节,而文件只有5个字节,那么i的值是多少?答案是2.第一次读取两个字节,第二次再读取两个字节,到了第三次,只剩下一个字节了,不足一个size的大小,所以这次不进行计数,因此返回值是2,而不是3.

  同样的,fread对应的函数fwrite有着类似的参数和执行过程。

fopen,fread和fwrite的更多相关文章

  1. open/fopen read/fread write/fwrite区别

    fopen /open区别 UNIX环境下的C 对二进制流文件的读写有两套班子:1) fopen,fread,fwrite ; 2) open, read, write这里简单的介绍一下他们的区别.1 ...

  2. fopen\fread\fwrite\fscanf\fprintf\fseek\feof\rewind\fgets\fputc等系列函数使用总结

    转载自:http://blog.csdn.net/xidianzhimeng/article/details/23541289 1 fopen 函数原型:FILE * fopen(const char ...

  3. fopen\fread\fwrite\fseed函数的使用

    使用 <stdio.h> 头文件中的 fopen() 函数即可打开文件,它的用法为: FILE *fopen(char *filename, char *mode); filename为文 ...

  4. 函数fgets和fputs、fread和fwrite、fscanf和fprintf用法小结 (转)

    函数fgets和fputs.fread和fwrite.fscanf和fprintf用法小结 字符串读写函数fgets和fputs 一.读字符串函数fgets函数的功能是从指定的文件中读一个字符串到字符 ...

  5. Matlab 用fread、fwrite实现大文件读写

    最近在分析一个35G的大数据文件,猛一看,是不是很吓人啊,不过还好,师兄写文件的格式非常规范,读取数据来也就很方便了,主要是使用了读写文件的两个函数fread和fwrite,下面用matlab简单尝试 ...

  6. C++之函数fgetc和fputc、fgets和fputs、fread和fwrite、fscanf和fprintf用法小结

    #include <iostream> #include <cstdio> #include <cstdlib> using namespace std; int ...

  7. C语言中的fread和fwrite

    C语言中的fread和fwrite是专门用来操作文件的方法. 1. fread负责从打开的文件指针中读取文件内容. 函数原型:size_t fread(void *p, size_t size, si ...

  8. fread 和 fwrite 函数用法示例以及注意事项

    1.函数功能   用来读写一个数据块. 2.一般调用形式   fread(buffer,size,count,fp);   fwrite(buffer,size,count,fp); 3.说明   ( ...

  9. fread和fwrite的使用

    fread和fwrite的使用 fread和fwrite一般用于二进制文件的输入/输出,要不然你打开fwrite写入的文件就是乱码. 1.fread和fwrite函数 数据块I/O fread与fwr ...

随机推荐

  1. Angular JS 学习之路由

    1.AngularJS路由允许我们通过不同的URL访问不同的内容:通过AngularJS可以实现多视图的单页WEB访问(SPA) 2.通常我们的URL形式为http://runoob.com/firs ...

  2. 浏览器-06 HTML和CSS解析2

    选择器 其实现由CSSSelector类来完成: CSSSelector的作用是储存从解析器生成的结果信息; 这里匹配指的是当需要为每个DOM中的节点计算样式时,WebKit需要根据当前的节点信息来从 ...

  3. Some Link: Java Reflection

    http://docs.oracle.com/javase/tutorial/reflect/index.html https://bugs.openjdk.java.net/browse/JDK-4 ...

  4. Spring+SpringMvc+Mybatis整合注意事项

    1.web.xml代码如下 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi ...

  5. CSS3 动画

      通过 CSS3,我们能够创建动画,这可以在许多网页中取代动画图片.Flash 动画以及 JavaScript. CSS3 动画 CSS3 @keyframes 规则 如需在 CSS3 中创建动画, ...

  6. [LintCode] Min Stack 最小栈

    Implement a stack with min() function, which will return the smallest number in the stack. It should ...

  7. python字符串前面加r

    在Python的string前面加上'r', 是为了告诉编译器这个string是个raw string,不要转意backslash '\' . 例如,\n 在raw string中,是两个字符,\和n ...

  8. 实战Java虚拟机之一“堆溢出处理”

    从今天开始,我会发5个关于java虚拟机的小系列: 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟机之三“G1的新生代GC” 实战Java虚拟机之四 ...

  9. 【emWin】例程五:显示数值

    实验指导书及代码包下载: 链接:http://pan.baidu.com/s/1pLexsAf密码:p0jf 实验现象:

  10. 授权其他数据库用户kill session

    授权其他数据库用户kill session kill session权限 CREATE OR REPLACE PROCEDURE P_KILL_SESSION(P_USER IN VARCHAR2, ...