Python读取1.5GB+文件遇MemoryError,如何实现边读边拆分?
解决大文件读取MemoryError:边读边提取JPG的方案
嘿,这个坑我刚踩过!一次性把1.5GB的文件全塞进内存,内存直接顶爆太正常了——咱们换个思路,逐块读取文件,边读边找JPG的头尾标记,这样内存占用只会维持在你设置的块大小,完全不会爆。
先说说原代码的问题:tdata = f.read()会把整个文件加载到内存里,别说1.5GB,就算是几百MB的文件都可能出问题,完全没必要这么干。
下面是修改后的代码,我给你加了详细的注释,咱们一步一步看:
def extract_jpgs_from_large_file(input_path, chunk_size=1024*1024): # 注意:必须用bytes类型,和rb模式读取的文件内容匹配 jpg_start = b'\xff\xd8' jpg_end = b'\xff\xd9' count = 0 buffer = b'' # 用来处理跨块的标记(比如标记一半在上一块,一半在这一块) current_jpg = None # 记录正在提取的JPG数据 # 用with语句自动管理文件,不用手动close,更安全 with open(input_path, 'rb') as f: while True: # 每次读1MB的块(可以根据内存情况调整,比如2MB、4MB都可以) chunk = f.read(chunk_size) if not chunk: # 文件读完了,检查有没有还没写完的JPG if current_jpg is not None: count += 1 with open(f'extracted{count:03d}.jpg', 'wb') as fw: fw.write(current_jpg) break # 把新读的块加到缓冲区里 buffer += chunk # 循环处理缓冲区里的所有可能的JPG标记 while True: if current_jpg is None: # 还没找到JPG起始标记,先找开头 start_pos = buffer.find(jpg_start) if start_pos == -1: # 没找到起始标记,保留缓冲区最后1字节(防止标记跨块) buffer = buffer[-1:] break else: # 找到起始标记,开始收集JPG数据 current_jpg = buffer[start_pos:] # 缓冲区剩下的部分从起始标记之后开始 buffer = buffer[start_pos + len(jpg_start):] else: # 正在收集JPG数据,找结束标记 end_pos = buffer.find(jpg_end) if end_pos == -1: # 没找到结束标记,把缓冲区内容加到当前JPG,保留最后1字节防跨块 current_jpg += buffer buffer = buffer[-1:] break else: # 找到结束标记,完整提取一个JPG current_jpg += buffer[:end_pos + len(jpg_end)] count += 1 # 写入文件 with open(f'extracted{count:03d}.jpg', 'wb') as fw: fw.write(current_jpg) # 重置状态,处理下一个JPG current_jpg = None buffer = buffer[end_pos + len(jpg_end):] # 调用函数,传入你的大文件路径 extract_jpgs_from_large_file('thumbdata3.dat')
几个关键要点你得注意:
- Bytes类型匹配:原代码里的
ss = '\xff\xd8'是字符串,和rb模式读出来的bytes不兼容,必须改成b'\xff\xd8',不然根本找不到标记! - 缓冲区处理:JPG的头尾标记可能跨两个块(比如上一块末尾是
\xff,这一块开头是\xd8),所以要保留缓冲区的最后1字节,确保不会漏掉跨块的标记。 - 分块大小可调:
chunk_size默认是1MB,如果你内存够大,可以改成10MB,处理速度会更快;内存紧张的话,设成256KB也没问题。 - 文件名格式修正:原代码里的
extracted%d03.jpg是错误的,改成extracted%03d.jpg(或者用f-string的{count:03d}),生成的文件名会是extracted001.jpg、extracted002.jpg这样的有序格式。
如果运行过程中还有问题,比如提取的JPG打不开,大概率是标记匹配的问题,你可以打印一下缓冲区的内容排查,或者调整缓冲区保留的字节数(比如保留2字节,确保不会漏掉标记)。
内容的提问来源于stack exchange,提问作者ᾯᾯᾯ
相关产品推荐
相关产品推荐

