Python二进制文件EOF

17 投票
4 回答
33658 浏览
提问于 2025-04-18 18:18

我想要读取一个二进制文件。

我在网上搜索“python 二进制 文件结束符”,找到了这个链接

现在,我有几个问题:

  1. 为什么容器(在SO回答中提到的x)里不是一个单独的字节,而是一大堆字节?我哪里做错了吗?
  2. 如果这样是正常的,我没有做错,那我该怎么读取一个单独的字节呢?也就是说,有没有办法在用read(1)方法读取文件时检测到文件结束符?

4 个回答

1

我做了以下事情。当调用read时,如果遇到文件的结尾,它会返回一个“假”的值,这样就会结束循环。使用while ch != "":虽然可以复制图像,但却导致了一个死循环。

from sys import argv
donor = argv[1]
recipient = argv[2]
# read from donor and write into recipient
# with statement ends, file gets closed
with open(donor, "rb") as fp_in:
    with open(recipient, "wb") as fp_out:
        ch = fp_in.read(1)
        while ch:
            fp_out.write(ch)
            ch = fp_in.read(1)
1

逐字节读取:

with open(filename, 'rb') as f:
    while True:
        b = f.read(1)
        if not b:
            # eof
            break
        do_something(b)
4

"" 表示文件的结束

with open(filename, 'rb') as f:
    for ch in iter(lambda: f.read(1),""): # keep calling f.read(1) until end of the data
        print ch
19

引用一下文档中的内容:

file.read([size])

从文件中最多读取size个字节(如果在读取过程中遇到文件结束符(EOF),可能会少于这个数量)。如果没有提供size参数或者它是负数,就会一直读取,直到遇到文件结束符为止。读取的字节会作为字符串返回。当立即遇到文件结束符时,会返回一个空字符串。(对于某些文件,比如终端文件,继续读取在遇到文件结束符后是有意义的。)请注意,这个方法可能会多次调用底层的C函数fread(),以尽量获取接近size字节的内容。另外,当处于非阻塞模式时,即使没有给size参数,返回的数据也可能少于请求的数量。

这意味着(对于一个常规文件):

  • f.read(1)会返回一个字节对象,里面可能包含1个字节,或者如果遇到文件结束符则返回0个字节。
  • f.read(2)会返回一个字节对象,里面可能包含2个字节,或者如果在读取第一个字节后遇到文件结束符则返回1个字节,或者如果立即遇到文件结束符则返回0个字节。
  • ...

如果你想一次读取一个字节,就需要在循环中使用read(1)并检查结果是否为空:

# From answer by @Daniel
with open(filename, 'rb') as f:
    while True:
        b = f.read(1)
        if not b:
            # eof
            break
        do_something(b)

如果你想一次读取50个字节,就需要在循环中使用read(50)

with open(filename, 'rb') as f:
    while True:
        b = f.read(50)
        if not b:
            # eof
            break
        do_something(b) # <- be prepared to handle a last chunk of length < 50
                        #    if the file length *is not* a multiple of 50

实际上,你甚至可以提前结束一次循环:

with open(filename, 'rb') as f:
    while True:
        b = f.read(50)
        do_something(b) # <- be prepared to handle a last chunk of size 0
                        #    if the file length *is* a multiple of 50
                        #    (incl. 0 byte-length file!)
                        #    and be prepared to handle a last chunk of length < 50
                        #    if the file length *is not* a multiple of 50
        if len(b) < 50:
            break

关于你问题的另一部分:

为什么这个容器[..]包含了一大堆[字节]?

参考那段代码

for x in file:  
   i=i+1  
   print(x)  

再引用一下文档

文件对象本身就是一个迭代器,[..]。当文件作为迭代器使用时,通常是在一个for循环中(例如,for line in f: print line.strip()),会重复调用next()方法。这个方法返回下一行输入,或者在遇到文件结束符时抛出StopIteration(当文件以写入模式打开时,行为是未定义的)。

上面的代码逐行读取一个二进制文件。它在每次遇到行结束符(\n)时停止。通常,这会导致不同长度的块,因为大多数二进制文件中行结束符的出现是随机分布的。

我不建议你以这种方式读取二进制文件。请优先选择基于read(size)的解决方案。

撰写回答