ZipFile.testzip() 在 Python 2 和 Python 3 上返回不同的结果
ZipFile.testzip() returning different results on Python 2 and Python 3
使用 zipfile
模块解压 Python 中的大型数据文件在 Python 2 上工作正常,但在 Python 3.6.0 上产生以下错误:
BadZipFile: Bad CRC-32 for file 'myfile.csv'
我将其追溯到检查 CRC 值的错误处理代码。
在 Python 上使用 ZipFile.testzip()
2 returns 没有(所有文件都很好)。 运行 它在 Python 3 returns 'myfile.csv'
表明该文件有问题。
要在 Python 2 和 Python 3 上重现的代码(涉及 300 MB 下载,抱歉):
import zipfile
import urllib
import sys
url = "https://de.iplantcollaborative.org/anon-files//iplant/home/shared/commons_repo/curated/Vertnet_Amphibia_Sep2016/VertNet_Amphibia_Sept2016.zip"
if sys.version_info >= (3, 0, 0):
urllib.request.urlretrieve(url, "vertnet_latest_amphibians.zip")
else:
urllib.urlretrieve(url, "vertnet_latest_amphibians.zip")
archive = zipfile.ZipFile("vertnet_latest_amphibians.zip")
archive.testzip()
有谁知道为什么存在这种差异,以及是否有办法让 Python 3 使用以下方法正确提取文件:
archive.extract("vertnet_latest_amphibians.csv")
我无法从存档中提取 Python 3。一些可能有用的调查结果(在 Mac OS X 上)。
检查存档的健康状况
将文件设置为只读以防止意外更改:
$ chmod -w vertnet_latest_amphibians.zip
$ ls -lh vertnet_latest_amphibians.zip
-r--r--r-- 1 lawh 2045336417 296M Jan 6 10:10 vertnet_latest_amphibians.zip
使用 zip
和 unzip
检查存档:
$ zip -T vertnet_latest_amphibians.zip
test of vertnet_latest_amphibians.zip OK
$ unzip -t vertnet_latest_amphibians.zip
Archive: vertnet_latest_amphibians.zip
testing: VertNet_Amphibia_eml.xml OK
testing: __MACOSX/ OK
testing: __MACOSX/._VertNet_Amphibia_eml.xml OK
testing: vertnet_latest_amphibians.csv OK
testing: __MACOSX/._vertnet_latest_amphibians.csv OK
No errors detected in compressed data of vertnet_latest_amphibians.zip
@sam-mussmann 也发现,7z
报告 CRC 错误:
$ 7z t vertnet_latest_amphibians.zip
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,4 CPUs x64)
Scanning the drive for archives:
1 file, 309726398 bytes (296 MiB)
Testing archive: vertnet_latest_amphibians.zip
--
Path = vertnet_latest_amphibians.zip
Type = zip
Physical Size = 309726398
ERROR: CRC Failed : vertnet_latest_amphibians.csv
Sub items Errors: 1
Archives with Errors: 1
Sub items Errors: 1
我的zip
和unzip
都比较老了; 7z
很新:
$ zip -v | head -2
Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
This is Zip 3.0 (July 5th 2008), by Info-ZIP.
$ unzip -v | head -1
UnZip 6.00 of 20 April 2009, by Debian. Original by Info-ZIP.
$ 7z --help |head -3
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,4 CPUs x64)
摘录
使用unzip
:
$ time unzip vertnet_latest_amphibians.zip vertnet_latest_amphibians.csv
Archive: vertnet_latest_amphibians.zip
inflating: vertnet_latest_amphibians.csv
real 0m17.201s
user 0m14.281s
sys 0m2.460s
使用 Python 2.7.13 提取,为简洁起见,使用 zipfile
的命令行界面:
$ time ~/local/python-2.7.13/bin/python2 -m zipfile -e vertnet_latest_amphibians.zip .
real 0m19.491s
user 0m12.996s
sys 0m5.897s
如您所见,Python 3.6.0(还有 3.4.5 和 3.5.2)报告错误的 CRC
假设 1:存档包含错误的 CRC zip
、unzip
和
Python 2.7.13检测失败; 7z
和Python3.4-3.6都在做
没错。
假设2:存档没问题; 7z
和 Python 3.4-3.6 都包含一个错误。
鉴于这些工具的相对年龄,我猜 H1 是正确的。
解决方法
如果您不使用 Windows 并信任存档的内容,使用常规 shell 命令可能更直接。类似于:
wget <the-long-url> -O /tmp/vertnet_latest_amphibians.zip
unzip /tmp/vertnet_latest_amphibians.zip vertnet_latest_amphibians.csv
rm -rf /tmp/vertnet_latest_amphibians.zip
或者您可以从 Python:
中执行 unzip
import os
os.system('unzip vertnet_latest_amphibians.zip vertnet_latest_amphibians.csv')
附带
catch ImportError
比检查版本稍微简洁一点
Python 口译员:
try:
from urllib.request import urlretrieve
except ImportError:
from urllib import urlretrieve
CRC 值正常。 zip 中记录的 'vertnet_latest_amphibians.csv' 的 CRC 是 0x87203305。解压后,这确实是文件的CRC
但是,给定的未压缩大小不正确。 zip 文件记录了 309,723,024 字节的压缩大小和 292,198,614 字节的未压缩大小(更小!)。实际上,未压缩的文件为 4,587,165,910 字节 (4.3 GiB)。这大于 32 位计数器中断的 4 GiB 阈值。
您可以这样修复它(至少在 Python 3.5.2 中有效):
archive = zipfile.ZipFile("vertnet_latest_amphibians.zip")
archive.getinfo("vertnet_latest_amphibians.csv").file_size += 2**32
archive.testzip() # now passes
archive.extract("vertnet_latest_amphibians.csv") # now works
作为@Kundor,将 file_size 设置为最大值 (2**32 - 1) 将起作用,但对于任何大于 4 GiB(4 GiB 减去 1 字节)的文件都会失败,因此将其设置为ZIP64 的最大大小(16 EiB 减去 1 字节)
测试(927MB 压缩和 11GB file_to_extract)
file: vertnet_latest_birds.csv
import zipfile
import urllib
import sys
url = "https://de.iplantcollaborative.org/anon-files//iplant/home/shared/commons_repo/curated/Vertnet_Amphibia_Sep2016/VertNet_Amphibia_Sept2016.zip"
zip_path = "vertnet_latest_amphibians.zip"
file_to_extract = "vertnet_latest_amphibians.csv"
if sys.version_info >= (3, 0, 0):
urllib.request.urlretrieve(url, zip_path)
else:
urllib.urlretrieve(url, zip_path)
archive = zipfile.ZipFile(zip_path)
if archive.testzip():
# reset uncompressed size header values to maximum
archive.getinfo(file_to_extract).file_size += (2 ** 64) - 1
open_archive_file = archive.open(file_to_extract, 'r')
# or archive.extract(file_to_extract)
在我的例子中,与提取文件时的实际大小相比,问题是错误的 ZipInfo.file_size
(Python 2.7 (正如@nick-matteo 发现的那样)。我发现 文件大小不匹配的原因是将 unicode 字符串传递给 zipfile.writestr()
函数。
在我的例子中,解决方案是在传递给 writestr()
函数之前将 unicode 编码为 utf8:
zf = zipfile.ZipFile(...)
if isinstance(file_contents, unicode):
file_contents = file_contents.encode("utf8")
zf.writestr("filename.txt", file_contents)
...
使用 zipfile
模块解压 Python 中的大型数据文件在 Python 2 上工作正常,但在 Python 3.6.0 上产生以下错误:
BadZipFile: Bad CRC-32 for file 'myfile.csv'
我将其追溯到检查 CRC 值的错误处理代码。
在 Python 上使用 ZipFile.testzip()
2 returns 没有(所有文件都很好)。 运行 它在 Python 3 returns 'myfile.csv'
表明该文件有问题。
要在 Python 2 和 Python 3 上重现的代码(涉及 300 MB 下载,抱歉):
import zipfile
import urllib
import sys
url = "https://de.iplantcollaborative.org/anon-files//iplant/home/shared/commons_repo/curated/Vertnet_Amphibia_Sep2016/VertNet_Amphibia_Sept2016.zip"
if sys.version_info >= (3, 0, 0):
urllib.request.urlretrieve(url, "vertnet_latest_amphibians.zip")
else:
urllib.urlretrieve(url, "vertnet_latest_amphibians.zip")
archive = zipfile.ZipFile("vertnet_latest_amphibians.zip")
archive.testzip()
有谁知道为什么存在这种差异,以及是否有办法让 Python 3 使用以下方法正确提取文件:
archive.extract("vertnet_latest_amphibians.csv")
我无法从存档中提取 Python 3。一些可能有用的调查结果(在 Mac OS X 上)。
检查存档的健康状况
将文件设置为只读以防止意外更改:
$ chmod -w vertnet_latest_amphibians.zip
$ ls -lh vertnet_latest_amphibians.zip
-r--r--r-- 1 lawh 2045336417 296M Jan 6 10:10 vertnet_latest_amphibians.zip
使用 zip
和 unzip
检查存档:
$ zip -T vertnet_latest_amphibians.zip
test of vertnet_latest_amphibians.zip OK
$ unzip -t vertnet_latest_amphibians.zip
Archive: vertnet_latest_amphibians.zip
testing: VertNet_Amphibia_eml.xml OK
testing: __MACOSX/ OK
testing: __MACOSX/._VertNet_Amphibia_eml.xml OK
testing: vertnet_latest_amphibians.csv OK
testing: __MACOSX/._vertnet_latest_amphibians.csv OK
No errors detected in compressed data of vertnet_latest_amphibians.zip
@sam-mussmann 也发现,7z
报告 CRC 错误:
$ 7z t vertnet_latest_amphibians.zip
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,4 CPUs x64)
Scanning the drive for archives:
1 file, 309726398 bytes (296 MiB)
Testing archive: vertnet_latest_amphibians.zip
--
Path = vertnet_latest_amphibians.zip
Type = zip
Physical Size = 309726398
ERROR: CRC Failed : vertnet_latest_amphibians.csv
Sub items Errors: 1
Archives with Errors: 1
Sub items Errors: 1
我的zip
和unzip
都比较老了; 7z
很新:
$ zip -v | head -2
Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
This is Zip 3.0 (July 5th 2008), by Info-ZIP.
$ unzip -v | head -1
UnZip 6.00 of 20 April 2009, by Debian. Original by Info-ZIP.
$ 7z --help |head -3
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=utf8,Utf16=on,HugeFiles=on,64 bits,4 CPUs x64)
摘录
使用unzip
:
$ time unzip vertnet_latest_amphibians.zip vertnet_latest_amphibians.csv
Archive: vertnet_latest_amphibians.zip
inflating: vertnet_latest_amphibians.csv
real 0m17.201s
user 0m14.281s
sys 0m2.460s
使用 Python 2.7.13 提取,为简洁起见,使用 zipfile
的命令行界面:
$ time ~/local/python-2.7.13/bin/python2 -m zipfile -e vertnet_latest_amphibians.zip .
real 0m19.491s
user 0m12.996s
sys 0m5.897s
如您所见,Python 3.6.0(还有 3.4.5 和 3.5.2)报告错误的 CRC
假设 1:存档包含错误的 CRC zip
、unzip
和
Python 2.7.13检测失败; 7z
和Python3.4-3.6都在做
没错。
假设2:存档没问题; 7z
和 Python 3.4-3.6 都包含一个错误。
鉴于这些工具的相对年龄,我猜 H1 是正确的。
解决方法
如果您不使用 Windows 并信任存档的内容,使用常规 shell 命令可能更直接。类似于:
wget <the-long-url> -O /tmp/vertnet_latest_amphibians.zip
unzip /tmp/vertnet_latest_amphibians.zip vertnet_latest_amphibians.csv
rm -rf /tmp/vertnet_latest_amphibians.zip
或者您可以从 Python:
中执行unzip
import os
os.system('unzip vertnet_latest_amphibians.zip vertnet_latest_amphibians.csv')
附带
catch ImportError
比检查版本稍微简洁一点
Python 口译员:
try:
from urllib.request import urlretrieve
except ImportError:
from urllib import urlretrieve
CRC 值正常。 zip 中记录的 'vertnet_latest_amphibians.csv' 的 CRC 是 0x87203305。解压后,这确实是文件的CRC
但是,给定的未压缩大小不正确。 zip 文件记录了 309,723,024 字节的压缩大小和 292,198,614 字节的未压缩大小(更小!)。实际上,未压缩的文件为 4,587,165,910 字节 (4.3 GiB)。这大于 32 位计数器中断的 4 GiB 阈值。
您可以这样修复它(至少在 Python 3.5.2 中有效):
archive = zipfile.ZipFile("vertnet_latest_amphibians.zip")
archive.getinfo("vertnet_latest_amphibians.csv").file_size += 2**32
archive.testzip() # now passes
archive.extract("vertnet_latest_amphibians.csv") # now works
作为@Kundor,将 file_size 设置为最大值 (2**32 - 1) 将起作用,但对于任何大于 4 GiB(4 GiB 减去 1 字节)的文件都会失败,因此将其设置为ZIP64 的最大大小(16 EiB 减去 1 字节)
测试(927MB 压缩和 11GB file_to_extract)
file: vertnet_latest_birds.csv
import zipfile
import urllib
import sys
url = "https://de.iplantcollaborative.org/anon-files//iplant/home/shared/commons_repo/curated/Vertnet_Amphibia_Sep2016/VertNet_Amphibia_Sept2016.zip"
zip_path = "vertnet_latest_amphibians.zip"
file_to_extract = "vertnet_latest_amphibians.csv"
if sys.version_info >= (3, 0, 0):
urllib.request.urlretrieve(url, zip_path)
else:
urllib.urlretrieve(url, zip_path)
archive = zipfile.ZipFile(zip_path)
if archive.testzip():
# reset uncompressed size header values to maximum
archive.getinfo(file_to_extract).file_size += (2 ** 64) - 1
open_archive_file = archive.open(file_to_extract, 'r')
# or archive.extract(file_to_extract)
在我的例子中,与提取文件时的实际大小相比,问题是错误的 ZipInfo.file_size
(Python 2.7 (正如@nick-matteo 发现的那样)。我发现 文件大小不匹配的原因是将 unicode 字符串传递给 zipfile.writestr()
函数。
在我的例子中,解决方案是在传递给 writestr()
函数之前将 unicode 编码为 utf8:
zf = zipfile.ZipFile(...)
if isinstance(file_contents, unicode):
file_contents = file_contents.encode("utf8")
zf.writestr("filename.txt", file_contents)
...