C++文件读入_最后一个数据后面的换行问题

来源:互联网 发布:tom大叔 javascript 编辑:程序博客网 时间:2024/05/15 01:43

  • 开场白
    • 文件输入格式
      • 回车和换行
      • 文件输入格式样例
    • 使用eof
  • 核心问题讨论
    • x是int类型变量
      • 方式一
      • 方式二
    • x是char类型变量
      • 方式一
      • 方式二
    • 使用fail
  • 写在最后
    • 关于写blog
    • 碎碎念

开场白

在C++中,使用ifstream文件输入流进行文件读取时,涉及到文件中的最后一个数据后面是否换行问题。

文件输入格式

回车和换行

为了更加严谨的表述,我们首先明确“回车”和“换行”的概念。

“回车”(Carriage Return)和“换行”(Line Feed)分别对应字符'\n''\r',对应的ASCII码分别为10和13,二者的意义从名字就可见一斑。“回车”是回到当前行的行首,而不会换到下一行,如果继续输出的话,当前行之前的内容会被依次覆盖;“换行”则是换到当前位置的下一行,而并不会回到行首。

直观的感受就是,Unix/Mac系统下的文件在Windows系统打开,所有文字会变成一行;而Windows系统下的文件在Unix/Mac系统打开,在每行的结尾可能会多出一个^M符号。

这是因为,在Unix系统里,每行结尾只有"<换行>",即'\n';Windows系统里面,每行结尾是"<回车><换行>",即"\r\n";Mac系统里,每行结尾是"<回车>",即'\r'。一个直接后果是,Unix/Mac系统下的文件在Windows里打开的话,所有文字会变成一行;而Windows里的文件在Unix/Mac下打开的话,在每行的结尾可能会多出一个^M符号。

我们讨论的是Unix系统里的文件输入,即每行结尾只有换行。

文件输入格式样例

考虑两种类型的文件输入格式,即“文件输入格式一”和“文件输入格式二”,二者的差异在于文件结束符之前有无换行符。至于数据之间的空白符是哪一种有几个或者是哪几种,都不是我们关心的,因为istream的成员函数中被重载的插入操作">>"的作用是“Extract formatted input”,所以在读入数据时,会“刻意地”按照数据类型读取。

文件输入格式一:

1 2 3 4\n

或者

1\n
2\n
3\n
4\n

文件输入格式二:

1 2 3 4

或者

1\n
2\n
3\n
4

所以,如前述,两种形式的输入文件格式的差异就在于最后一个数据之后有无换行符。

使用eof

接下来我们还要明确一个常量EOF。

End-of-File
It is a macro definition of type int that expands into a negative integral constant expression (generally, -1).
It is used as the value returned by several functions in header to indicate that the End-of-File has been reached or to signal some other failure conditions.

一个宏定义,值为-1,一些头文件指示文件的结束或者一些其他的failure情况。

调用ios类的成员函数eof实现

int ios::eof();

该函数返回0表示文件未结束(false)返回非0表示文件结束(true)。也就是判断是否遇到文件结尾的EOF标志。

举一个简单的例子,

ofstream out_file("./myfile",ios::out);out_file << "12"; out_file.close();int x;char ch1, ch2;ifstream in_file("./myfile",ios::in);/* * case 1: * in_file >> ch1 >> ch2; * 此时 in_file.eof() == 0; *//* * case 2: * in_file >> x; * 此时 in_file.eof() == 1; */in_file.close();

myfile文件已经写入了“12”,之后是一个默认的EOF。

如果文件输入流想要获取char类型的数据,只需读入一个字节即可,而不会关心下一个字符是什么,所以读取完两个字符‘1’和‘2’之后就完事大吉,你再问我文件读取结束没有,对不起我不知道,所以我告诉你没有结束,不代表文件确实没有读取结束,不代表文件还有东西可以读取,只能说是我不知道文件读取有没有结束罢了。

而如果文件输入流获取的数据类型是int,我们来思考一下读取的过程。先是‘1’,不错,但是文件输入流眉头一皱,发现事情并不简单,‘1’倒是可以滥竽充个数,算是个int类型,但是万一,万一人家不是个位数呢?所以继续看看吧……额……果然,还有一个‘2’,行吧……那“12”?显然不能这么轻率,再往后看看,这一看不要紧,发现了天大的秘密,‘2’后面确实没有其他数字可以一起凑成int类型变量了,但是却看到了EOF。虽说不到黄河心不死,但人家又不是故意的,我只是想看看还有没有同盟,谁知道这一下可是断了念想。现在你问我文件有没有读取结束,我还能说什么,当然是选择……告诉你文件读取结束了。

现在切入正题似乎有点无关紧要了,尴尬……

核心问题讨论

