这个问题已经被问到here和here,但没有一个解决方案适合我。
如何在Python 3中有效地从大文件中删除第一个行?
我正在编写一个需要记录的程序,并且日志文件具有可配置的最大大小,这可能是无限的。因此,我不想使用readlines()
或类似的方法,因为这些将是内存密集型的。速度不是一个大问题,但如果可以完成重写整个文件,而没有临时文件,那就太好了。
解决方案需要跨平台。
示例日志文件:
[09:14:56 07/04/17] [INFO] foo
[23:45:01 07/04/17] [WARN] bar
[13:45:28 08/04/17] [INFO] foobar
... many thousands more lines
输出:
[23:45:01 07/04/17] [WARN] bar
[13:45:28 08/04/17] [INFO] foobar
... many thousands more lines
此代码将循环运行:
while os.path.getsize(LOGFILE) > MAXLOGSIZE:
# remove first line of file
以下解决方案均无法正常运行且具有内存效率:
解决方案#1 - 工作但效率低下
with open('file.txt', 'r') as fin:
data = fin.read().splitlines(True)
with open('file.txt', 'w') as fout:
fout.writelines(data[1:])
解决方案#2 - 不起作用,将文件留空
import shutil
source_file = open('file.txt', 'r')
source_file.readline()
target_file = open('file.txt', 'w')
shutil.copyfileobj(source_file, target_file)
解决方案#3 - 有效,但使用其他文件:
with open("file.txt",'r') as f:
with open("new_file.txt",'w') as f1:
f.next() # skip header line
for line in f:
f1.write(line)
答案 0 :(得分:1)
试试这个。如您所述,它使用第3种方法,但不会创建新文件。
@DataProvider
答案 1 :(得分:1)
所以,这种方法非常黑客。如果您的线尺寸大小相同且标准偏差很小,它将很有效。我的想法是将文件的某些部分读入缓冲区,该缓冲区小到足以提高内存效率但足够大,以至于写入两端都不会搞砸(因为线条大小相同,几乎没有变化,我们可以交叉我们的手指,祈祷它会起作用)。我们基本上跟踪文件中的位置并来回跳转。我使用collections.deque
作为缓冲区,因为它从两端都有良好的append
性能,我们可以利用队列的FIFO特性:
from collections import deque
def efficient_dropfirst(f, dropfirst=1, buffersize=3):
f.seek(0)
buffer = deque()
tail_pos = 0
# these next two loops assume the file has many thousands of
# lines so we can safely drop and buffer the first few...
for _ in range(dropfirst):
f.readline()
for _ in range(buffersize):
buffer.append(f.readline())
line = f.readline()
while line:
buffer.append(line)
head_pos = f.tell()
f.seek(tail_pos)
tail_pos += f.write(buffer.popleft())
f.seek(head_pos)
line = f.readline()
f.seek(tail_pos)
# finally, clear out the buffer:
while buffer:
f.write(buffer.popleft())
f.truncate()
现在,让我们尝试使用一个表现良好的假装文件:
>>> s = """1. the quick
... 2. brown fox
... 3. jumped over
... 4. the lazy
... 5. black dog.
... 6. Old McDonald's
... 7. Had a farm
... 8. Eeyi Eeeyi Oh
... 9. And on this farm they had a
... 10. duck
... 11. eeeieeeiOH
... """
最后:
>>> import io
>>> with io.StringIO(s) as f: # we mock a file
... efficient_dropfirst(f)
... final = f.getvalue()
...
>>> print(final)
2. brown fox
3. jumped over
4. the lazy
5. black dog.
6. Old McDonald's
7. Had a farm
8. Eeyi Eeeyi Oh
9. And on this farm they had a
10. duck
11. eeeieeeiOH
如果dropfirst
< buffersize
有点"松弛"。由于您只想删除第一行,只需保留dropfirst=1
,然后您可以将buffersize=100
或其他内容设为安全。它将比读取数千行"更加节省内存,如果没有单行比前一行更大,那么你应该是安全的。但要注意,这是非常粗糙的边缘。