1

我试图通过查看前四个字节来检测给定文件是否是 ZIP 文件。这是在 iOS 应用程序中,因此文件句柄由 Cocoa 框架处理,但实际的字节比较内容是 C 语言,我真的不知道。

    unsigned char aBuffer[4];
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
    NSData *data = [fileHandle readDataOfLength:4];
    [data getBytes:aBuffer];
    if (aBuffer[0] == 0x50 && aBuffer[1] == 0x4b && aBuffer[2] == 0x03 && aBuffer[3] == 0x04) {
        archiveType = ARCHIVE_TYPE_ZIP;
    }

它有效,但让我觉得笨拙。有没有更好的方法来比较这 4 个字节?(是的,我知道它需要更多的错误检查。)

4

5 回答 5

5

你应该使用memcmp. 就像strcmp,但为了记忆。

if (memcmp([data bytes],"PK\3\4",4) == 0) {
    // success
}

也就是说,由于您使用的是 Objective-C,因此您应该寻找比 C 更高级别的实现。我建议使用您期望的数据构建一个 NSData,然后使用[data isEqual: expectedData].

NSData *expectedHeader = [NSData dataWithBytes: "PK\3\4" length: 4];
if ([expectedHeader isEqual: data]) {
    // success
}

您也可以isEqualToData:改用 if isEqual:。我更喜欢短标识符,但isEqualToData:效率更高,并且在暴露于不匹配的类型时会抛出异常。

您现在非常接近您的意图,而不是实际的机制。

@jsd 澄清说他在寻找程序员的效率,而不是运行时的效率。但是对于将来阅读本文的任何人:忘记运行时效率。您多久检查一次 zip 标头?相反,担心代码有多简单以及它可能出错的方式有多少。并且总是在合适的时候支持更高级别的抽象。

于 2012-08-28T23:48:51.287 回答
2

您总是可以输入aBuffera union,因此您只需进行一次比较即可检查:

union {
  unsigned char asBytes[4];
  uint32_t asInt;
} aBuffer;
...
[data getBytes:aBuffer.asBytes];
if (aBuffer.asInt == 0x504b0304) { ... } // or 0x04034b50, depending on endianness
于 2012-08-28T23:54:02.793 回答
1

我真的没有想到任何更有效的方法。

编译器可能会很好地为您优化它。

而且由于它只是一个不在循环或任何东西中的语句,我不确定是否有理由尝试手动优化它。

您可以做的一件事是进行无符号长比较,如

unsigned char fileCheck [4] = {0x50, 0x4b, 0x03, 0x04};
unsigned char aBuffer[4];

NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
NSData *data = [fileHandle readDataOfLength:4];
[data getBytes:aBuffer];

if (*(unsigned long *)aBuffer == *(unsigned long *)fileCheck) {
    // it is a file
    archiveType = ARCHIVE_TYPE_ZIP;
}
于 2012-08-28T23:56:41.763 回答
0

这很笨拙,但不太可能是低效的。

另一种方法是使用 C 的memcmp函数。例如:

if(!memcmp(aBuffer, "PK\003\004", 4))
{
    archiveType = ARCHIVE_TYPE_ZIP;
}

您可能还希望削减额外的缓冲区:

NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
NSData *data = [fileHandle readDataOfLength:4];
if ([data length] >= 4 && !memcmp([data bytes], "PK\003\004", 4))     
{
    archiveType = ARCHIVE_TYPE_ZIP;
}

C(以及因此,Objective-C)保证从左到右评估 if 语句并在可能的情况下提前退出,因此首先检查长度将明确避免memcmp超出范围。

于 2012-08-28T23:50:58.120 回答
-1

您是否尝试过以下操作?

if (*((unsigned int *) aBuffer) == 'PK\3\4') {
    archiveType = ARCHIVE_TYPE_ZIP;
}

这应该适用于数据类型大小一致的 iOS 设备,例如 iPad 和 iPhone。

那些反对我的人一定不熟悉多字符文字。

于 2012-08-28T23:59:21.597 回答