社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

python2和python3之间的zipfile头语言编码位设置不同

Keeely • 5 年前 • 1607 次点击  

在使用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。那是第11位:语言编码。bit11似乎已经准备好了 if filename.encode("ascii") 投掷。

我试图在创建zipinfo对象后通过设置标志来强制这个位,但是它被重置为 0x00 在里面 _open_to_write() .

我想知道这里是否有人有好的解决办法。理想情况下,我希望两个输出都 标志集,因为这反映了jar实用程序的功能。

编辑: 更新以添加 info.flag_bits = 0x800 写下我要达到的目标。我在windows上复制了这个: activepython 3.6.0.3600与activepython 2.7.14.2717、windows 10相比。 在Linux上: Python3.6.6与Python2.7.11 如果这很重要的话,我正以我的例子运行它,没有hashbang,直接调用解释器:

pythonX test.py
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/41249
 
1607 次点击  
文章 [ 2 ]  |  最新文章 5 年前
Keeely
Reply   •   1 楼
Keeely    6 年前

我暂时用这样的东西:

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()似乎是将来不会改变的东西。

torek
Reply   •   2 楼
torek    6 年前

编辑: 下面的代码适用于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 ,而这只适用于Python3.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.6zipfile.py源代码)。

要获得位集,您需要一个不能直接用ascii编码的文件名,例如:

info.filename = u"sch\N{latin small letter o with diaeresis}n" # "file.txt"

(此符号适用于Python2.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 )