4

我正在尝试使用 Python 从现有文件中提取多个位置。这是我当前用于提取位置的代码:

    self.fh = open( fileName , "r+")
    p = re.compile('regGen regPorSnip begin')
    for line in self.fh :
        if ( p.search(line) ):
            self.porSnipStartFPtr = self.fh.tell()
            sys.stdout.write("found regPorSnip")

这段代码使用不同的搜索值重复了多次(打开的文件更少),并且似乎有效:我得到了正确的消息,并且变量具有值。

但是,使用下面的代码,第一个写入位置是错误的,而随后的写入位置是正确的:

    self.fh.seek(self.rstSnipStartFPtr,0)
    self.fh.write(str);
    sys.stdout.write("writing %s" % str )
    self.rstSnipStartFPtr = self.fh.tell()

我已经读过,由于 Python 倾向于“提前阅读” ,传递某些read/readline选项可能会导致错误的告诉值。fh我看到的一个避免这种情况的建议是读取整个文件并重写它,这在我的应用程序中不是一个非常吸引人的解决方案。

如果我将第一个代码段更改为:

  for line in self.fh.read() :
        if ( p.search(line) ):
            self.porSnipStartFPtr = self.fh.tell()
            sys.stdout.write("found regPorSnip")

然后它似乎self.fh.read()只返回字符而不是整行。搜索永远不会匹配。对于self.fh.readline().

我的结论是,fh.tell只有在写操作后查询时才返回有效的文件位置。

有没有办法在阅读/搜索时提取准确的文件位置?

谢谢。

4

2 回答 2

12

文件对象方法的文档中解释了原因(相当模糊)next()

当文件用作迭代器时,通常在 for 循环中(例如,for line in f: print line),next() 方法会被重复调用。此方法返回下一个输入行,或在 EOF 被命中时引发 StopIteration。为了使 for 循环成为循环文件行的最有效方式(一种非常常见的操作),next() 方法使用隐藏的预读缓冲区。作为使用预读缓冲区的结果,将 next() 与其他文件方法(如 readline())结合起来无法正常工作。但是,使用 seek() 将文件重新定位到绝对位置将刷新预读缓冲区。

返回的值tell()反映了这个隐藏的预读缓冲区已经到达了多远,这通常比程序实际检索到的字符多出几千字节。

没有可移植的方法解决这个问题。如果您需要tell()与阅读线混合,请改用文件的readline()方法。权衡是,作为获得可用tell()结果的回报,迭代一个大文件readline()通常比使用for line in file_object:.

代码

具体来说,将循环更改为:

line = self.fh.readline()
while line:
    if p.search(line):
        self.porSnipStartFPtr = self.fh.tell()
        sys.stdout.write("found regPorSnip")
    line = fh.readline()

不过,我不确定这是否是您真正想要的: tell()正在捕获下一行开头的位置。如果想要行首的位置则需要更改逻辑,如下所示:

pos = self.fh.tell()
line = self.fh.readline()
while line:
    if p.search(line):
        self.porSnipStartFPtr = pos
        sys.stdout.write("found regPorSnip")
    pos = self.fh.tell()
    line = fh.readline()

或者用“循环半”来做:

while True:
    pos = self.fh.tell()
    line = self.fh.readline()
    if not line:
        break
    if p.search(line):
        self.porSnipStartFPtr = pos
        sys.stdout.write("found regPorSnip")
于 2013-11-01T16:37:20.613 回答
0

我想我不明白这个问题

>>> fh = open('test.txt')
>>> fh.tell()
0L
>>> fh.read(1)
'"'
>>> fh.tell()
1L
>>> fh.read(5)
'a" \n"'
>>> fh.tell()
7L
于 2013-11-01T16:24:04.510 回答