x是int类型变量

方式一

int x;in_file >> x;;while (!in_file.eof()) {    ...... //使用x的值       in_file >> x;}

当我们使用被赋值为3的变量x的时候,继续读下一个数据4,然后如果4后面什么都没有(文件输入格式二),那么直接就知道文件读完了,所以while循环的判断条件就不成立了,毕竟你就问我文件是不是读完了,我怎么管得着你是不是使用了读完的数据,所以4就不会被使用了,而如果4后面还有一个换行符(文件输入格式一),那么文件输入符读完4,一下子看到了空白符’\n’,也就不再越雷池了,所以while条件成立,傻傻地又进入了循环中,使用了4的值,却不再可以读取到其他数据,然后光明磊落地退出循环,功德圆满。

方式二

int x;while (!in_file.eof()) {    in_file >> x;    ...... //使用x的值}

至于这种方式,就很好理解啦,读完3,就使用3,然后判断文件没有结束(毕竟3和4之间有空白符),然后读取4,使用4,如果4之后有换行符(文件输入格式一),那可以说是很尴尬了,欺骗文件输入流,让人家以为没有结束,还稀里糊涂进入了while循环,不得不再使用一次变量4;要是4之后没有换行符(文件输入格式二),还是比较完美的,使用结束之后恰好知道文件读取结束,满分。

x是char类型变量

方式一

char x;in_file >> x;while (!in_file.eof()) {    ...... //使用x的值       in_file >> x;}

文件输入格式一和文件输入格式二均可,读完3,下一个是空白符,继续进入循环中,使用3,读入4,然后什么都不管,再进入,使用4,再一读,没了(空白符pass之后的文件结束符或者干脆直接撞到EOF身上),很好,循环结束,相安无事。

方式二

char x;while (!in_file.eof()) {    in_file >> x;    ...... //使用x的值}

还是直接考虑读取3之后,使用3,然后傻傻地进到循环中,读4,使用4,然后又一次傻傻地进到循环中,啥也没读到……再使用一次4:-),然后乖乖地退出循环。

使用fail

int x; //char xifstream in_file("./myfile",ios::in);in_file >> x; //首先读入第一个数据,到空白符就不再继续读while (!in_file.fail()) { //判断文件是否读取结束    ...... //使用x的值    in_file >> x;   }

当读入3之后,可以使用3,然后再读入4(与类型无关,读到的一定是4),这个时候我们应该搞清楚eof和fail的区别,

Reaching the End-of-File sets the eofbit. But note that operations that reach the End-of-File may also set the failbit if this makes them fail (thus setting both eofbit and failbit).

但是,

Returns true if either (or both) the failbit or the badbit error state flags is set for the stream.

就是说fail在意的是之前的读取有没有事与愿违,而eof则是考虑得比较长远。

所以既然读入了4,就一定不会将failbit置位(badbit暂时不考虑),所以不会fail,可以继续进入while循环。

这样看来,OJ当中还是最后一种方式最稳妥,甚至省略fail函数,直接while (cin >> x),代码 cin >> x之后返回是一个cin对象的引用,此时cin会测试是否达到文件末尾(设置eofbit)或者流出错(设置badbit或者failbit)。


写在最后

关于写blog

深谙自己不是一个灵光的人,而且大多数时候笨得出奇,无论是在智商上还是情商上。没有智慧,再加以懒惰的恶习,想必是无可救药了。一个学期大抵只写了两篇聊以自慰的blog,还都是请教大神的详实口供记录辅以一点自己的狗尾续貂。

刚刚还被问到我的田地还要不要继续种大葱,其实我也在纠结这个问题。我现在的想法是,大葱是一定要种的,但是微信公众号这个形式的局限性和受众使我有点犹豫。

于我而言,技术相关的blog可以有色彩,却很难有温度。

但往往最重要的不是景深,而是情深。

碎碎念

考试周结束后,我没有感受到一丝一毫的解脱,只知道我的to-do-list中优先级最高的一项被划掉之后,涌上来的是一波deadline,当考完最后一门专业课的当天下午,迫不得已推掉了之前计划好的出游,在机房从下午写到晚上,当机房晚上只剩下我一个人在跑dp的时候,能听到的只有窗外的雨点敲打空调的声音,很清晰,却很慵懒,夹杂着南京特有的闷热,压的我很难呼吸,很想哭,很想回家。

我不是不可以拒绝,我只是总是很怕错过。

中二的年纪已经过去,成长和蜕变意味着的也许是可以走更少的弯路吧,尽管我们都不愿承认,很少有人可以到达心中的耶路撒冷。

我喜欢霍炬和西乔,却又不是很喜欢,关于他们,就不在这里瞎扯啦。

阅读全文
2 0
原创粉丝点击