fopen,fread和fwrite
在最近的编程练习和写东西的过程中,常常用到了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的更多相关文章
- open/fopen read/fread write/fwrite区别
fopen /open区别 UNIX环境下的C 对二进制流文件的读写有两套班子:1) fopen,fread,fwrite ; 2) open, read, write这里简单的介绍一下他们的区别.1 ...
- fopen\fread\fwrite\fscanf\fprintf\fseek\feof\rewind\fgets\fputc等系列函数使用总结
转载自:http://blog.csdn.net/xidianzhimeng/article/details/23541289 1 fopen 函数原型:FILE * fopen(const char ...
- fopen\fread\fwrite\fseed函数的使用
使用 <stdio.h> 头文件中的 fopen() 函数即可打开文件,它的用法为: FILE *fopen(char *filename, char *mode); filename为文 ...
- 函数fgets和fputs、fread和fwrite、fscanf和fprintf用法小结 (转)
函数fgets和fputs.fread和fwrite.fscanf和fprintf用法小结 字符串读写函数fgets和fputs 一.读字符串函数fgets函数的功能是从指定的文件中读一个字符串到字符 ...
- Matlab 用fread、fwrite实现大文件读写
最近在分析一个35G的大数据文件,猛一看,是不是很吓人啊,不过还好,师兄写文件的格式非常规范,读取数据来也就很方便了,主要是使用了读写文件的两个函数fread和fwrite,下面用matlab简单尝试 ...
- C++之函数fgetc和fputc、fgets和fputs、fread和fwrite、fscanf和fprintf用法小结
#include <iostream> #include <cstdio> #include <cstdlib> using namespace std; int ...
- C语言中的fread和fwrite
C语言中的fread和fwrite是专门用来操作文件的方法. 1. fread负责从打开的文件指针中读取文件内容. 函数原型:size_t fread(void *p, size_t size, si ...
- fread 和 fwrite 函数用法示例以及注意事项
1.函数功能 用来读写一个数据块. 2.一般调用形式 fread(buffer,size,count,fp); fwrite(buffer,size,count,fp); 3.说明 ( ...
- fread和fwrite的使用
fread和fwrite的使用 fread和fwrite一般用于二进制文件的输入/输出,要不然你打开fwrite写入的文件就是乱码. 1.fread和fwrite函数 数据块I/O fread与fwr ...
随机推荐
- nodejs review-01
lesson lesson-code 05 Run your first web server 使用curl //指定方法;显示header信息 curl -X GET -i localhost:30 ...
- 读写注册表 registrykey 创建删除
namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { Initialize ...
- mac os x 10.10.3 安装protoc
预装如下环境 autoconf 2.6.9automake 1.14libtool 2.4 Building from source Download latest version of procbu ...
- Codeforces Round #FF(255) DIV2
A - DZY Loves Hash 水题,开辟一个数组即可 #include <iostream> #include <vector> #include <algori ...
- Apache虚拟主机配置,实现多域名访问本地项目PHP空间,以及配置403Forbidden等错误的解决办法
第一步: apache主配置文件修改: 用文本编辑器打开apache的conf目录下 httpd.conf 将下面以下代码取消注释 LoadModule rewrite_module modules ...
- java分享第九天-01(抽象类)
1 为什么需要抽象类?如何定义抽象类 是一种模板模式,抽象类为所有子类提供了一个通用模板,子类可以在这个模版基础上进行扩展: 通过抽象类,可以避免子类设计的随意性.通过抽象类,我们就可以做到严格限制子 ...
- Canvas 实现七彩喷泉
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- SQL SERVER 数据库操作脚本
创建数据库 create Database MYDB on ( Name=mydb_dat, FileName='c:\data\mydate.mdf',size=10,maxsize=50 ) LO ...
- 【PC网站前端架构探讨系列】结合公司网站首页,谈前端模块化开发与网站性能优化实践
说在前面 上午给大家分享的个人认为比较全,比较官方,比较清晰的grunt使用教程,被挪出首页了,不过没关系,毕竟不是原创,大家想看,我现在贴出地址: http://www.cnblogs.com/sy ...
- KnockoutJS:
一.ko对象 js对象的改变都会导致viewmodel的变化,但view不一定变化 往ko对象里面push,viewmodel的变化,引起view的变化. 往js对象里面push,model的变化引起 ...