python - zipfile 标头语言编码位在 Python2 和 Python3 之间设置不同
问题描述
我希望这段代码在使用 Python 2 或 Python 3 运行时也能正常工作
from zipfile import ZipFile, ZipInfo
with ZipFile("out.zip", 'w') as zf:
content = "content"
info = ZipInfo()
info.filename = "file.txt"
info.flag_bits = 0x800
info.file_size = len(content)
zf.writestr(info, content)
但是,在 Python 2 下 out.zip 开始:
50 4b 03 04 14 00 00 08
在 Python3 下,它启动:
50 4b 03 04 14 00 00 00
不同的部分是flag_bits
,0x800
对于 Python 2,0x00
对于 Python 3 设置为 。 那是 BIT11:语言编码。BIT11 似乎得到了定if filename.encode("ascii")
投。
我试图通过在创建 ZipInfo 对象后设置标志来强制启用此位,但它被重置回0x00
in _open_to_write()
。
我想知道这里是否有人有一个好的解决方案。理想情况下,我希望两个输出都设置标志,因为这反映了 jar 实用程序的作用。
编辑: 更新以添加该info.flag_bits = 0x800
行只是为了说明我想要实现的目标。我已经在 Windows 上复制了这个:ActivePython 3.6.0.3600,与 ActivePython 2.7.14.2717,Windows 10。在 Linux 上:Python 3.6.6 与 Python 2.7.11 以防万一,我完全按照我的示例运行它,不hashbang,直接调用解释器:
pythonX test.py
解决方案
编辑:这是适用于我的 Python 2.7 但不适用于 3.6 的代码(有点神秘,它似乎在今晚早些时候工作):
$ cat zipf.py
from __future__ import print_function
from zipfile import ZipFile, ZipInfo
with ZipFile("out.zip", 'w') as zf:
content = "content"
info = ZipInfo()
info.filename = "file.txt"
info.flag_bits = 0x800
# don't set info.file_size here: zf.writestr() does that
zf.writestr(info, content)
with open('out.zip', 'rb') as stream:
byteseq = stream.read(8)
for i in byteseq:
if isinstance(i, str): i = ord(i)
print('{:02x}'.format(i), end=' ')
print()
运行为:
$ python2.7 zipf.py
50 4b 03 04 14 00 00 08
但:
$ python3.6 zipf.py
50 4b 03 04 14 00 00 00
当然可以通过确保在创建条目之前打开文件来使其工作。info
但是,您必须避免writestr
使用 ,这仅适用于 Python 3.6(并且似乎相当滥用):
from __future__ import print_function
from zipfile import ZipFile, ZipInfo
with ZipFile("out.zip", 'w') as zf:
info = ZipInfo()
info.filename = "file.txt"
content = "content"
if not isinstance(content, bytes):
content = content.encode('utf8')
info.file_size = len(content)
with zf.open(info, 'w') as stream:
info.flag_bits = 0x800
stream.write(content)
with open('out.zip', 'rb') as stream:
byteseq = stream.read(8)
for i in byteseq:
if isinstance(i, str): i = ord(i)
print('{:02x}'.format(i), end=' ')
print()
3.6重置所有info.flag_bits
(通过open
它所做的内部)可能是不正确的,尽管我不太清楚。
原答案如下
我无法重现这一点,但是如果文件名是 Unicode 并且编码为 ASCII 失败,则设置标志位中的第 11 位是对的:
def _encodeFilenameFlags(self):
if isinstance(self.filename, unicode):
try:
return self.filename.encode('ascii'), self.flag_bits
except UnicodeEncodeError:
return self.filename.encode('utf-8'), self.flag_bits | 0x800
else:
return self.filename, self.flag_bits
(Python 2.7 zipfile.py 源代码)或:
def _encodeFilenameFlags(self):
try:
return self.filename.encode('ascii'), self.flag_bits
except UnicodeEncodeError:
return self.filename.encode('utf-8'), self.flag_bits | 0x800
(Python 3.6 zipfile.py 源代码)。
要设置位,您需要一个不能直接用 ASCII 编码的文件名,例如:
info.filename = u"sch\N{latin small letter o with diaeresis}n" # "file.txt"
(此表示法适用于 Python 2.7 和 3.6)。
我试图在创建 ZipInfo 对象后通过设置标志来强制启用该位,但它在 _open_to_write() 中被重置回 0x00。
如果我添加:
info.filename = "file.txt"
info.flag_bits |= 0x0800
(在将文件名设置为 之后u"schön"
)并在 Python 2.7 或 3.6 下运行它,我在标题中设置了位(当然,zip 目录中的文件名更改回file.txt
)。
推荐阅读
- php - PHP将检查转换为使用三元运算符
- wordpress - WordPress RSS 提要显示错误的 lastBuildDate
- python - 从不同页面获取元素时如何避免 StaleElementReferenceError?
- c# - ASP.NET 多层应用迁移到 ASP.NET Core
- javascript - 如何将一个对象数组转换为另一个对象数组?
- c++ - 在 C++ 中找不到 AoS 和 SoA 之间的性能差异
- c# - 在激活特定注册期间发生错误,而不是在每台计算机上
- python - OCR/tesseract/OpenCV 中是否有任何方法可以从图像的特定区域中提取文本?
- api-gateway - 使用 Kong API 网关禁用路由
- groovy - Katalon Studio 将多个文件上传到同一输入字段