查看 stdio.h 可以看到如下定义:

#define  EOF  (-1)

#define  _IOEOF  0x0010 
#define  feof(_stream)  ((_stream)->_flag & _IOEOF)

由此可以看出,这两种方式的原理是不同的。

在这里先说下EOF和feof()这个两个宏定义,在我们学的课本中有这样的描述。

EOF是不可输出字符,因此不能在屏幕上显示。由于字符的ASCII码不可能出现-1,因此EOF定义为-1是合适的。当读入的字符值等于EOF时,表示读入的已不是正常的字符而是文件结束符,但这适用对文本文件的读写。在二进制文件中,信息都是以数值方式存在的。EOF的值可能就是所要处理的二进制文件中的信息。这就出现了需要读入有用数据却被处理为“文件结束“的情况。为了解决这个问题,C提供了一个feof()函数,可以用它来判断文件是否结束。feof(fp)用于测试fp所指向的文件的当前状态是否为“文件结束”。如果是,函数则返回的值是1(真),否则为0(假)。

说了这两个的定义,肯定还对二进制文件和文本文件的区别有些模糊(唉,因为我当时就对这些搞不懂),那现在就回顾下这两个文件的概念。C语言支持的是流式文件,它把文件看作由一个一个的字符(字节)数据组成的序列。根据数据的组织和操作形式,可以分为ASCII文件和二进制文件。

ASCII文件又称为文本文件,它是在一个字节的存储单元上存放一个字符(在外存中存放的是该字符的ASCII码,每个字符将占一个字节)。

二进制文件是把内存中的数据按其在内存中的存储格式在磁盘上原样保存。

对字符而言,由于其外存存储格式和内存表示格式相同,所以,在外存上也存放每个字符的ASCII码。

但是说EOF只能用于文本文件,其实不然,这点不是特别的准确,还要看定义的变量的类型。

下面这段程序对文本文件和二进制文件都可以:

int c;
while((c=fgetc(fp)) != EOF)
{
printf("%X/n", c); 
}
如果读到了FF,由于c定义为int型,所以实际上c=0x000000FF,不等于EOF(-1=0xFFFFFFFF),因此不会误判为文件结尾。

但是如果把c定义为char类型,就有可能产生混淆了。
char c;
while((c=fgetc(fp)) != EOF)
{
printf("%X/n", c); 
}
因为文本文件中存储的是ASCII码,而ASCII码中FF代表空值(blank),一般不使用,所以如果读文件返回了FF,说明已经到了文本文件的结尾。但是如果是二进制文件,其中可能会包含FF,因此不能把读到EOF作为文件结束的条件,此时只能用feof()函数。

在VC里,只有当文件位置指针(fp->_ptr)到了文件末尾,然后再发生读/写操作时,标志位(fp->_flag)才会被置为含有_IOEOF。然后再调用feof(),才会得到文件结束的信息。

对于feof()这个函数, 它是先读再判断是否到文件尾, 也就是说在它之前一定要读一次才能做出判断。

因此,如果运行如下程序:
char c;
while(!feof(fp))
{
c = fgetc(fp);
printf("%X/n", c); 
}
会发现多输出了一个FF,原因就是在读完最后一个字符后,fp->flag仍然没有被置为_IOEOF,因而feof()仍然没有探测到文件结尾。直到再次调用fgetc()执行读操作,feof()才能探测到文件结尾。这样就多输出了一个-1(即FF)。它是先判断==>再读(可能这时就是文件尾了, 读不出东西了)

正确的写法应该是:
char c;
c = fgetc(fp);
while(!feof(fp))
{
printf("%X/n", c); 
c = fgetc(fp);
}

第二种方法就是:(不使用feof,换种方法。)

1、把文件内部指针移动到文件尾部。
fseek(fp,0,2);
2、用一个整形变量记录这个文件尾部的位置
wjcd = ftell(fp);
 
