关于读超大文件的问题

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下会不会有同样的情况出现?

5369 次点击
所在节点    Python
36 条回复
jox
2014-12-27 23:25:55 +08:00
@BGLL 是windows这么实现的还是其他的系统也是这么做的?如果使用打开文件操作得到了两个指针,然后就不再检查是否达到文件尾了吗?如果一个程序在读一个文件的同时,另外一个程序在往这个文件写数据该怎么处理?
msg7086
2014-12-27 23:26:16 +08:00
@jox 简单说,以前的文件大小只能是128字节的整数。要终结一个文件,就必须在数据里加上这个^Z。
而后续所有的操作系统都必须兼容这个约定,否则就会破坏很多依赖这个约定的程序了。
msg7086
2014-12-27 23:29:45 +08:00
@jox 文本文件模式本来就是历史遗留问题。
如果一个文件是正确的文本文件,那里面就不应该存在^Z。
如果文件里有^Z而且你打算正确处理文件,那就不该以文本文件模式打开。
你说的这些本来就你有矛盾啊。
msg7086
2014-12-27 23:30:23 +08:00
@msg7086 s/就你有/就有/
jox
2014-12-27 23:44:41 +08:00
@msg7086 ??是不是回错人了?

按照windows的标准,如果有^Z字符出现在不是文件尾的地方,那这就不是个符合windows标准的文本文件,就要使用rb模式来打开,这可能是LZ遇到的问题。

我在考虑的是在Unix系统下,流程序如何确定它达到了文件尾,我考虑的是这里的实现细节,然后意识到这个问题并不是简单两句话能解释清楚的,于是就放弃了。
lululau
2014-12-27 23:49:22 +08:00
jox
2014-12-27 23:54:28 +08:00
@lululau 。。都是我已经知道的东西。。。。

我没想明白的是,feof这样的调用是如何实现的,多个文件同时对同一个文件读写这样的情况又是怎么处理的,之类的。
BGLL
2014-12-28 00:04:22 +08:00
@jox
@jox

现在主流系统就Windows还兼容这种老掉牙的规则了。

也就是说现在的系统下程序得到一个文件就从磁盘系统知道文件的精确到字节起始和结束位置了(也就是起始指针和结束指针),文件尾的定义不就是结束指针的位置吗。

同时写入读取不矛盾啊,当然没有绝对的同时,还是有先后顺序的。有时候会用因为条件限制会用一个文件进行程序间的通信。

Unix下也和Windows一样通过磁盘系统得到文件头、尾啊。



@msg7086 现在的系统里文件也一样,一个扇区也只能存一个文件,只是磁盘系统封装了记录找到文件尾的操作。
9hills
2014-12-28 00:11:17 +08:00
@jox 在Linux上,EOF其实就是一个宏: -1。由于以文本模式读文件出来的肯定是unsigned char,所以可以保证没有-1被读出来。这个-1是系统碰到文件结束后返回的(根据文件大小)

Windows上,有些特殊的动作,某个字符(上面有人说,但我记不清楚了)也可以充当EOF的作用,塞到文本中,用非二进制方式读到这里就断了。

所以lz的问题,应该在Linux上不会复现
9hills
2014-12-28 00:14:04 +08:00
@jox 文件头文件尾是文件系统中存储的,这个精确定位,不存在模糊。。。具体参见各种文件系统的实现。
jox
2014-12-28 00:14:09 +08:00
@BGLL 这位兄弟看来对操作系统很了解啊。

如果两个进程同时对一个文件进行读写,其中一个进程打开文件之后另一个进程又往这个文件里写入了一些数据,那第一个进程打开时的文件尾与第二个进程打开文件时的文件尾就不一样了,另外结束指针是什么?这个结束指针是怎么用的?通过这个指针能够得到文件当前的文件尾是什么?

Is it a thing like (int) or a (int *)? What is it?
jox
2014-12-28 00:15:09 +08:00
@9hills 我知道的。。。。。。。
denghongcai
2014-12-28 01:29:45 +08:00
实际上就是操作系统如何实现open这个系统调用,你可以看linux下open如何实现的。实际上fd里只有文件的偏移,没有尾指针,多个程序追加同一个文件是由文件系统实现的原子操作来保证一致性,而且这种追加要求open时以“a”的方式打开文件。
msg7086
2014-12-28 01:55:59 +08:00
@BGLL 一扇区只能存一文件这是文件系统的约定而不是公理。
先不说有些文件系统已经支持在inode之类的地方内嵌小文件了。我记得是有文件系统可以实现单个扇区多个文件的。
BGLL
2014-12-28 09:32:45 +08:00
@jox

是啊文件结尾会改变
C++里 ios::end 代表文件末尾指针,f.seekg(0,ios::end) 把读取指针定位到文件末尾指针。
文件改变了,取到的文件结尾也就不同。

你例子中的while(!f.eof()){...},要是读取中文件变长了会接着读到新结尾,变短了还是会读到原来长度(因为有缓存机制)

要完全实时读取,得每写一个字节重新向系统获取一次文件结尾。



@msg7086
你说是簇吧簇是文件系统最小分配空间,扇区是磁盘最小分配空间,由硬盘上的固件决定。
要一个扇区存储多个文件对于文件系统来说貌似要多很多操作步骤间接完成感觉性能上不划算啊,各种小众文件系统就不知道了,反正NTFS、Ext 二大文件系统是只能一个扇区写一个文件。“inode之类的地方内嵌小文件了"把连续数据块一起处理省略 inode 的 extents?LLinux的东西我不了解。
msg7086
2014-12-28 11:30:07 +08:00
@BGLL 是的主流文件系统是单个簇只放文件,主要是提高操作效率,但是并非是硬性规定。
典型的,路由器或者嵌入式设备通常会把文件紧凑存放以节约空间。
Linux下有在inode树内部内嵌文件的FS。没记错的话Reiser系就有这个习惯。

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

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

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

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

© 2021 V2EX