我做了一个简单的请求代码,从服务器下载一个文件
r = requests.get("https:.../index_en.txt.lzma")
index_en= open('C:...index_en.txt.lzma','wb')
index_en.write(r.content)
index_en.close
当我现在用7zip在目录中手动解压缩文件时,一切正常,文件描述正常。
我尝试了两种方法在python程序中做到这一点,但由于文件以。lzma结尾,我猜下面的方法是一个更好的方法
import lzma
with open('C:...index_en.txt.lzma') as compressed:
print(compressed.readline)
with lzma.LZMAFile(compressed) as uncompressed:
for line in uncompressed:
print(line)
这个给了我错误:"压缩文件在到达流结束标记之前结束">
我尝试的第二种方法是7zip,因为手工操作效果很好
with py7zr.SevenZipFile("C:...index_en.txt.lzma", 'w') as archive:
archive.extract(path="C:...Json")
这个给了我错误:OSError 22无效参数在"与py7zr…"行
我真的不明白问题出在哪里。为什么它可以手工工作,而不是在python中?由于
您没有关闭文件,因此在文件在某个不确定的未来点被清理之前,用户模式缓冲区中的数据在磁盘上是不可见的(可能根本不会发生,也可能直到程序退出才发生,即使它退出了)。因此,任何试图通过除您写入的单个句柄以外的方式访问该文件的尝试都不会看到未刷新的数据,这将导致它看起来好像文件被截断了,从而得到您所观察到的错误。
最小的解决方案实际上是调用close
,将index_en.close
改为index_en.close()
。但实际上,只要可能,您应该对所有文件(以及锁、套接字之类的东西和所有其他需要清理的资源)使用with
语句,这样即使发生异常,文件也肯定会关闭;对于要写入的文件来说,这是最重要的(如果没有它,数据可能不会被刷新到磁盘),但即使对于打开读取的文件,在病态情况下,您也可能最终达到打开文件句柄限制。
重写你的第一个代码块是完全安全的,让你:
with requests.get("https:.../index_en.txt.lzma") as r, open(r'C:...index_en.txt.lzma','wb') as index_en:
index_en.write(r.content)
注意:request.Response
对象也是上下文管理器,所以我将它添加到with
中,以确保底层连接被及时释放回池。我还用r
前缀您的本地路径,使其成为原始字符串;在Windows上,如果路径中有反斜杠,您总是希望这样做,以便以Python识别为字符串转义的字符开头的文件或目录不会被损坏(例如,"C:foo"
实际上是"C:<form feed>oo"
,既不包含反斜杠也不包含f
)。
如果文件很大,您甚至可以稍微优化一下,将数据流式传输到文件中(需要固定的内存开销,与底层连接的缓冲区大小相关),而不是急切地抓取(需要与文件大小成比例的内存):
# stream=True means underlying file is opened without being immediately
# read into memory
with requests.get("https:.../index_en.txt.lzma", stream=True) as r, open(r'C:...index_en.txt.lzma','wb') as index_en:
# iter_content(None) produces an iterator of chunks of data (of whatever size
# is available in a single system call)
# Changing to writelines means the iterator is consumed and written
# as the data arrives
index_en.writelines(r.iter_content(None))
用with
语句控制requests.get
在这里更重要(因为stream=True
模式意味着底层套接字不会被立即消耗和释放)。
还要注意,print(compressed.readline)
没有做任何事情(因为您没有调用readline
)。如果在原始LZMA数据之前的响应中有一些文本行,那么您没有跳过它。如果没有这样的垃圾行,并且正确地调用了readline
(使用print(compressed.readline())
),那么它将破坏解压缩,因为文件指针现在将跳过文件的前几个(或许多)字节,落在一些几乎随机的偏移位置。
最后,
with py7zr.SevenZipFile("C:...index_en.txt.lzma", 'w') as archive:
archive.extract(path="C:...Json")
是错误的,因为你给它传递了一个模式,表明你打开它是为了写,而你显然是想从它读;要么省略'w'
,要么改成'r'
。