C++字符输入方式初探——为什么不能eof

C++以EOF结尾读取多行字符串 博文背景 一直想像cat >> hello.txt或者cat >> hello.txt <<- 'EOF'这个样子以EOF为结束符读取多行文本,搜了一些函数感觉都是曲线救国,不如自己从最基础的东西开始自己造。 具体操作 #include <iostream> using namespace std; int main() { char buff[1000]; int index=0; char temp; while(temp 阅读详情

前言

众所周知,C++的字符处理,尤其是输入输出,各种“读到文件结束”十分复杂。本文简单研究一下C++的各种字符输入方式。

由于C++提供了各种eof方法以供判断文件结尾,这就让人产生一种写类似这样代码的冲动:

while(eof){
    读入什么东西;
}
而它在绝大多数情况下都是不能正常工作的。如果你改成这样:

while(true){
    读入什么东西;
    if(eof) break;
}

就能正常运行了,但是看上去一点也不不优美酷炫装逼吊炸天。

本文的目的就是对C++中各种主流读入方式,研究第一种方法为什么不行。

注意:本文所有测试都是在Windows环境下进行的。

C风格

C++中,字符输入输出有两种风格:一种是继承于C的,cstdio库中包含的各函数,另一种是C++风格的,iostream/fstream。

在cplusplus.com中,可以查到cstdio的如下成员函数:

getchar();fgetc();getc();
gets();fgets();
scanf();fscanf();
fread();

各种getchar

这里有三个函数:getchar,fgetc和getc。其中,fgetc和getc等价,它们都相当于给定了输入文件指针的getchar。

getchar函数的返回值是一个int:如果它成功读取字符,这个int就等于读取字符的ASCII码,否则就是EOF。这个EOF是编译器用宏定义的一个常数,也就是-1.  “读取不成功”包含你能想到的各种情况:比如读完了文件的最后一个字符,或文件不存在,或文件为空。

如果用getchar读取文件,能否用eof(注意这是小eof,那个大EOF是一个宏定义的常量)判断文件是否结束呢?答案是不行。不妨做个实验:

    freopen("input.in","r",stdin);
    while(!feof(stdin)){
        cout<<(int)getchar()<<endl;
    }

这个feof是cstdio中的eof函数(判断文件是否结束),在这里一定不能用cin.eof(),它俩根本不是一个系统的。

这个原因是为什么呢?在cstdio中,判断eof的方式是设一个标志(end of file indicator),这个标志平时是0,而当你用任何方式试图去读“文件结尾再下一位”的时候,它就会变成一个正值,表明文件结束。但是,如果一直用getchar,那么它站在文件末尾的时候并不知道下一位还有没有东西,那就需要进行一次尝试,所以就读进来了一个-1.

参见文章:

当然,不能eof也不是什么大事:用-1判就行了嘛。本来getchar返回-1就是为这个目的而设计的。

在C++读入界,getchar就像一个香港记者:跑得比谁都快,(因此称霸OI选 手的快速读入模板),但读来读去,不做任何格式分析,too simple。想判空格?想判换行?客官您自己写去吧。

举个栗子:
    freopen("input.in","r",stdin);
    char c;
    while((c=getchar())!=-1){
        cout<<c;
    }

如此就可把一个文件里的所有字符从头到尾,原封不动地输出出来。

gets和fgets

不同于getchar/fgetc,fgets和gets有显著区别。

gets

gets的函数原型是:
char * gets ( char * str );

它的作用是:读取字符数组直到遇见一个回车('\n'),或读到了文件末尾。如果读取成功,它会把读取的东西放进str数组中(不包括最后可能的换行符,并在str的末尾添加一个'\0'),并返回str的指针。如果读取失败,str数组不变,并返回一个空指针(NULL)。如果这一行有回车,它就相应读入一个长度为0的字符数组。

