Python 中 file.read() 的多字节请求 EOF
Python文档中关于 file.read() 的说明提到,当遇到文件结束(EOF)时,会返回一个空字符串。文档还进一步说明:
需要注意的是,这个方法可能会多次调用底层的C语言函数fread(),以尽量获取接近请求字节数的内容。另外,当处于非阻塞模式时,即使没有给出字节数参数,返回的数据量也可能少于请求的数量。
我相信Guido已经非常明确地表达了他对不添加f.eof()的看法 非常清楚,所以我们需要遵循Python的方式!
不过,对我来说不太清楚的是,如果从读取中得到的字节数少于请求的数量,但仍然得到了数据,这是否可以确定你已经到达了文件结束(EOF)。
也就是说:
with open(filename,'rb') as f:
while True:
s=f.read(size)
l=len(s)
if l==0:
break # it is clear that this is EOF...
if l<size:
break # ? Is receiving less than the request EOF???
如果在调用 file.read(size)
时收到的字节数少于请求的数量,直接 break
是否会导致潜在错误?
2 个回答
这是我C语言编译器文档中关于fread()
函数的说明:
size_t fread(
void *buffer,
size_t size,
size_t count,
FILE *stream
);
fread函数返回实际读取的完整项目数量,如果发生错误或者在达到指定数量之前遇到文件末尾,那么返回的数量可能会少于你要求的数量。
所以,如果得到的数量少于size
,这就意味着可能发生了错误,或者已经到达了文件的末尾——在这种情况下,break
退出循环是正确的做法。
你可能没有意识到,Python和C语言是完全不同的。
首先,来回顾一下:
- st=f.read() 会读取文件直到结束,或者如果以二进制方式打开,则读取到最后一个字节;
- st=f.read(n) 尝试读取
n
个字节,但最多只会读取n
个字节; - st=f.readline() 一次读取一行,行的结束标志是 '\n' 或文件结束;
- st=f.readlines() 使用 readline() 来读取文件中的所有行,并返回一个包含这些行的列表。
如果文件读取方法到达了文件结束(EOF),它会返回 ''
。在其他类似文件的操作中,比如 StringIO、socket.makefile 等,也会使用相同的 EOF 测试。从 f.read(n)
返回的字节数少于 n
并不一定意味着到达了文件结束!虽然这段代码在99.99%的情况下可能有效,但在它不工作的情况下,找到问题会非常让人沮丧。而且,这种写法在 Python 中也不太好。这里 n
的唯一作用是限制返回值的大小。
那么,Python 的文件操作方法为什么会返回 少于 n
个字节呢?
- 文件结束(EOF)当然是一个常见原因;
- 网络套接字在读取时可能会超时,但仍然保持打开状态;
- 恰好
n
个字节可能会导致逻辑多字节字符之间的断裂(比如文本模式下的\r\n
,还有我认为的 Unicode 中的多字节字符)或一些你不知道的底层数据结构; - 文件处于非阻塞模式,另一个进程开始访问该文件;
- 暂时无法访问文件;
- 文件、磁盘、网络等出现了潜在的错误情况,可能是暂时的;
- 程序收到了一个信号,但信号处理程序忽略了它。
我会这样重写你的代码:
with open(filename,'rb') as f:
while True:
s=f.read(max_size)
if not s: break
# process the data in s...
或者,写一个 生成器:
def blocks(infile, bufsize=1024):
while True:
try:
data=infile.read(bufsize)
if data:
yield data
else:
break
except IOError as (errno, strerror):
print "I/O error({0}): {1}".format(errno, strerror)
break
f=open('somefile','rb')
for block in blocks(f,2**16):
# process a block that COULD be up to 65,536 bytes long