Python 3中的字符编码转换

3

在Python 2.x中,我使用了

"shift-jis".decode('shift-jis').encode('utf-8')

但是在Python 3.x中已经没有str.decode()函数了,那么在Python 3.x中等价的代码是什么?

更新:

更具体地说:

Python2中的函数是

def unzip(file, dir):
    zips = zipfile.ZipFile(file)
    for info in zips.infolist():
        info.filename = info.filename.decode('shift-jis').encode('utf-8')
        zips.extract(info,dir)

        print(info, filename)

这个函数的Python 3等效代码是什么?

Python 3 默认使用 Unicode 字符串,因此在字符串前添加 'b' 表示它是一个二进制字符串,正如 mflatishler 在他的例子中指出的那样。 - asermax
2个回答

5
针对您更新后的问题:
def unzip(file, directory): # dir is a keyword
    with zipfile.ZipFile(file, mode='r') as zips:
        zips.printdir()
        zips.extractall(directory)

.

>>> b'\x82\xb3'.decode('shiftjis')
'さ'
>>> b'\x82\xb3'.decode('shift-jis')
'さ'
>>> b'\x82\xb3'.decode('shift_jis')
'さ'
>>> '日本語'.encode('shiftjis')
b'\x93\xfa\x96{\x8c\xea'
>>> b'\x93\xfa\x96{\x8c\xea'.decode('shiftjis')
'日本語'

读取文件时:

with open('shiftjis.txt', 'r', encoding='shiftjis') as file:
    # do something with it

阅读更多:http://docs.python.org/3.3/library/io.html#i-o-base-classes

一个不那么理智的版本:

with open('shiftjis.txt', 'rb') as file:
    string = file.read().decode('shift-jis')

1
请不要使用最后一个示例,这是不必要的。with open('shiftjis.txt', 'r', encoding='shiftjis') 是一个更明智的选择。有关更多信息,请参见此页面 - Fredrick Brennan
当您使用with关键字时,无需调用.close()方法。 - KangOl
谢谢,但是 shift-jis 文件名根本没有被转换。该函数的目的不仅仅是提取压缩文件,还要将每个 shift-jis 文件名转换为 utf-8。 - kReoSo
它正在尝试为您解码文件名,根据zip文件的情况,使用utf-8或cp437编码。如果它解码错误,您需要先对其进行编码以获取文件的原始字节,然后再正确地解码它。例如:info.filename.encode('cp437').decode('shift-jis') - Thomas K
很奇怪,我尝试了一些zip文件,但是 info.filename.encode('shift-jis').decode('utf-8') 不起作用,并且出现了UnicodeEncodeError错误。在Python2中,问题中的代码运行得非常完美。 - kReoSo
这个字符集编码问题正在该主题讨论中:http://bugs.python.org/issue10614,http://bugs.python.org/issue15602。 - mflatischler

0

我自己有这个需求,而最简单粗暴的方法是:

def unzip(file, dir):
    zips = zipfile.ZipFile(file)
    for info in zips.infolist():
        info.filename = info.filename.encode("cp437").decode("shift-jis")
    print("Extracting: " + info.filename.encode(sys.stdout.encoding,errors='replace').decode(sys.stdout.encoding))
    zips.extract(info,dir)
    print("")

ZipFile 似乎将所有文件名在内部都视为 DOS(代码页 437)。与 Python 2 不同,Python 3 将所有字符串都存储为某种 UTF。因此,我们将文件名转换为字节数组,并将原始字节字符串解码为 shift-JIS,以获取最终文件名。

print 行执行类似的操作,但是针对 stdout 的默认编码进行转换。这可以防止在 Windows 上发生错误,因为它的终端几乎从不支持 Unicode。(但是,如果支持,则应正确显示名称。)

这对于一些 zip 文件效果很好,直到出现问题...

Traceback (most recent call last):
  File "jp\j-unzip.py", line 73, in <module>
    unzip(archname,archpath)
  File "jp\j-unzip.py", line 68, in unzip
    info.filename = info.filename.encode("cp437").decode("shift-jis")
UnicodeDecodeError: 'shift_jis' codec can't decode byte 0x8f in position 28: illegal multibyte sequence

额外内容!这个问题需要一些思考才能解决,但问题在于一些有效的Shift-JIS字符包含反斜杠,而ZipFile将其转换为正斜杠!例如,十在Shift-JIS中编码为8F 5C。这被转换为8F 2F,这是一个非法序列。以下(可能过于复杂)的代码检查此条件是否发生错误,并尝试修复它。但也许还有其他字符会出现这种情况,而序列是有效的,因此您会得到错误的字符而不是错误。 :(

def convert_filename(inname):
    err_ctr=0
    keep_going = True

    trans_filename = bytearray(inname.encode("cp437"))

    while keep_going:
        keep_going = False

        try:
            outname = trans_filename.decode("shift-jis")
        except UnicodeDecodeError as e:
            keep_going = True

            if e.args[4]=="illegal multibyte sequence":
                p0, p1 = e.args[2], e.args[3]
                print("Trying to fix encoding error at positions " + str(p0) +", "+ str(p1) + " caused by shift-jis sequence " + hex(trans_filename[p0]) +", "+  hex(trans_filename[p1]) )

                if (trans_filename[p0]>127 and trans_filename[p1] == 0x2f):
                    trans_filename[p1] = 0x5c
                else:
                    print("Don't know how to fix this error. Quitting. :(")
                    raise e

                err_ctr = err_ctr + 1
                print("This is error #" + str(err_ctr) + " for this filename.")
            else:
                raise e

        if err_ctr>50:
            print("More than 50 iterations. Are we stuck in an endless loop? Quitting...")
            sys.exit(1)

    return outname

def unzip(file, dir):
    zips = zipfile.ZipFile(file)
    for info in zips.infolist():
        info.filename = convert_filename(info.filename)
    print("Extracting: " + info.filename.encode(sys.stdout.encoding,errors='replace').decode(sys.stdout.encoding))
    zips.extract(info,dir)
    print("")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接