那么,gets能不能用eof判文件结束呢?答案是:不太行。用下面的代码试一试:

    freopen("input.in","r",stdin);
    char s[100];
    char *s1;
    while(!feof(stdin)){
        s1=gets(s);
        printf("%d ",s1);
        cout<<s<<" "<<strlen(s)<<endl;
    }

你会发现:如果你的文件最后一行不是回车,那么它能正常工作。否则不行。

这个锅是这么产生的:当fgets读了最后一行,看到了回车,并发现后面没有的时候,就会设置eof标志 。而这一行的读入是否成功呢?如果这一行有字符,它就认为成功,否则(即最后一行是回车)失败。如果读取失败,那它就不会改变str的值——这就导致看上去,倒数第二行被读了两次。

当然,eof不靠谱,用靠谱的方法就是了——看看gets的返回值是不是NULL即可。如果你是一名经验丰富的膜法师,不妨用这个函数,无他,代码短尔。

fgets

fgets和原型是:
char * fgets ( char * str, int num, FILE * stream );

它比gets多了两个参数:num代表最多允许读取的位数,stream是文件指针。

fgets还有一个重大的不同:它会把回车(如果有的话)放在读入的字符数组后面。这就会造成一种尴尬的局面:最后一行的后面没有回车(如果它能被成功读入的话)。其他都和gets一样。

scanf和fscanf

这两个都是大家喜闻乐见的函数。fscanf就是scanf加一个文件指针,这里按下不表。

scanf的用法比较多样,这里主要关注两种:用它读"%s"(字符数组)和"%c"(字符)。

字符数组

scanf("%s",str); 这条命令意味着:一直读字符,直到遇到空格/tab/回车/文件结束。它返回一个整数:如果读取成功,就返回读取的字符数组个数(这里只打了一个%s,所以至多是1),如果读取失败,就返回常量EOF(也就是-1)。如果想用eof读到文件结尾,就会和gets遇到同样的问题:如果文件的最后一个字符是空格/tab/回车之一,最后一次读取就会挂掉。

不用eof,用返回-1这个标准判断就能搞定了,大家都说好。

字符

scanf("%c",&ch);这条命令等价于:ch=getchar(); 自然,试图用eof判结尾也会遇到getchar中出现的问题。

换言之,scanf一个字符和getchar的功能完全一样——而且它比getchar跑得慢。

那这个用法和咸鱼有什么区别呢?

完全没有区别,它就是咸鱼。

呃……其实还有点区别。你可以指定读几个字符,并且把传过去的&ch换成一个字符数组。这个用法比较生僻,就略过了。

fread

fread的函数原型是:

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

它的作用是一下把一整个文件(stream)读进数组ptr里。size是要读入的每个字符的大小(以byte记),而count是总共至多读多少个字符。它的返回值是一个非负整数:成功读入的字符个数。

这个函数一般不用,用法和getchar类似,好处也是跑得比谁都快,不过得预先开一个数组。 举个栗子:

    FILE *fin = fopen("input.in","r");
    char s[100]={0};
    cout<<fread(s,sizeof(char),100,fin)<<endl;
    cout<<strlen(s)<<endl;

C++风格

至于C++风格,就是大家喜闻乐见的,iostream和fstream全家桶。

首先明确一点:iostream和fstream都是继承istream类而来。换个容易理解的说法:cin和fin其实是一个东西。下面我们全部用cin为例。

研究一下istream类,其中能读东西的大概是这些函数:

cin>>yituodongxi;//一坨东西
cin.get();
cin.getline();
cin.read();

重载运算符

istream类型重载了>>(右移)运算符,使得你能够顺滑地干类似cin>>a;这样的事情。我们在这里讨论两种情况:读一个字符,和读一堆字符。

读一个

C++实现istream类型的方法比较复杂,它包装了一个streambuf类型,从而让它同时能读字符/字符串/字符数组。具体的操作细节不用关心,如果cin>>ch;(ch是char型),它做的事情是:读第一个非空格/tab/回车/文件结束的字符。

