2009-12-02 124 views
8

我有一个使用NSData对象加载的二进制文件。有没有办法在二进制数据中找到一系列字符'abcd',并返回偏移量而不将整个文件转换为字符串?似乎它应该是一个简单的答案,但我不知道该怎么做。有任何想法吗?在二进制数据中查找字符串

我在iOS 3上这样做,所以我没有-rangeOfData:options:range:可用。

我打算把这一个奖励给Sixteen Otto来建议strstr。我去了,并找到了C函数strstr的源代码,并将其重写为在一个固定长度的Byte数组上工作 - 偶尔与char数组不同,因为它不是null结尾。这里是我结束了代码:

- (Byte*)offsetOfBytes:(Byte*)bytes inBuffer:(const Byte*)buffer ofLength:(int)len; 
{ 
    Byte *cp = bytes; 
    Byte *s1, *s2; 

    if (!*buffer) 
     return bytes; 

    int i = 0; 
    for (i=0; i < len; ++i) 
    { 
     s1 = cp; 
     s2 = (Byte*)buffer; 

     while (*s1 && *s2 && !(*s1-*s2)) 
      s1++, s2++; 

     if (!*s2) 
      return cp; 

     cp++; 
    } 

    return NULL; 
} 

这会返回一个指针字节的第一次出现,我在寻找的东西,在缓冲区,字节数组应该包含字节。

我这样称呼它:

// data is the NSData object 
const Byte *bytes = [data bytes]; 
Byte* index = [self offsetOfBytes:tag inBuffer:bytes ofLength:[data length]]; 
+0

如果您的数据确实可以包含空值(原始strstr()左侧太多),您为offsetOfBytes发布的代码:inBuffer:ofLength:将会遇到很多问题。至少,你需要传递字节的长度,因为这个函数不知道应该有多长时间。 – 2009-12-03 15:42:50

+0

嘿。感谢您的反馈。我在ofLength:参数中传入字节的长度,所以我不确定你的意思。谢谢。 – 2009-12-03 16:15:49

+0

你传递了​​两个字节的指针,但只有一个长度。这意味着你的代码不可能知道'bytes'和'buffer'有多长,这意味着你有可能在你的搜索中结束其中一个。 – 2009-12-03 18:07:28

回答

14

将您的子到NSData对象,并使用rangeOfData:options:range:寻找那些字节在较大NSData。确保字符串编码匹配!

在iPhone上,如果没有,您可能必须自己做。 C函数strstr()将为您提供指向缓冲区内第一次出现的模式的指针(只要不包含空值!),但不包含索引。下面是一个函数,应该做的工作(但是不敢保证,因为我还没有真正尝试运行它...):

- (NSUInteger)indexOfData:(NSData*)needle inData:(NSData*)haystack 
{ 
    const void* needleBytes = [needle bytes]; 
    const void* haystackBytes = [haystack bytes]; 

    // walk the length of the buffer, looking for a byte that matches the start 
    // of the pattern; we can skip (|needle|-1) bytes at the end, since we can't 
    // have a match that's shorter than needle itself 
    for (NSUInteger i=0; i < [haystack length]-[needle length]+1; i++) 
    { 
     // walk needle's bytes while they still match the bytes of haystack 
     // starting at i; if we walk off the end of needle, we found a match 
     NSUInteger j=0; 
     while (j < [needle length] && needleBytes[j] == haystackBytes[i+j]) 
     { 
      j++; 
     } 
     if (j == [needle length]) 
     { 
      return i; 
     } 
    } 
    return NSNotFound; 
} 

这个运行在像O(纳米),其中n是缓冲区长度,m是子字符串的大小。它被编写为与NSData一起工作,原因有两个:1)这就是您似乎手头上的,以及2)这些对象已经封装了实际字节和缓冲区的长度。

+1

我应该提到我在iPhone上没有rangeofData:options:range:方法。如果它确实会是一个完美的答案。 – 2009-12-02 22:53:16

+0

很酷。我会尝试你的代码,看看它是如何发展的。再次感谢你的帮助。 – 2009-12-03 19:50:21

+3

更新:rangeOfData自iOS 4起可用。 – steipete 2013-01-14 13:46:02

1

如果您使用的是雪豹,一种便捷的方式是在NSData中使用新的-rangeOfData:options:range:方法,该方法返回第一次出现的数据段的范围。否则,您可以使用其-bytes方法自己访问NSData的内容来执行自己的搜索。

+0

好点。我没有注意到-rangeOfData:options:range:仅在10.6中添加。 – 2009-12-02 20:07:42

+1

所以我没有可用的方法,因为我在iPhone上这样做。你用什么C函数来比较我正在寻找的字符子串到我从-bytes方法获得的缓冲区中?有任何想法吗? – 2009-12-02 22:54:45

1

我有同样的问题。 与其他建议相比,我解决了这个问题。

第一,我重新格式化数据(假设你的NSData保存在var rawFile)有:

NSString *ascii = [[NSString alloc] initWithData:rawFile encoding:NSAsciiStringEncoding]; 

现在,你可以很容易做到的字符串搜索像“ABCD”或任何你想使用NSScanner类将ascii字符串传递给扫描仪。也许这不是很有效,但它的工作原理,直到-rangeOfData方法也可用于iPhone。

+0

感谢您的回复。我在问题中提到的一个标准是“没有将整个文件转换为字符串”,所以这对我来说不是一个可行的解决方案。现在查看我的原始问题以查看我提出的解决方案。它运行良好,根本不需要复制任何数据。我只是遍历NSData对象中的字节,寻找我需要的字符序列,然后在找到第一次出现时返回一个指向数组中该位置的指针。 – 2009-12-03 06:53:07

+0

是的,我明白了。 真正的意义在于了解这种转换的成本,我对此没有任何线索。向苹果请求这个...也必须开始在他们的论坛中寻找。 :-) – Andy 2009-12-10 14:05:03