解压缩后文件名的字符集错误



我遇到以下问题:我通过SSZipArchive(在Swift应用程序中)提取了一个zip文件,但有些文件名包含"无效"字符
我想原因是我在Windows下压缩了文件,所以现在名称是用ANSI编码的。

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

官方规范规定,路径应以代码页437 MS-DOS Latin-US或UTF-8编码(如果设置了通用字段的Bit 11):

D.1 ZIP格式历史上只支持原始的IBM PC字符编码集,通常称为IBM代码页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路径名并正确解码。

可能在最新的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

最新更新