它和getchar都是“读一个字符”的方法,而如果妄图用cin.eof()判文件结尾,getchar的问题在这里同样会出现。更为坑爹的一点是:如果读不成,它会直接让你的ch保持原样,而非像getchar那样,给一个-1供人凭吊。在表面上看来,就是最后一个字符会重复读两遍。

举个栗子:

    freopen("input.in","r",stdin);
    char ch;
    while(!cin.eof()){
        cin>>ch;
        cout<<(int)ch<<" "<<cin.eof()<<endl;
    }

读一堆

如果你cin的是一个字符串/字符数组,那它的表现和scanf一个字符数组是类似的。如果文件的最后一个字符是空格/tab/回车之一,while(eof)就会失败。

get

cin.get()重载了好多种,不过都和之前我们研究过的函数等价:

int get();//这货就是getchar
istream& get (char& c);//这货相当于scanf("%c"),但读取失败会返回NULL
istream& get (char* s, streamsize n);//这货相当于scanf("%s"),同样读取失败返回NULL
istream& get (char* s, streamsize n, char delim);//和楼上类似但可以自定义分隔符
istream& get (streambuf& sb);//能兼容上面几种
istream& get (streambuf& sb, char delim);//和楼上类似但可以自定义分隔符

getline

cin.getline()有两种:

istream& getline (char* s, streamsize n );
istream& getline (char* s, streamsize n, char delim );//能自定义分隔符

它其实和gets一样(但你得指定最大读取长度n),试图用while(eof)时遇到的问题也和gets一样:如果文件的最后一个字符是回车,就会挂掉。

举个栗子:
    freopen("input.in","r",stdin);
    char ch[100];
    while(!cin.eof()){
        cin.getline(ch,100);
        cout<<strlen(ch)<<" "<<cin.eof()<<endl;
    }

read

其实就是fread的C++版本啦。毫无保留地把所有字符读进来,从头读到尾。不过用法简洁一些,省去了sizeof(char)。

举个栗子:

    freopen("input.in","r",stdin);
    char str[100];
    cin.read(str,100);
    cout<<strlen(str)<<endl;

总结

