1

我有以下问题:我通过 SSZipArchive(在 Swift 应用程序中)提取了一个 zip 文件,并且有一些文件名带有“无效”字符。
我认为原因是我在 Windows 下压缩了文件,因此名称现在以 ANSI 编码。

有没有办法在解压缩过程中转换所有“损坏”的文件夹和文件名?
还是以后?如果我必须遍历文件夹树并重命名文件,那将没有问题。
但我不知道如何找出在 ANSI 中设置了哪些名称,我也不知道如何更正字符集。

4

2 回答 2

1

官方规范说路径应该用Code Page 437 MS-DOS Latin US 或 UTF-8 编码(如果设置了通用字段的第 11 位):

D.1 ZIP 格式在历史上只支持原始的 IBM PC 字符编码集,通常称为 IBM Code Page 437。这将文件名字符的存储限制为仅在原始 MS-DOS 值范围内的字符,并且不能正确支持其他字符编码或语言的文件名。为了解决这个限制,本规范将支持以下更改。

D.2 如果未设置通用位 11,则文件名和注释应符合原始 ZIP 字符编码。如果设置了通用位 11,则文件名和注释必须支持 Unicode 标准,版本 4.1.0 或更高版本,使用 UTF-8 存储规范定义的字符编码形式。Unicode 标准由 Unicode 联盟 (www.unicode.org) 发布。存储在 ZIP 文件中的 UTF-8 编码数据不应包含字节顺序标记 (BOM)。

我最近发布了一个名为ZIPFoundation的 ZIP 文件格式的 Swift 开源实现。它符合标准,应该能够检测 Windows 路径名并正确解码它们。

于 2017-10-13T17:32:25.777 回答
0

可能已在最新的 SSZipArchive(当前为 2.1.1)中修复。我以类似于下面代码的方式实现了对非 Unicode 文件名的支持,因此您可以根据需要重用它来自己处理文件名。

好的,它在 Objective-C 中,但由于 SSZipArchive 本身已经有修复,你不应该再需要它了。否则,要么制作一个桥接头以将 Objective-c 代码包含到您的 Swift 应用程序中,要么将其转换为 Swift(应该很容易)。

@implementation NSString (SSZipArchive)

+ (NSString *)filenameStringWithCString:(const char *)filename size:(uint16_t)size_filename
{
    // unicode conversion attempt
    NSString *strPath = @(filename);
    if (strPath) {
        return strPath;
    }

    // if filename is non-unicode, detect and transform Encoding
    NSData *data = [NSData dataWithBytes:(const void *)filename length:sizeof(unsigned char) * size_filename];
    // supported encodings are in [NSString availableStringEncodings]
    [NSString stringEncodingForData:data encodingOptions:nil convertedString:&strPath usedLossyConversion:nil];
    if (strPath) {
        return strPath;
    }

    // if filename encoding is non-detected, we default to something based on data
    // note: hexString is more readable than base64RFC4648 for debugging unknown encodings
    strPath = [data hexString];
    return strPath;
}
@end

@implementation NSData (SSZipArchive)

// initWithBytesNoCopy from NSProgrammer, Jan 25 '12: https://stackoverflow.com/a/9009321/1033581
// hexChars from Peter, Aug 19 '14: https://stackoverflow.com/a/25378464/1033581
// not implemented as too lengthy: a potential mapping improvement from Moose, Nov 3 '15: https://stackoverflow.com/a/33501154/1033581
- (NSString *)hexString
{
    const char *hexChars = "0123456789ABCDEF";
    NSUInteger length = self.length;
    const unsigned char *bytes = self.bytes;
    char *chars = malloc(length * 2);
    // TODO: check for NULL
    char *s = chars;
    NSUInteger i = length;
    while (i--) {
        *s++ = hexChars[*bytes >> 4];
        *s++ = hexChars[*bytes & 0xF];
        bytes++;
    }
    NSString *str = [[NSString alloc] initWithBytesNoCopy:chars
                                                   length:length * 2
                                                 encoding:NSASCIIStringEncoding
                                             freeWhenDone:YES];
    return str;
}
@end
于 2017-10-13T05:09:33.287 回答