3、把文件内部指针移到到文件头部;
fseek(fp,0,0);
 
4、这样就可以了
while(wjcd == ftell(fp))

注: 
1)  欲将读写位置移动到文件开头时:fseek(FILE  *stream,0,SEEK_SET);

2)   欲将读写位置移动到文件尾时:fseek(FILE  *stream,0,SEEK_END);

参数SEEK_SET是从距文件开头offset位移量为新的读写位置;SEEK_CUR是以目前的读写位置往后增加offset个位移量;SEEK_END将读写位置指向文件尾后再增加offset个位移量。当whence值为SEEK_CUR或SEEK_END时,参数offset允许负值的出现。

#include <stdio.h>
#include <stdlib.h> void process(char *filename, int n); int main(void)
{
char *filename = "file.c";
int n = ;
process(filename, n);
return ;
} void process(char *filename, int n)
{
FILE *fp1, *fp2;
char ch, pre;
int flag = ;
int i, j;
if((fp1 = fopen(filename, "rb")) == NULL)
{
printf("Cannot open this file1.\n");
exit();
}
if((fp2 = fopen("temp.c", "wb")) == NULL)
{
printf("Cannot open this file2.\n");
exit();
} pre = '\0';
ch = fgetc(fp1);
while(!feof(fp1))
{
if(ch == '/' && pre == '/')
{
while(!feof(fp1))
{
pre = ch;
ch = fgetc(fp1);
if(ch == '\n')
{
ch = fgetc(fp1);
pre = '\n';
break;
}
}
} if(ch == '*' && pre == '/')
{
while(!feof(fp1))
{
pre = ch;
ch = fgetc(fp1);
if(ch == '/' && pre == '*')
{
ch = fgetc(fp1);
pre = '\0';
break;
}
}
} if(ch == '"')
{
while(!feof(fp1))
{
fputc(pre, fp2);
putchar(pre);
pre = ch;
ch = fgetc(fp1);
if(ch == '"')
break;
}
}
if(ch == ' ' && pre == ' ')
{
while(!feof(fp1))
{
ch = fgetc(fp1);
if(ch != ' ')
{
break;
} }
} fputc(pre, fp2);
putchar(pre);
pre = ch;
ch = fgetc(fp1);
} // remove(filename);
// rename("temp.c", filename);
fclose(fp1);
fclose(fp2);
}

