Python2和Python3之间的zipfile头语言编码位集不同
我希望这段代码在使用Python2或Python3运行时也能起到同样的作用Python2和Python3之间的zipfile头语言编码位集不同,python,python-2.7,zipfile,python-3.7,Python,Python 2.7,Zipfile,Python 3.7,我希望这段代码在使用Python2或Python3运行时也能起到同样的作用 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)
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
不同的部分是标志位
,对于Python 2设置为0x800
,对于Python 3设置为0x00
。这就是第11位:语言编码。如果filename.encode(“ascii”)抛出,则位11似乎被设置为
我试图在创建ZipInfo对象后通过设置标志来强制启用此位,但它在\u open\u to\u write()
中被重置回0x00
我不知道这里是否有人有好的解决办法。理想情况下,我希望两个输出都设置标志,因为这反映了jar实用程序的功能
编辑:已更新以添加info.flag\u bits=0x800
行,仅说明我正在尝试实现的目标。我在Windows上复制了这个:
ActivePython 3.6.0.3600,vs 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(有点神秘,它似乎在今晚早些时候起作用):
运行方式:
$ python2.7 zipf.py
50 4b 03 04 14 00 00 08
但是:
通过确保在创建info
条目之前打开文件,当然可以使其正常工作。但是,您必须避免使用writestr
,这只适用于Python 3.6(似乎有点滥用):
可能是3.6重置所有信息标志位
(通过内部打开
)是不正确的,尽管我并不清楚
原始答案如下
我无法重现这一点,但您是对的,如果文件名为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源代码)或:
(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
)。我目前正在使用类似的东西:
from zipfile import ZipFile, ZipInfo
import struct
orig_function = ZipInfo.FileHeader
def new_function(self, zip64=None):
header = orig_function(self, zip64)
fmt = "B"*len(header)
blist = list(struct.unpack(fmt, header))
blist[7] |= 0x8
return struct.pack(fmt, *blist)
setattr(ZipInfo, "FileHeader", new_function)
with ZipFile("out.zip", 'w') as zf:
content = "content"
info = ZipInfo()
info.filename = "file.txt"
info.file_size = len(content)
zf.writestr(info, content)
希望不会太快中断,FileHeader()似乎是将来不会改变的东西。也许我弄错了,但我似乎在我的Debian机器上的Python 3.5.3和Python 2.7.13下得到了Python 2和Python 3的输出50 4b 03 04 14 00 00
,对于我来说,在使用Python2和Python3的Windows上的输出是相同的(正如您在问题中针对Python3所展示的)。听起来有点依赖操作系统。你在跑什么?@martineau那还不是我想要的,我想把两者的位子都设置好,我已经改变了我的问题,因为以前不太清楚。谢谢你的测试,这是有用的反馈,也许你可以发布你的版本。基利:明白了。您的代码将创建一个文件。您需要确保在该文件中的某个偏移量处设置了位。看起来如果没有其他东西,你可以在使用二进制文件I/O创建文件后手动修改该文件。@martineau,这确实是我最后的选择,但这是一个非常糟糕的解决方案。如果你在Python3中设置了filename==file.txt的位,你能发布完整的代码吗?@Keeley:发布后我删除了它,但我从上次编辑之前复制你的样本开始。它基本上符合你目前的样品。我在FreeBSD上运行了它,但是只要zipfile
库代码是相同的,行为就应该是相同的……谢谢,但是我可以为所有使用的python提供精确的主版本和次版本吗。我对这篇文章投了赞成票,但目前它并没有给出一个解决方案(为两个Python版本设置了位),所以不能接受。一个是sys.version\u info(major=2,minor=7,micro=15,releaselevel='final',serial=0)
,另一个是sys.version\u info(major=3,minor=6,micro=6,releaselevel='final',serial=0)
。让我也尝试重新创建测试。
def _encodeFilenameFlags(self):
try:
return self.filename.encode('ascii'), self.flag_bits
except UnicodeEncodeError:
return self.filename.encode('utf-8'), self.flag_bits | 0x800
info.filename = u"sch\N{latin small letter o with diaeresis}n" # "file.txt"
info.filename = "file.txt"
info.flag_bits |= 0x0800
from zipfile import ZipFile, ZipInfo
import struct
orig_function = ZipInfo.FileHeader
def new_function(self, zip64=None):
header = orig_function(self, zip64)
fmt = "B"*len(header)
blist = list(struct.unpack(fmt, header))
blist[7] |= 0x8
return struct.pack(fmt, *blist)
setattr(ZipInfo, "FileHeader", new_function)
with ZipFile("out.zip", 'w') as zf:
content = "content"
info = ZipInfo()
info.filename = "file.txt"
info.file_size = len(content)
zf.writestr(info, content)