关于读超大文件的问题

2014-12-27 18:47:07 +08:00
 JoshOY

今天碰到几个学长在做数据仓库,需要把一个8GB的txt文件读出来处理,文件有7000多万行。
他们用C++写,然后碰到了问题:文件打开后while(!f.eof()){...}只读取了10万多行就停了。

于是我写了个python脚本,f=open('xxx.txt', 'r')发现同样只能读10万多行。
然后google了一下,看到StackOverflow上有人说用二进制打开,于是尝试了f=open('xxx.txt', 'rb')结果全读出来了…然后让他们用C++试试二进制打开于是也成功了。

请问其中的原理是什么?
本人当时用的是windows,话说unix下会不会有同样的情况出现?

5322 次点击
所在节点    Python
36 条回复
lululau
2014-12-27 19:23:09 +08:00
Unix 不区分文本和二进制
lsylsy2
2014-12-27 19:32:59 +08:00
8GB的话,会不会有32位的问题?
9hills
2014-12-27 19:35:04 +08:00
一般来说是你这个文本文件有问题,内部有了一个EOF
BGLL
2014-12-27 19:40:32 +08:00
是不是文本里有\0
JoshOY
2014-12-27 19:51:58 +08:00
http://stackoverflow.com/questions/9905874/python-does-not-read-entire-text-file

呃,我找了一下当时看的应该是这篇。
回复说:
According to the docs, text mode vs. binary mode only has an impact on end-of-line characters. But (if I remember correctly) I believe opening files in text mode on Windows also does something with EOF (hex 1A).
难道是windows的原因导致读取时文本内出现了EOF?
jox
2014-12-27 20:01:58 +08:00
你在什么系统读的这个文件?这个文件又是在什么系统生成的? 不同的操作系统使用不同的字符来标记文本文件的行,如果这个文件是在windows系统下生成的,你在linux系统下使用文本模式来打开这个文件会遇到这种问题,你试试先转换成当前操作系统的格式,然后再尝试使用文本模式打开,txt只是windows下一个文本处理软件生成的文件带的扩展名,这样的一个文件与linux下的text file是不同的,'r'模式只是按照当前操作系统对于行结束的定义返回\n


另外EOF并不是一个文件中实际存在的字符,现代C在打开文件的时候会在读完所有的字符之后返回一个EOF值,并不是说C读到了一个EOF字符。

看看这个: http://c-faq.com/stdio/textvsbinary.html
JoshOY
2014-12-27 20:38:49 +08:00
@jox 非常感谢 我找了找其他的文章 应该是只有windows会出这种问题,。

确实和EOF没什么关系,应该是遇到0之类的特殊字符导致的。
UNIX下没有问题。
BGLL
2014-12-27 20:42:02 +08:00
@jox
但是在文本模式中遇到SUB(ASCII 26)字符会当作文本结束
BGLL
2014-12-27 20:43:49 +08:00
@JoshOY
你应该 找到断点位置,看看断点是什么
jox
2014-12-27 20:47:15 +08:00
@BGLL 不同的操作系统有不同的实现,以前有系统会使用某个特殊的字符来标记文件的结束,现在只是读完所以可读字符之后返回一个数值或者一个特殊字符而已,文件中并不存在实际的EOF字符。
mringg
2014-12-27 20:51:27 +08:00
设条件断点,非得把这个问题弄明白!!!!!
BGLL
2014-12-27 21:04:41 +08:00
@jox EOF只是找到末尾后返回的“信号”,我说的是那个末尾
你说的“以前的系统”就是Windows,所有Windows中SUB(ASCII 26)都会被当作文本末尾
jox
2014-12-27 21:14:02 +08:00
@BGLL 现在windows也这么做么?这样做有缺陷啊,如果使用某个特殊字符来标记文件末尾,那么就不能向文件中输入这个字符了。现在的操作系统会单独保存一个文件的meta数据,可读字符有多少,文件大小之类的,我的理解是现代C在读文件的时候是根据这个meta数据来确定是否到达文件尾或者读完了所有可读字符(有的时候可读字符数并不一定等于文件实际的大小)
BGLL
2014-12-27 21:33:52 +08:00
@jox
没错,所有的,包括现在的Windows8.1,SUB(ASCII 26)都是被当作文本末尾的
以标准的文本模式打开文件遇到SUB(ASCII 26)就会被当作结尾了,以二进制文件模式打开就不会有这个问题。

记录文件大小那是磁盘系统的事情了。
jox
2014-12-27 21:47:25 +08:00
@BGLL 这样啊,我的意思是在读文件的时候去读文件的meta数据,然后根据这个来判断是否到达了文件尾。

如果以二进制模式打开文件,那程序要如何判断是否到达了文件尾?
Neveroldmilk
2014-12-27 22:25:38 +08:00
二进制文件的末尾应该就是文件流的末尾,没有字符了吧。
jox
2014-12-27 22:57:45 +08:00
我发现我也没有搞清楚这个问题,刚刚看了一下相关的资料,python的话,因为好多python实现都是C写的,可以认为在I/O这方面跟C是一样的,只考虑Unix类的系统的话,需要借助系统调用才能确定达到了end of file状态,当达到end of file的时候会返回宏EOF,Unix下的rb模式和r是一样的,根据fopen的手册,b只是为了兼容ISO/IEC 9899:1990 (``ISO C90'')标准:

>> The mode string can also include the letter ``b'' either as last character or as a character
>> between the characters in any of the two-character strings described above. This is strictly
>> for compatibility with ISO/IEC 9899:1990 (``ISO C90'') and has no effect; the ``b'' is ignored.
BGLL
2014-12-27 23:02:35 +08:00
@jox

系统从磁盘系统得到文件就知道文件首尾指针了。
具体怎么实现是磁盘系统的事了,程序不用管,大多数是把文件尺寸放在文件分配表里(FAT)。

以文本模式打开文件是特别的存在,有这个模式主要为了兼容性(该死的换行符,就因为这个换行符耗费了多少人的生命)顺便方便处理简单的文本。DOS特别为了兼容过去的CP / M系统(它的磁盘系统没法精确定位文件结束位置,因为它的记录文件大小只到扇区,一个扇区128字节,一个文件的结束可能在第1个字节也可以在第111字节,所以要一个结束符标记(关键他磁盘系统不把这个操作封装起来,交给人们在文件末尾写额外标识符))
然后Windows为了兼容DOS......
jox
2014-12-27 23:11:44 +08:00
@BGLL 现在的windows系统的内核不再是DOS了吧?

我感觉楼主的这个问题引出的关于文件系统的问题对我来说要想彻底整明白暂时知识储备不够。。。。你说的这些我也看不太懂。。。。不过还是很感谢分享信息
BGLL
2014-12-27 23:21:05 +08:00
@jox
跟内核是什么没关系,只是为了兼容过去的标准,微软特别注重兼容性(比如为了兼容CR、LF两种换行标准,windows下换行符直接就是CR+LF)。

简单的来说,程序从系统得到文件就是文件起始指针和结束指针,Windows下为了兼容性在文本模式时特别的把ASCII 26的位置当结束位置了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/157187

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX