Преобразование NSData в шестнадцатеричный NSString

Что касается следующего вопроса: Преобразовать NSData в HEX NSString

Я решил проблему, используя решение, предоставленное Эриком Айгнером:

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);

Результатом будет 4294967295, что неверно. Разве это не должно быть 55, поскольку берется только гекса 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 байтам данных и преобразовать их в байтовый указатель?


person Dawson    schedule 21.11.2012    source источник
comment
Это очень медленная реализация. Сначала отформатируйте в буфер, а затем конвертируйте в NSString.   -  person trojanfoe    schedule 21.11.2012


Ответы (2)


Похоже, вы пытаетесь преобразовать 8 байтов, хранящихся в NSData, в 64-битное целое число (в 0x3700000000000000 8 байтов).

Если это ваша цель, то форматирование байтов в виде строки с последующим их синтаксическим анализом - это не способ сделать это. Вам нужно выяснить, в каком формате endian (т.е. сначала младший или самый старший байт) находятся ваши данные, а затем использовать соответствующие функции преобразования endian.

Кстати, причина, по которой вы получаете 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 не требует выравнивания доступа к данным, но в этом случае будет работать лучше.

person CRD    schedule 21.11.2012
comment
Это то, чего я пытаюсь достичь. Ранее я пробовал инициализировать строку, используя NSData с NSUTF16LittleEndianStringEncoding, но это дало мне 7. Однако, прочитав ваш ответ и посоветовавшись извне, я понял, что такой способ выполнения ошибочен и совершенно неверен. Есть идеи, как узнать, в каком формате endian находятся мои данные перед их преобразованием? - person Dawson; 22.11.2012
comment
@Dawson - есть известный способ действительно определить формат порядка байтов, вы могли бы догадаться на основе значений, но на самом деле вы бы не знали. Поэтому, если вы не знаете ожидаемый диапазон значений, вам нужно найти ответ от того, кто производит данные. - person CRD; 22.11.2012
comment
Предполагая, что данные имеют формат с прямым порядком байтов. Какие соответствующие функции преобразования я мог бы использовать для преобразования (0x3700000000000000) в 64-битное целое число, которое в данном случае равно 55? Насколько мне известно, NSScanner не имеет для этого подходящей функции. - person Dawson; 22.11.2012
comment
@Dawson - я добавил к ответу выше. - person CRD; 22.11.2012
comment
@Dawson - по сути, вы сами написали ответ NSData *fileContents = [NSData dataWithContentsOfFile:@"file path here"]; Byte *ptrToBytes = [fileContents bytes];. Теперь приведите и увеличивайте ptrToBytes по мере необходимости для обработки ваших данных. - person CRD; 22.11.2012
comment
Привет, большое спасибо за помощь. Теперь мой код работает отлично. Последний вопрос, есть небольшая семантическая проблема при инициализации байтового указателя, кажется, что выражение типа 'const void *' отбрасывает квалификаторы - person Dawson; 22.11.2012
comment
Я нашел причину и решил ее, спасибо. Вместо этого следует использовать void const * ptrToBytes = [...], поскольку когда [fileContents bytes] возвращает void const *, он указывает на данные, с которыми вы не хотите связываться. - person Dawson; 22.11.2012

4 294 967 295 - это ULONG_MAX, поэтому кажется, что сканер просто переполняется. (Метод scanHexInt съедает все найденные шестнадцатеричные цифры, а поскольку ноль также является шестнадцатеричной цифрой, метод проглатывает все число, минуя ULONG_MAX.)

person zoul    schedule 21.11.2012
comment
проблема, похоже, связана с наиболее значимым и наименее значимым битом. Если я использую кодировку с прямым порядком байтов, строка, скорее всего, займет всего 37 и игнорирует нули позади. Но как мне этого добиться? - person Dawson; 21.11.2012
comment
Что вы на самом деле пытаетесь сделать с точки зрения высокого уровня? - person zoul; 21.11.2012
comment
Я пытаюсь преобразовать 8 байтов в 64-битное целое число, но я не уверен, как это сделать. Похоже, что мой метод достижения этого неправильный. Следовательно, я ищу совета. - person Dawson; 22.11.2012