为什么while(eof)这种写法不能正常运行呢?简而言之,分为两种:读单个字符的方法,原因是在“无法预知未来”;读一串字符的方法,原因是“无法定义失败”。好像突然就哲学起来了……(逃

我们总结出如下这张表格,作为参考:

函数名读取个数分隔符eof什么时候失败读取失败的特征
getchar/fgetc/getc一个无永远返回-1
gets/fgets多个回车('\n')文件末尾是分隔符返回NULL
scanf/fscanf "%c"一个无永远返回-1
scanf/fscanf "%s"多个空格/tab/回车文件末尾是分隔符返回-1
fread多个无无无
cin>>ch一个空格/tab/回车永远返回NULL
cin>>str多个空格/tab/回车文件末尾是分隔符返回NULL
cin.get()一个无永远返回-1
cin.get(ch)一个无永远返回NULL
cin.get(str)多个空格/tab/回车文件末尾是分隔符返回NULL
cin.getline多个回车('\n')文件末尾是分隔符返回NULL
cin.read多个无无无


关于EOF的那点事 介绍 初学C++,C语言的程序员在优快云、SegmentFault上提的最多的问题便是 如何在读取用户输入或读取文件时处理EOF(End-of-file,文件结尾)条件? 然而,几乎所有的此类问题都体现了提问者对EOF这个概念的完全误解。本文章试着解释这个令人迷惑的问题。 辟谣!EOF字符 初学者对EOF的第一个误解就是“EOF字符” — 简单地说,总有人觉得EOF是一个字符,实际上“EOF... 阅读详情

相关推荐

国产FPGA开发实战:紫光同创编程软件中的约束文件详解与常见问题

本文详细解析了紫光同创FPGA编程软件中约束文件的关键配置技巧与常见问题。通过基础架构解析、时钟约束实践、物理约束调控及典型问题排查指南,帮助开发者高效掌握国产FPGA开发中的约束文件应用,提升开发效率与设计稳定性。

gin88的博客 909

C++的文件结尾EOF

C++的文件结尾EOF

little_startoo的博客 2989

Cadence Sigrity PowerDC实战:如何用PCB电热混合仿真解决散热难题(附详细配置步骤)

本文详细介绍了如何利用Cadence Sigrity PowerDC进行PCB电热混合仿真,以解决高功率密度设计中的散热难题。文章通过解析电热耦合原理,并附上详细的配置步骤与实战技巧,指导硬件工程师同步优化电气性能与热管理,从而在设计源头提升产品可靠性与效率。

weixin_29292535的博客 197

EOF是什么?

原文链接      作者: 阮一峰      日期: 2011年11月12日  我学习C语言的时候,遇到的一个问题就是EOF。 它是end of file的缩写,表示"文字流"(stream)的结尾。这里的"文字流",可以是文件(file),也可以是标准输入(stdin)。 比如,下面这段代码就表示,如果不是文件结尾,就把文件的内容复制到屏幕上

Turing@XDU的专栏 3769

医学图像处理实战:3D Slicer从安装到病灶分析全流程指南

本文提供了一份详尽的3D Slicer实战指南,涵盖从软件安装、DICOM数据导入到图像增强、病灶分割及三维可视化的全流程。重点演示了如何利用阈值分割、区域生长等核心图像处理技术进行病灶分析,并生成定量报告,是医学影像分析初学者的实用入门教程。

weixin_29278805的博客 601

解决 maven编译项目-Fatal error compiling: 无效的目标发行版: 21 -> [Help 1]

惊奇的发现,maven没有用环境变量中设置好的JAVA_HOME目录,至此发现错误原因,原来执行java -version默认使用系统环境变量JAVA_HOME,而maven优先使用了windows的用户变量JAVA_HOME(这里之前忘记修改,仍然指向java8路径),修改用户变量JAVA_HOME指向java21或删除该用户变量JAVA_HOME(如无它用)后即可。打开maven包所在的目录,在maven的配置文件 mvn.cmd 文件中,设置java_home的目录。

litlit023的专栏 1799

C++——EOF是个什么东西?

EOF是end of file的缩写,表示”文字流”(stream)的结尾。这里的”文字流”,可以是文件(file),也可以是标准输入(stdin)。 EOF不是特殊字符,而是一个定义在头文件stdio.h的常量,一般等于-1。#define EOF (-1) 除了表示文件结尾,EOF还可以表示标准输入的结尾。但是,标准输入与文件不一样,无法事先知道输入的长度,必须手动输入一个字符,表示到达EOF。

Learn From The Masters 3万+

万字详解YOLOv8网络结构Backbone/neck/head以及Conv、Bottleneck、C2f、SPPF、Detect等模块

YOLOv8是由Ultralytics开发的最先进的目标检测模型,推升了速度、准确性和用户友好性的界限。YOLO这一缩写代表“你只看一次”(You Only Look Once),通过在一次网络传递中同时预测所有边界框,提升了算法的效率和实时处理能力。相比之下,其他一些目标检测技术需要经过多个阶段或过程来完成检测。YOLOv8在流行的YOLOv5架构上进行了扩展,在多个方面提供了改进。

专注于图像领域,主要研究内容包括计算机视觉和深度学习,特别是在图像分类、目标检测和图像生成等方面有深入的研究和实践经验。 14万+

C / C++ - EOF

C / C++ - EOF 1. End-of-File,EOF It is a macro definition of type int that expands into a negative integral constant expression (generally, -1). EOF 是一个 int 类型的宏定义,它扩展为负整数常量表达式 (通常为 -1)。 It is used as the value returned by several functions in header <c

程永强 1464

Openclaw第3章:进阶部署

本章面向需要在特定环境中稳定运行 OpenClaw 的用户,介绍 Windows、Linux、Docker 以及 Cloudflare Workers 四种进阶部署方式。每种方式各有侧重:Windows 部署适合桌面用户日常使用,Linux 部署适合开发者,Docker 部署适合追求环境隔离的运维场景,Cloudflare Workers 部署则适合希望获得全球边缘网络加速的用户。读者可根据自身环境和需求选择合适的方案,完成本章后可继续阅读第4章了解维护升级流程。

Maynor的博客 1295

第1章 机器人SLAM算法工程师主要做什么的?(附SLAM学习路线及资料推荐)

对于想从事机器人与自动驾驶领域的在校大学生、研究生来说,建图定位算法工程师是一个极具发展潜力的岗位,而SLAM知识体系则是胜任该岗位的核心支撑。从基础的计算机工具、C++语言,到核心的开源框架、数学知识,再到硬件衔接与项目实践,每一个环节都不可或缺。建议大家结合自身兴趣确定侧重方向(如激光SLAM、融合定位),先通过开源项目建立实践认知,再补全理论知识,同时积极参与社区交流。

行知SLAM 953

关于c/c++语言的EOF

EOF是指文件的结束符,是一个宏定义 用cin.get()、getchar()可以在不了解其它输入/输出知识的情况下编写出 数量惊人的有用的代码 第一种做法: #include #include using namespace std; int main() { char c; while(scanf("%c",&c) !=EOF) { printf("%c",c)

qq_2896431151的博客 2370

CTK Plugin Framework插件框架学习1--Qt编译CTK

【代码】Qt编译CTK。

贝勒里恩的博客 7433

C++字符输入方式详解

本文提到的输入方式有:cin cin.get cin.getline gets 一、cin (1)使用cin输入时要包含头文件iostream。 (2)它从标准输入流中读取对应数据类型的数据,并自动判断读入数据是否有效,可用cin.good()或者cin.fail()来判断 (3)cin语句执行完后不会舍弃最后一个结束符(tab键、空格键、回车键),它被留在缓冲区。 例证 #include <iostream> using namespace std; int main() {

weixin_61343294的博客 1万+

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

C++ ifstream eof()的使用

C++ ifstream eof() 的使用 eof() 的使用方法1 ifstream.eof() 读到文件结束符时返回true。 大家可能有一个误区,认为读到文件结束符就是读到文件的最后一个字符。 其实不然,文件结束符是文件最后一个字符的下一个字符0xFF,eof() 读到文件结束符0xFF时返回true。 代码验证1 先来看看如下这段代码2: #include <iostream> #include <fstream> #include <string> using

Nine_CC的博客 1万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C/C++ ifstream之eof()

 用ifstream的eof(),竟然读到文件最后了,判断eof还为false。网上查找资料后,终于解决这个问题。参照文件:http://tuhao.blogbus.com/logs/21306687.html 在使用C/C++读文件的时候,一定都使用过eof()这个函数来判断文件是否为空或者是否读到文件结尾了,也会在使用这个函数的过程中遇到一些问题,如不能准确的判断是否为空或者是

rebel_321的专栏 3万+

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

EOF在C++中的含义

1 EOF在C++中的定义为:const int EOF = -1; 2 它这么定义是有道理的: 3 举个例子来说 4 int ch; //注意:这里定义了ch是int型的,而不是char型的 5 while((ch = cin.get()) != EOF) 6 { 7 cout << ch; 8 } 9 我们都知道,cin.get()...

weixin_30408165的博客 1865

《C++杂记》循环终止输入方式(Ctrl+Z,Ctrl+C,Ctrl+D(EOF))

ctrl-c 发送 SIGINT 信号给前台进程组中的所有进程。常用于终止正在运行的程序。 ctrl-z 发送 SIGTSTP 信号给前台进程组中的所有进程,常用于挂起一个进程。 ctrl-d 不是发送信号,而是表示一个特殊的二进制值,表示 EOF。 接下来笔者通过不同的例子来说明这些组合键的差异。  字符串输入 #include &amp;amp;lt;iostream&amp;amp;gt; #inc...

不问归期的博客 1万+

C++eof()判断是否读取到文件尾

C++借助eof()函数,判断是否读到文件尾。

qq_62390970的博客 4792

c++中使用getchar读入字符串。使用EOF结束

是一个特殊的常量,通常用于判断输入是否已经结束。当你从文件或标准输入(比如键盘)读取数据时,如果到达了文件的末尾或者用户输入了结束信号(比如按下。是一种常见的方法,适用于需要逐个字符处理输入的场景。为了让大家更容易理解,我们可以简化代码,避免复杂的操作。会从标准输入读取一个字符,并返回其ASCII值。是一个标准库函数,用于从标准输入(通常是键盘)读取一个字符。就像是书的最后一页,当你读到最后一页时,就知道书已经读完了。在这个示例中,所有读取的字符都会被添加到。的缩写,表示文件的结束。

babyai996的博客 629

~scanf()和EOF

可以经常在ACM代码中看到 while(~scanf("%d",&n)){ } 这样的代码,意思是在读到输入结尾时循环也结束。 ~是按位取反 scanf的返回值是输入值的个数 如果没有输入值就是返回-1 -1按位取反结果是0 while(~scanf("%d", &n))就是当没有输入的时候退出循环 一般在读到输入流结尾时,scanf返回的是EOF。 EOF是在头文件stdio.h中定义的常量,一般为-1。 ~EOF则是对EOF进行按位取反操作,则为00000000。所以wh

guuuuug的博客 1598

C++ eof()函数相关应用技巧分享

C++编程语言中的很多功能在我们的实际应用中起着非常大的作用。比如在对文件文本的操作上,就可以用多种方式来实现。在这里我们介绍的C++ eof()函数就是其中一个比较常用的基本函数。 在使用C/C++读文件的时候,一定都使用过C++ eof()函数来判断文件是否为空或者是否读到文件结尾了,也会在使用这个函数的过程中遇到一些问题,如不能准确的判断是否为空或者是否到了文件尾,以至于有些人可能还会怀疑...

u011089523的博客 1562

C++ 读取单个字符

get() 是 istream 类的成员函数。 int get(); 此函数从输入流中读入一个字符,返回值就是该字符的 ASCII 码。如果碰到输入的末尾,则返回值为 EOF。 EOF 是 End of File 的缩写。istream 类中从输入流(包括文件)中读取数据的成员函数,在把输入数据都读取完后再进行读取,就会返回 EOF。EOF 是在 iostream 类中定义的一个整型常量,值为 ...

weixin_45743799的博客 7857

C++文件操作之eof函数问题

关于eof函数       fstream / ifstream / ofstream 类中的 成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();       在使用这个函数时,如执行下面代码: #include #include using namespace std; int main(){ o

pear的学习笔记 1万+

EOF的意义及用法

c/c++ 中EOF的意义及用法 EOF,为End Of File的缩写,通常在文本的最后存在此字符表示资料结束。 在微软的DOS和Windows中,读取数据时终端不会产生EOF。此时,应用程序知道数据源是一个终端(或者其它“字符设备”),并将一个已知的保留的字符或序列解释为文件结束的指明;最普遍地说,它是ASCII码中的替换字符(Control-Z,代...

Tsingke 2071

EOF的意义及用法(while(scanf("%d",&n) != EOF))

EOF,为End Of File的缩写,通常在文本的最后存在此字符表示资料结束。 在微软的DOS和Windows中,读取数据时终端不会产生EOF。此时,应用程序知道数据源是一个终端(或者其它“字符设备”),并将一个已知的保留的字符或序列解释为文件结束的指明;最普遍地说,它是ASCII码中的替换字符(Control-Z,代码26)。 在C语言中,或更精确地说成C标准函数库中表示文件结束符(end o...

大C的博客 31万+
上一篇: [CCPC2015][HDU5548]Mahjong解题报告
下一篇: 武器与战术:对聚变时代太空舰队的构想
wmdcstdio
博客等级 码龄12年 76粉丝 51原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值