使用与文件系统中使用的编码不同的字符编码解压文件名

使用与文件系统中使用的编码不同的字符编码解压文件名

我偶尔会得到文件名以 ISO-8859-1 或其他 Unicode 之前的方案编码的 tarball。我的系统使用 UTF-8,因此当我使用常用​​选项 ( tar xvf foo.tar) 解压这些存档时,我最终会得到一个充满 mojibake 文件名的目录。

到目前为止我一直在使用convmv将文件名转换为 UTF-8他们已被提取。这有点不方便,因为我要么需要调用convmv每个受影响的文件,要么将文件解压到新目录中,convmv在整个目录上运行,然后将文件移动到我最初想要的位置。如果无法将此功能编码到 shell 脚本中,是否有某种方法可以在解压存档文件名时将其即时转换为 UTF-8?

答案1

这是一个小tar文件提取器,它在提取之前修改内存中的名称:

#!/usr/bin/python27

import tarfile

def transform(data):
    u = data.decode('latin1')
    return u.encode('utf8')

tar = tarfile.open('archive.tar')
for m in tar.getmembers():
    m.name = transform(m.name)

tar.extractall()

警告:与 GNU 不同tar,该提取器不会剥离前导/。要么向该提取器添加检查逻辑,要么tar在使用tar -t.

答案2

托马斯·埃克的回答几乎对我有用,除了调用getmembers似乎将整个存档读入内存,而我拥有的 tar 文件太大了。这是一次读取一个文件的修改版本。名称转换也不同,因为在我的 tar 文件中,文件名显示有代理字符;我不确定这是否普遍适用。

#!/usr/bin/env python3

import tarfile
import sys
from pathlib import Path

def transform(data):
    try:
        data.encode('utf-8')
        return data
    except UnicodeEncodeError as err:
        u = data.encode('utf-8', 'surrogateescape')
        v = u.decode('latin-1')

        print(f'Replaced filename: {v}')
        v.encode('utf-8') # check that it can be encoded
        return v

tar = tarfile.open(sys.argv[1])
while (m := tar.next()) is not None:
    if m.name == '.':
        continue
    m.name = transform(m.name)
    path = Path(m.name)
    try:
        if path.stat().st_size == m.size:
            print(f'Already exists with the same size: skipping {m.name}')
            continue
        else:
            print(f'Size mismatch, extracting: {m.name}', flush=True)
    except FileNotFoundError:
        print(f'About to extract {m.name}', flush=True)
    tar.extract(m)
    print(f'Extracted {m.name}')

相关内容