Преобразование шестнадцатеричной строки данных в NSData в Objective C (cocoa)
довольно новый разработчик iPhone здесь. Создание приложения для отправки команд RS232 устройству, ожидающему их через соединение сокетов TCP/IP. У меня есть часть коммитов, и я могу отправить команды ASCII в порядке. Это команды шестнадцатеричного кода, с которыми у меня возникают проблемы.
Итак, скажем, у меня есть следующие шестнадцатеричные данные для отправки (в этом формате):
\ X1C\x02d\x00\x00\x00\XFF\x7F
Как преобразовать это в объект NSData, который ожидает мой метод отправки?
Очевидно, что это не работает для этих шестнадцатеричных данных (но для стандартных команд ascii):
NSString *commandascii;
NSData *commandToSend;
commandascii = @"\x1C\x02d\x00\x00\x00\xFF\x7F";
commandToSend = [commandascii dataUsingEncoding:NSStringEncoding];
Для начала некоторые из шестнадцатеричных кодов \x являются escape-символами, и при компиляции в XCode появляется предупреждение "входное преобразование остановлено...". И NSStringEncoding явно не подходит для этой шестнадцатеричной строки.
Итак, первая проблема заключается в том, как сохранить эту шестнадцатеричную строку, я думаю, тогда как преобразовать в NSData.
Любые идеи?
Ответы
Ответ 1
Код для hex в NSStrings, например "00 05 22 1C EA 01 00 FF". 'command' - это шестнадцатеричный NSString.
command = [command stringByReplacingOccurrencesOfString:@" " withString:@""];
NSMutableData *commandToSend= [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
for (int i = 0; i < ([command length] / 2); i++) {
byte_chars[0] = [command characterAtIndex:i*2];
byte_chars[1] = [command characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[commandToSend appendBytes:&whole_byte length:1];
}
NSLog(@"%@", commandToSend);
Ответ 2
Здесь примерный декодер, реализованный в категории на NSString.
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
unsigned char strToChar (char a, char b)
{
char encoder[3] = {'\0','\0','\0'};
encoder[0] = a;
encoder[1] = b;
return (char) strtol(encoder,NULL,16);
}
@interface NSString (NSStringExtensions)
- (NSData *) decodeFromHexidecimal;
@end
@implementation NSString (NSStringExtensions)
- (NSData *) decodeFromHexidecimal;
{
const char * bytes = [self cStringUsingEncoding: NSUTF8StringEncoding];
NSUInteger length = strlen(bytes);
unsigned char * r = (unsigned char *) malloc(length / 2 + 1);
unsigned char * index = r;
while ((*bytes) && (*(bytes +1))) {
*index = strToChar(*bytes, *(bytes +1));
index++;
bytes+=2;
}
*index = '\0';
NSData * result = [NSData dataWithBytes: r length: length / 2];
free(r);
return result;
}
@end
Ответ 3
Если вы можете жестко закодировать шестнадцатеричные данные:
const char bytes[] = "\x00\x12\x45\xAB";
size_t length = (sizeof bytes) - 1; //string literals have implicit trailing '\0'
NSData *data = [NSData dataWithBytes:bytes length:length];
Если ваш код должен интерпретировать шестнадцатеричную строку (если шестнадцатеричная строка находится в переменной с именем inputData
, а lengthOfInputData
- длина inputData
):
#define HexCharToNybble(x) ((char)((x > '9') ? tolower(x) - 'a' + 10 : x - '0') & 0xF)
int i;
NSMutableData *data = [NSMutableData data];
for (i = 0; i < lengthOfInputData;)
{
char byteToAppend;
if (i < (lengthOfInputData - 3) &&
inputData[i+0] == '\\' &&
inputData[i+1] == 'x' &&
isxdigit(inputData[i+2]) &&
isxdigit(inputData[i+3]))
{
byteToAppend = HexCharToNybble(inputData[i+2]) << 4 + HexCharToNybble(input[i+3]);
i += 4;
}
else
{
byteToAppend = inputData[i];
i += 1;
}
[data appendBytes:&byteToAppend length:1];
}
Ответ 4
Это старая тема, но я хотел бы добавить несколько замечаний.
• Сканирование строки с помощью [NSString characterAtIndex]
не очень эффективно.
Получите строку C в UTF8, затем сканируйте ее с помощью *char++
намного быстрее.
• Лучше выделить NSMutableData
с пропускной способностью, чтобы избежать многократного изменения размера блока. Я думаю, что NSData еще лучше (см. Следующий пункт)
• Вместо создания NSData с использованием malloc, затем [NSData dataWithBytes]
и, наконец, свободного, используйте malloc и [NSData dataWithBytesNoCopy:length:freeWhenDone:]
Он также избегает операции с памятью (перераспределять, копировать, освобождать). FreeWhenDone boolean сообщает NSData взять на себя ответственность за блок памяти и освободить его, когда он будет выпущен.
• Вот функция, которую я должен преобразовать шестнадцатеричные строки в байтовые блоки. На входной строке не так много ошибок, но распределение проверено.
Форматирование входной строки (например, удаление 0x, пробелов и знаков препинания) лучше из функции преобразования.
Почему мы потеряем некоторое время, выполняя дополнительную обработку, если мы уверены, что ввод в порядке.
+(NSData*)bytesStringToData:(NSString*)bytesString
{
if (!bytesString || !bytesString.length) return NULL;
// Get the c string
const char *scanner=[bytesString cStringUsingEncoding:NSUTF8StringEncoding];
char twoChars[3]={0,0,0};
long bytesBlockSize = formattedBytesString.length/2;
long counter = bytesBlockSize;
Byte *bytesBlock = malloc(bytesBlockSize);
if (!bytesBlock) return NULL;
Byte *writer = bytesBlock;
while (counter--) {
twoChars[0]=*scanner++;
twoChars[1]=*scanner++;
*writer++ = strtol(twoChars, NULL, 16);
}
return[NSData dataWithBytesNoCopy:bytesBlock length:bytesBlockSize freeWhenDone:YES];
}
Ответ 5
Если я хочу жестко закодировать байты, я делаю что-то вроде этого:
enum { numCommandBytes = 8 };
static const unsigned char commandBytes[numCommandBytes] = { 0x1c, 0x02, 'd', 0x0, 0x0, 0x0, 0xff, 0x7f };
Если вы получаете эти байты с обратным слэшем во время выполнения, попробуйте функцию strunvis
.
Очевидно, что это не работает для этих шестнадцатеричных данных (но для стандартных команд ascii):
NSString *commandascii;
NSData *commandToSend;
commandascii = @"\x1C\x02d\x00\x00\x00\xFF\x7F";
commandToSend = [commandascii dataUsingEncoding:NSStringEncoding];
Для начала некоторые из шестнадцатеричных кодов \x
являются escape-символами, и при компиляции в XCode я получаю предупреждение "входное преобразование остановлено...". И NSStringEncoding явно не подходит для этой шестнадцатеричной строки.
Во-первых, это Xcode, с нижним регистром c.
Во-вторых, NSStringEncoding
- это тип, а не идентификатор кодировки. Этот код не должен компилироваться вообще.
Более того, обратная косая черта не является кодировкой; на самом деле, он в значительной степени не зависит от кодирования. Обратная косая черта и "х" являются символами, а не байтами, что означает, что они должны быть закодированы в (и декодированы из) байты, что является заданием кодирования.
Ответ 6
Другой способ сделать это.
-(NSData *) dataFromHexString:(NSString *) hexstr
{
NSMutableData *data = [[NSMutableData alloc] init];
NSString *inputStr = [hexstr uppercaseString];
NSString *hexChars = @"0123456789ABCDEF";
Byte b1,b2;
b1 = 255;
b2 = 255;
for (int i=0; i<hexstr.length; i++) {
NSString *subStr = [inputStr substringWithRange:NSMakeRange(i, 1)];
NSRange loc = [hexChars rangeOfString:subStr];
if (loc.location == NSNotFound) continue;
if (255 == b1) {
b1 = (Byte)loc.location;
}else {
b2 = (Byte)loc.location;
//Appending the Byte to NSData
Byte *bytes = malloc(sizeof(Byte) *1);
bytes[0] = ((b1<<4) & 0xf0) | (b2 & 0x0f);
[data appendBytes:bytes length:1];
b1 = b2 = 255;
}
}
return data;
}
Ответ 7
Я знаю, что это очень старый поток, но в Objective C есть схема кодирования, которая может легко преобразовать вашу строку шестнадцатеричных кодов в символы ASCII.
1) удалите \x
из строки и не сохраняя пробелы в строке, просто преобразуйте строку в NSData
, используя:
[[NSData alloc] initWithData:[stringToBeConverted dataUsingEncoding:NSASCIIStringEncoding]];
Ответ 8
Hex-данные - это всего лишь байты в памяти, вы считаете это строкой, потому что, как вы ее видите, но они могут представлять что угодно.
Попробуйте: (набрал в браузере, может содержать ошибки)
NSMutableData *hexData = [[NSMutableData alloc] init];
[hexData appendBytes: 0x1C];
[hexData appendBytes: 0x02D];
и т.д...