我需要解压在 Windows 中生成的日语 zip 文件。我正在使用unzip
。
如果我使用,unzip files.zip
我会得到错误的文件名。因此,根据这个问题,我使用unzip
来-O cp932
解压缩它们。这样,我就能得到正确的文件名。
但是有些 zip 文件需要密码。我知道正确的密码,但unzip
总是提示密码错误。
经过一番调查,我发现使用纯英文密码可以成功解压 zip 文件。也就是说,使用“Hello”这样的密码可以解压 zip 文件,但使用“こんにちは”这样的密码会导致“密码错误”。所以我猜这与字符编码有关。
事实上,我尝试过以下两种方法:
unzip -O cp932 compressed.zip
并在要求输入密码时粘贴“こんにちは”。unzip -O cp932 -P 'こんにちは' compressed.zip
。
它们都不起作用。
我发现了类似的问题这里没有答案。这个问题似乎是在寻求一种提供任何字节序列作为unzip
密码的方法。如果这个问题有答案,那么解决方案也适用于我的问题,因为我可以手动将密码转换为正确的字符编码,并将转换后的字符串提供给unzip
。
答案1
如果使用非 Unicode 编解码器创建了 zip 文件,并且还使用密码加密,则您传递给unzip
命令的密码也需要在此特定编解码器中编码为字节。在 Linux 上,您传递给的参数unzip
将被读取为utf-8
,这就是unzip -O cp932 -P 'こんにちは' compressed.zip
不起作用的原因。
总而言之,您需要一种方法来提供以字节为cp932
单位编码的密码以进行解压缩。没有简单的方法可以使用unzip
命令来执行此操作,但可以使用 Python 脚本执行此操作:
from zipfile import ZipFile
def extract_zip(archive_name, out_path, pwd, codec):
# password also needs to be encoded with codec
password = pwd.encode(codec) if pwd else None
# metadata_encoding argument is available in Python3.11
with ZipFile(archive_name, "r", metadata_encoding=codec) as myzip:
myzip.extractall(out_path, pwd=password)
extract_zip("compressed.zip", "output_dir", "こんにちは", "cp932")
答案2
zipfile
根本不支持 AES 加密的 zip 文件,所以它不在考虑范围内。第三方模块pyzipper适用于 AES zip 文件,无论压缩算法如何。但它是基于zipfile
Python 3.7 创建的,因此没有metadata_encoding
。可以修改其源代码以添加该功能(相当简单),但这里有一个简短的脚本,只需在后期处理中修复文件名:
from pathlib import Path
import shutil
import pyzipper
def extract_encrypted_ANSI_zip(zipfile, password, encoding='gbk', create_new_folder=True):
zipfile = Path(zipfile)
output_dir = Path.cwd() / zipfile.stem if create_new_folder else Path.cwd()
# create a temp folder to extract into, so we can fix the filenames before moving them to the output folder
temp = Path('temp')
while temp.exists():
temp = temp.with_name(temp.stem + '_')
temp.mkdir()
with pyzipper.AESZipFile(str(zipfile), 'r', compression=pyzipper.ZIP_DEFLATED, encryption=pyzipper.WZ_AES) as extracted_zip:
extracted_zip.extractall(str(temp), pwd=password.encode(encoding, errors='replace'))
all_files = [f for f in temp.rglob('*') if f.is_file()]
for f in all_files:
relative_path = f.relative_to(temp)
old_path = str(relative_path)
new_path = old_path.encode('cp437', errors='replace').decode(encoding, errors='replace')
if new_path != old_path:
print(old_path, '-->', new_path)
f2 = (output_dir / new_path)
f2.parent.mkdir(parents=True, exist_ok=True)
try:
f.rename(f2)
except FileExistsError:
print(f2, 'already exists')
shutil.rmtree(temp)
extract_encrypted_ANSI_zip('comparessed.zip', password="こんにちは", encoding='cp932')