Python2和Python3之间的zipfile头语言编码位集不同

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)

我希望这段代码在使用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)
    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)