Преобразовать шестнадцатеричную строку данных в NSData в Objective C (какао) - PullRequest
10 голосов
/ 26 февраля 2010

довольно новый разработчик iPhone здесь. Создание приложения для отправки команд RS232 на устройство, ожидающее их через сокет TCP / IP. У меня есть часть связи, и я могу нормально отправлять команды ASCII. У меня проблемы с шестнадцатеричным кодом.

Допустим, у меня есть следующие шестнадцатеричные данные для отправки (в этом формате):

\ X1C \ x02d \ x00 \ x00 \ x00 \ XFF \ x7F

Как мне преобразовать это в объект NSData, чего ожидает мой метод send?

Очевидно, что это не работает для этих шестнадцатеричных данных (но работает для стандартных команд ascii):

NSString *commandascii;
NSData *commandToSend;
commandascii = @"\x1C\x02d\x00\x00\x00\xFF\x7F";
commandToSend = [commandascii dataUsingEncoding:NSStringEncoding];

Для начала, некоторые из шестнадцатеричных кодов \ x являются escape-символами, и я получаю предупреждение «преобразование ввода остановлено ...» при компиляции в XCode. И NSStringEncoding явно не подходит для этой шестнадцатеричной строки.

Итак, первая проблема заключается в том, как сохранить эту шестнадцатеричную строку, я думаю, и как преобразовать ее в NSData.

Есть идеи?

Ответы [ 9 ]

30 голосов
/ 01 марта 2010

Код для шестнадцатеричного кода в строках NSS типа «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);
9 голосов
/ 26 февраля 2010

Вот пример декодера, реализованного в категории на 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
4 голосов
/ 26 февраля 2010

Если вы можете жестко закодировать шестнадцатеричные данные:

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];
}
2 голосов
/ 09 июня 2013

Это старая тема, но я хотел бы добавить несколько замечаний.

• Сканирование строки с помощью [NSString characterAtIndex] не очень эффективно. Получить строку C в UTF8, затем отсканировать ее с помощью *char++ намного быстрее.

• Лучше выделять NSMutableData с емкостью, чтобы избежать трудоемкого изменения размера блока. Я думаю, что NSData еще лучше (см. Следующий пункт)

• Вместо создания NSData с использованием malloc, затем [NSData dataWithBytes] и, наконец, освобождения, используйте malloc и [NSData dataWithBytesNoCopy:length:freeWhenDone:]

Также избегает операций с памятью (перераспределение, копирование, освобождение). Логическое значение freeWhenDone сообщает 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];
}
1 голос
/ 26 февраля 2010

Если я хочу жестко закодировать байты, я делаю что-то вроде этого:

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 - это тип, а не идентификатор кодировки. Этот код не должен компилироваться вообще.

Более того, обратная косая черта не является кодировкой; на самом деле, это в значительной степени не зависит от кодирования. Обратная косая черта и 'x' являются символами, а не байтами, что означает, что они должны быть закодированы (и декодированы из) байтов, что является задачей кодирования.

0 голосов
/ 03 июня 2018
-(NSData*) convertToByteArray:(NSString*) command {
    if (command == nil || command.length == 0) return nil;
    NSString *command1 = command;
    if(command1.length%2 != 0) {
        // to handle odd bytes like 1000 decimal = 3E8 with is of length = 3
        command1 = [NSString stringWithFormat:@"0%@",command1];
    }
    NSUInteger length = command1.length/2 ;
    NSMutableData *commandToSend = [[NSMutableData alloc] initWithLength:length];
    char byte_chars[3] = {'\0','\0','\0'};
    unsigned char whole_byte;
    for (int i=0; i<length; i++) {
        byte_chars[0] = [command1 characterAtIndex:i*2];
        byte_chars[1] = [command1 characterAtIndex:i*2+1];
        whole_byte = strtol(byte_chars, NULL, 16);
        [commandToSend appendBytes:&whole_byte length:1];
    }
    NSRange commandRange = NSMakeRange(commandToSend.length - length, length);
    NSData *result = [commandToSend subdataWithRange:commandRange];
    return result;
}
0 голосов
/ 13 октября 2014

Я знаю, что это очень старый поток, но в Objective C есть схема кодирования, которая может легко преобразовать вашу строку шестнадцатеричных кодов в символы ASCII.

1) удалите \x из строки и не оставляя пробелов в строке, просто преобразуйте строку в NSData, используя:

[[NSData alloc] initWithData:[stringToBeConverted dataUsingEncoding:NSASCIIStringEncoding]];
0 голосов
/ 22 апреля 2014

Еще один способ сделать это.

-(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;
}
0 голосов
/ 26 февраля 2010

Шестнадцатеричные данные - это просто байты в памяти, вы воспринимаете их как строку, потому что вы видите их так, но они могут представлять что угодно. Попробуйте: (набрано в браузере, может содержать ошибки)

NSMutableData *hexData = [[NSMutableData alloc] init];

[hexData appendBytes: 0x1C];
[hexData appendBytes: 0x02D];

и т.д ...

...