Python用zipfile/shutil压缩含德语变音符号文件遇编码错误
解决Python zipfile/shutil压缩含无效Unicode字符文件名的问题
看起来你遇到的不是普通德语变音符号的问题——错误里的\udcfc是孤立的UTF-16代理字符,不是正常的ü(正常ü的Unicode是\u00fc)。这种孤立代理字符属于无效的UTF-8序列,所以zipfile尝试编码时会抛出surrogates not allowed错误。
下面给你两种可行的解决方案,分别对应你用的zipfile和shutil.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




