将NSData转换为十六进制的NSString

5
参考以下问题:将NSData转换为HEX NSString 我已经使用Erik Aigner提供的解决方案解决了这个问题,即:
NSData *data = ...;
NSUInteger capacity = [data length] * 2;
NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:capacity];
const unsigned char *dataBuffer = [data bytes];
NSInteger i;
for (i=0; i<[data length]; ++i) {
  [stringBuffer appendFormat:@"%02X", (NSUInteger)dataBuffer[i]];
}

然而,问题在于如果十六进制数据末尾带有多余的零,则字符串值会不同。例如,如果该十六进制数据是字符串@"3700000000000000",则使用扫描器转换为整数时:

unsigned result = 0;
NSScanner *scanner = [NSScanner scannerWithString:stringBuffer];
[scanner scanHexInt:&result];
NSLog(@"INTEGER: %u",result);

结果应该是55,而不是4294967295,因为只有十六进制37被取出。那么如何去掉零呢?
编辑:(回答CRD的问题)
谢谢你澄清我的疑惑。所以你做的事情实际上是直接从字节指针中读取64位整数,对吗?但我还有一个问题。如何将NSData强制转换为字节指针?
为了让您更容易理解,我将解释一下我最初的做法。
首先,我显示了我拥有的文件的数据(数据以十六进制表示)。
NSData *file = [NSData dataWithContentsOfFile:@"file path here"];
NSLog(@"Patch File: %@",file);

输出:

在此输入图片描述

接下来,我读取并偏移文件的前8个字节,并将它们转换为字符串。

// 0-8 bytes
[file seekToFileOffset:0];
NSData *b = [file readDataOfLength:8];
NSUInteger capacity = [b length] * 2;
NSMutableString *stringBuffer = [NSMutableString stringWithCapacity:capacity];
const unsigned char *dataBuffer = [b bytes];
NSInteger i;
for (i=0; i<[b length]; ++i) {
    [stringBuffer appendFormat:@"%02X", (NSUInteger)dataBuffer[i]];
}
NSLog(@"0-8 bytes HEXADECIMAL: %@",stringBuffer);

正如您所看到的,0x3700000000000000是接下来的8个字节。如果要访问接下来的8个字节,我只需要更改SeekFileToOffset的值为8,以便访问下一个8个字节的数据。
总之,您给我的解决方案很有用,但是手动输入十六进制值并不实际。如果将字节格式化为字符串然后解析它们不是做法,那么如何直接访问数据的前8个字节并将它们转换为字节指针?

这是一个非常慢的实现。先将其格式化到缓冲区中,然后再转换为 NSString - trojanfoe
2个回答

3

4,294,967,295是ULONG_MAX,看起来扫描器只是溢出了。(scanHexInt方法会读取它找到的所有十六进制数字,并且由于零也是一个十六进制数字,该方法会吞噬整个数字,超过ULONG_MAX。)


问题似乎出在最高有效位和最低有效位。如果我使用小端编码,该字符串很可能只占用37,忽略后面的零。但是我该如何实现呢? - Dawson
你从高层次的角度来看,真正想要做什么? - zoul
我正在尝试将8个字节转换为64位整数,但我不确定应该采用什么适当的方法。看起来我的实现方式是错误的。因此,我正在寻求一些建议。 - Dawson

3
你似乎试图将存储在NSData中的8个字节转换为64位整数(0x3700000000000000中有8个字节)。
如果这是你的目标,那么通过将字节格式化为字符串,然后解析它们的方式不是实现目标的方法。你需要找出你的数据遵循哪种字节序(即最低有效字节优先还是最高有效字节优先),然后使用适当的字节序转换函数。
顺便说一句,你之所以得到4294967295(0xFFFFFFFF)是因为你要求NSScanner读取32位整数,而你的数字(0x3700000000000000)溢出了。
回复评论:
由于x86采用小端模式,因此可以通过直接进行强制类型转换从字节指针中读取小端模式的64位整数。
Byte bytes[] = { 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
NSLog(@"%lld", *(int64_t *)bytes);

如果你需要读取大端数据,那么你可以使用一个字节序转换函数,例如Endian64_Swap - 它只是翻转你所拥有的内容,所以先读取再翻转。
请注意,x86不要求数据访问对齐,但如果对齐,则性能会更好。

这是我想要实现的目标。我之前尝试使用NSData和NSUTF16LittleEndianStringEncoding来初始化字符串,但结果为7。然而,在阅读了您的答案并寻求外部建议后,我意识到这种做法是有缺陷和错误的。有什么办法可以在转换数据之前找出它所使用的字节序格式吗? - Dawson
@Dawson - 没有真正确定字节序的方法,你可以根据数值猜测,但你并不会真正“知道”。因此,除非你知道所需查找的数值范围,否则你需要向生产数据的人寻求答案。 - CRD
假设数据采用小端格式。我应该使用哪些适当的转换函数将 (0x3700000000000000) 转换为一个64位整数,在这种情况下是55呢?据我所知,NSScanner似乎没有适合这种情况的函数。 - Dawson
@Dawson - 你已经基本上自己写出了答案 NSData *fileContents = [NSData dataWithContentsOfFile:@"文件路径"]; Byte *ptrToBytes = [fileContents bytes];。现在根据需要将 ptrToBytes 进行转换和增量操作,以便处理你的数据。 - CRD
我已经找到了原因并解决了,谢谢。应该使用void const *ptrToBytes = [...],因为当[fileContents bytes]返回一个“void const *”时,它指向的数据是不希望你干扰的。 - Dawson
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接