You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Python用zipfile/shutil压缩含德语变音符号文件遇编码错误

解决Python zipfile/shutil压缩含无效Unicode字符文件名的问题

看起来你遇到的不是普通德语变音符号的问题——错误里的\udcfc孤立的UTF-16代理字符,不是正常的ü(正常ü的Unicode是\u00fc)。这种孤立代理字符属于无效的UTF-8序列,所以zipfile尝试编码时会抛出surrogates not allowed错误。

下面给你两种可行的解决方案,分别对应你用的zipfileshutil.make_archive场景:

方案一:修改zipfile代码,修复文件名后压缩

核心思路是:读取文件用原始路径(避免找不到文件),但写入zip时用修复后的文件名(让zip能正常编码)。我们可以写一个函数清理无效的Unicode字符,再修改遍历逻辑:

import os
import zipfile

def fix_invalid_filename(s):
    # 处理孤立代理字符:先保留代理字节,再用replace替换无法解码的部分为�
    return s.encode('utf-8', 'surrogatepass').decode('utf-8', 'replace')

def zipdir(path, ziph):
    for root, dirs, files in os.walk(path):
        # 修复当前目录名的无效字符
        fixed_root = fix_invalid_filename(root)
        # 计算当前目录相对于压缩根目录的路径(用于zip内部的目录结构)
        rel_root = os.path.relpath(fixed_root, path)
        if rel_root == '.':
            rel_root = ''
        
        for file in files:
            fixed_file = fix_invalid_filename(file)
            # zip内部的文件路径(修复后的)
            arcname = os.path.join(rel_root, fixed_file) if rel_root else fixed_file
            # 实际文件系统中的路径(原始路径,确保能找到文件)
            actual_file_path = os.path.join(root, file)
            
            ziph.write(actual_file_path, arcname=arcname)

if __name__ == '__main__':
    with zipfile.ZipFile('path/to/destination.zip', 'w', zipfile.ZIP_DEFLATED) as zipf:
        zipdir('path/to/folder', zipf)

方案二:自定义shutil.make_archive替代方案

因为shutil.make_archive内部调用的还是zipfile,所以我们可以自己实现类似逻辑,加入文件名修复:

import os
import zipfile

def fix_invalid_filename(s):
    return s.encode('utf-8', 'surrogatepass').decode('utf-8', 'replace')

def custom_make_archive(base_name, root_dir):
    zip_path = f"{base_name}.zip"
    with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zf:
        for root, dirs, files in os.walk(root_dir):
            fixed_root = fix_invalid_filename(root)
            rel_root = os.path.relpath(fixed_root, root_dir)
            if rel_root == '.':
                rel_root = ''
            
            for file in files:
                fixed_file = fix_invalid_filename(file)
                arcname = os.path.join(rel_root, fixed_file) if rel_root else fixed_file
                actual_path = os.path.join(root, file)
                zf.write(actual_path, arcname=arcname)
    return zip_path

# 使用示例
custom_make_archive('/path/to/destination', '/path/to/folder')

为什么之前的尝试失败?

你之前直接对文件路径做encode/decode转换,导致实际读取文件的路径被修改了(比如把原始的含代理字符的路径变成了错误的编码路径),所以抛出FileNotFoundError。而我们的方案是分离了实际文件路径zip内部存储的文件名,既保证能找到文件,又解决了编码问题。

额外说明

如果你的文件名里确实是正常的德语变音符号(äöü),Python 3的zipfile默认会用UTF-8编码处理,不会报错。这次的问题完全是因为存在无效的孤立代理字符——这种字符可能是文件系统的编码兼容问题导致的,或者是文件名被错误生成的结果。

内容的提问来源于stack exchange,提问作者sebastian

火山引擎 最新活动