【c的文件操作】文本文件和二进制文件(内存映像)的不同 文件结尾判断feof , EOF的更多相关文章

  1. YTU 2925: 文件操作--文本文件读入

    2925: 文件操作--文本文件读入 时间限制: 1 Sec  内存限制: 128 MB 提交: 38  解决: 16 题目描述 现有100名学生的姓名(name).学号(num).英语(Englis ...

  2. C++入门到理解之文件操作(文本文件的读写+二进制文件的读写)

    原文地址http://www.javayihao.top/detail/168 一:概述 1.程序在运行中产生的数据都是临时数据,程序一旦运行结束会被释放,可以通过文件相关的操作将数据持久保存. 2. ...

  3. 128 C语言实现文件复制功能(包括文本文件和二进制文件)

    文件的复制是常用的功能,要求写一段代码,让用户输入要复制的文件以及新建的文件,然后对文件进行复制.能够复制的文件包括文本文件和二进制文件,你可以复制1G的电影,也可以复制1Byte的txt文档. 实现 ...

  4. C语言实现文件复制功能(包括文本文件和二进制文件)

    文件的复制是常用的功能,要求写一段代码,让用户输入要复制的文件以及新建的文件,然后对文件进行复制.能够复制的文件包括文本文件和二进制文件,你可以复制1G的电影,也可以复制1Byte的txt文档. 实现 ...

  5. c#文件操作

    1.创建文件夹 //using System.IO; Directory.CreateDirectory(%%1);   2.创建文件 //using System.IO; File.Create(% ...

  6. C++文件操作(fstream)

    C++ 通过以下几个类支持文件的输入输出: ofstream: 写操作(输出)的文件类 (由ostream引申而来) ifstream: 读操作(输入)的文件类(由istream引申而来) fstre ...

  7. C的文件操作

    文件文件的基本概念 所谓“文件”是指一组相关数据的有序集合. 这个数据集有一个名称,叫做文件名. 实际上在前面的各章中我们已经多次使用了文件,例如源程序文件.目标文件.可执行文件.库文件 (头文件)等 ...

  8. C# 文件操作大全

    1.创建文件夹//using System.IO;Directory.CreateDirectory(%%1); 2.创建文件//using System.IO;File.Create(%%1); 3 ...

  9. PHP文件操作 读取与写入

    基本知识: PHP文件系统是基于Unix系统的 文件数据基本类型:二进制数据.文本数据 文件输入流:数据从源文件到内存的流动 文件输出流:数据从内存保存到文件的流动 文件操作函数: >>& ...

随机推荐

  1. 三分钟学会.NET微服务之Polly

    熔断降级是一个非常重要的概念,我们先说一下什么是熔断降级,咱们都知道服务发现,一个有问题的服务器没来得急注销过一会就崩溃掉了,那么我们的请求就有可能访问一个已经崩溃的服务器,那么就会请求失败,因为已经 ...

  2. FragmentTabHostBottomDemo【FragmentTabHost + Fragment实现底部选项卡】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 使用FragmentTabHost实现底部选项卡效果. 备注:该Demo主要是演示FragmentTabHost的一些设置和部分功能 ...

  3. 深入解析ThreadLocal 详解、实现原理、使用场景方法以及内存泄漏防范 多线程中篇(十七)

    简介 从名称看,ThreadLocal 也就是thread和local的组合,也就是一个thread有一个local的变量副本 ThreadLocal提供了线程的本地副本,也就是说每个线程将会拥有一个 ...

  4. js事件循环机制辨析

     对于新接触js语言的人来说,最令人困惑的大概就是事件循环机制了.最开始这也困惑了我好久,花了我几个月时间通过书本,打代码,查阅资料不停地渐进地理解他.接下来我想要和大家分享一下,虽然可能有些许错误的 ...

  5. java基础(十)-----Java 序列化的高级认识

    将 Java 对象序列化为二进制文件的 Java 序列化技术是 Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现 Serializable 接口,使用 ...

  6. Memcached的安装配置与测试

    https://www.cnblogs.com/edisonchou/p/3855969.html

  7. Notepad++替换SQL Server Select窗口列名的中括号的小技巧

    条件:“查找模式”那选中“扩展” 一.简单替换 查找目标(包括空格,各个SSMS版本可能不同): ]\r\n ,[ 替换为:, 二.替换为@ 查找目标(包括空格,各个SSMS版本可能不同): ]\r\ ...

  8. Java 中的几种线程池,你之前用对了吗

    好久不发文章了,难道是因为忙,其实是因为懒.这是一篇关于线程池使用和基本原理的科普水文,如果你经常用到线程池,不知道你的用法标准不标准,是否有隐藏的 OOM 风险.不经常用线程池的同学,还有对几种线程 ...

  9. Centos 7.6搭建Tomcat 环境,发布Java项目

    安装 JDK Tomcat 的安装依赖 JDK,在安装 Tomcat 之前需要先安装 Java JDK.输入命令 java -version,如果显示 JDK 版本,证明已经安装了 JDK java ...

  10. 80后程序员降薪6K,预感中年危机来袭,准备跳槽却碰壁

    一提及程序员,很多人想到的都是“工资高”“技术好”诸如此类的,可见程序员是个非常赚钱的职业,所以每年都会有很多毕业生来选择这个行业. 但是社会是公平的,不要只看程序员表面上的光鲜亮丽,其背后也有很多的 ...