防止BufferedReader关闭文件以获取文件列表

sim*_*ack 1 python bufferedreader

我有一个扩展BufferedReader的类,并且文件流列表b.close()被除最后一个流之外的所有文件调用,我想保持流打开。我该怎么做?

谢谢

类TestReader(BufferedReader):
    通过

清晰度测试(流):
    用于流中的流:
        b = TestReader(流)
        do_something(b)
    #除了stream [-1]以外的所有流都关闭了,如何防止这种情况发生?

stream = [open('test1.txt','rb'),open('test2.txt','rb')]
测试(流)
stream.do_something_else()

Fra*_*ila 5

即使在实现BufferedIOBase类中包装IOBase对象,它们的接口也是一个流(一切都从继承IOBase),所以IOBase对象的常规行为是在超出范围时关闭自身。BufferedIOBase只是将close()调用委托给基础流。

您不应将a BufferedReader视为流包装器(尽管这是如何实现的),而应将其视为现有流的类型转换。 两个流的状态完全绑定在一起。但是,您可以将打包的流与解除绑定detach(),但这会使BufferedIOBase对象无用。

此外,当mode 为时,已经io.open返回一个,因此您要进行双缓冲。您应该改用。BufferedReaderrbio.FileIO

您有几种选择:

  1. 创建一个新的流和一个新的基础文件描述符,并传递文件名而不是流。这是您最简单,最安全的选择。

  2. 创建原始文件描述符,并根据需要从它们创建流。这需要格外小心,因为多个流不会同时使用同一文件描述符。例如:

    fd = os.open('test.txt', os.O_RDONLY)
    file1 = FileIO(fd, 'r', closefd=False)
    file2 = FileIO(fd, 'r', closefd=False)
    
    file1.read(100)
    assert file1.tell() == 100
    file2.read(100)
    assert file1.tell() == 200
    
    Run Code Online (Sandbox Code Playgroud)
  3. detach()BufferedIOBase对象关闭其流之前的基础流。(请记住倒带!)

    def test(streams):
        for stream in streams:
            b=TestReader(stream)
            do_something(b)
            wrappedstream = b.detach()
            assert wrappedstream is stream
    
    Run Code Online (Sandbox Code Playgroud)

    您甚至可以在析构函数中实现此目的:

    class TestReader(BufferedReader):
        def __del__(self):
            self.detach()
            # self.raw will not be closed,
            # rather left in the state it was in at detachment
    
    Run Code Online (Sandbox Code Playgroud)

    或者,close()如果您认为语义错误,则完全禁用委派:

    class TestReader(BufferedReader):
        def close(self):
            self.closed = True
    
    Run Code Online (Sandbox Code Playgroud)

我对自己的工作没有全面了解(可能需要其他设计),但是这就是我实现所看到的代码的方式:

from io import FileIO, BufferedReader
import io
import os

class TestReader(BufferedReader):
    pass

def test(streams):
    for stream in streams:
        b = TestReader(stream)

def test_reset(streams):
    """Will try to leave stream state unchanged"""
    for stream in streams:
        pos = stream.tell()
        b = TestReader(stream)
        do_something(b)
        b.detach()
        stream.seek(pos)



filenames = ['test1.txt', 'test2.txt']

# option 1: just make new streams

streams = [FileIO(name, 'r') for name in filenames]
test(streams)
streams = [io.open(name, 'rb') for name in filenames]
#etc


# option 2: use file descriptors
fds = [os.open(name, os.O_RDONLY) for name in filenames]
#closefd = False means "do not close fd on __del__ or __exit__"
#this is only an option when you pass a fd instead of a file name
streams = [FileIO(fd, 'r', closefd=False) for fd in fds]
test(streams)
streams = []
for fd in fds:
    os.lseek(fd, 0, os.SEEK_SET)
    streams.append(io.open(fd, 'rb', closefd=False))
    # you can also .seek(0) on the BufferedReader objects
    # instead of os.lseek on the fds


# option 3: detach

streams = [FileIO(name, 'r') for name in filenames]
test_reset(streams)
# streams[*] should still be in the same state as when you passed it in
Run Code Online (Sandbox Code Playgroud)