在最近的编程练习和写东西的过程中,常常用到了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. jmeter仅一次控制器

    仅针对 1个线程的 多线程的那个不生效  想实现多次执行某个请求只执行一次 需要设置为单线程 循环次数设置为多次就可以了 

  2. Tomcat 解压版安装

    1.下载tomcat7.0 http://tomcat.apache.org/download-70.cgi

  3. codeforces#271 (Div. 2)预处理

    B. Worms time limit per test 1 second memory limit per test 256 megabytes input standard input outpu ...

  4. socket是什么?(翻译)

    根据stackoverflow的答案: 原文:A socket represents a single connection between two network applications. The ...

  5. ZeroMQ接口函数之 :zmq_connect - 由一个socket创建一个对外连接

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_connect zmq_connect(3)  ØMQ Manual - ØMQ/3.2.5 Name zmq_c ...

  6. 【Alpha】Daily Scrum Meeting第二次

    一.Daily Scrum Meeting照片 二.Burndown Chart 由于此次项目延期7天,因此Burndown Chart较第一次会变宽 三.项目进展 登陆模块已经能和服务器交流 可以使 ...

  7. 【Alpha】Daily Scrum Meeting总结

    一.项目预期计划和现实进展 项目预期计划 现实进展 登陆 完成 使用菜单 完成 查看自己的信息 完成(额外完成可修改) 完成能用的界面 完成(额外美化) 可以导入导出表格 导入表格完成,导出未完成 教 ...

  8. *cf.4 贪心

    D. Kostya the Sculptor time limit per test 3 seconds memory limit per test 256 megabytes input stand ...

  9. 服务器部署多个tomcat经验

    如果想在服务器上部署两个或多个tomcat项目,可以采用多个端口的方法: 如何修改Tomcat端口?   答:在Tomcat的conf文件夹里有个server.xml文件,修改里面的<Conne ...

  10. phpcms开发过程中遇到的问题总结

    1.Q:phpcms后台页面使用ajax会进不了控制器方法中 A:因为后台安全性比较高,需要进行hash验证,直接在浏览器输入ajax要访问的路径就会出现如下图的报错.所以后台页面尽可能用其他方法   ...