在Objective C(可可)中将十六进制数据字符串转换为NSData

Posted

技术标签:

【中文标题】在Objective C(可可)中将十六进制数据字符串转换为NSData【英文标题】:Convert hex data string to NSData in Objective C (cocoa) 【发布时间】:2011-01-21 06:59:40 【问题描述】:

这里是相当新的 iPhone 开发人员。构建应用程序以通过 TCP/IP 套接字连接将 RS232 命令发送到期望它们的设备。我已经关闭了通讯部分,并且可以很好地发送 ASCII 命令。这是我遇到问题的十六进制代码命令。

假设我有以下十六进制数据要发送(以这种格式):

\x1C\x02d\x00\x00\x00\xFF\x7F

如何将其转换为我的 send 方法所期望的 NSData 对象?

显然这不适用于此十六进制数据(但适用于标准 ascii 命令):

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

首先,一些 \x 十六进制代码是转义字符,我在 XCode 中编译时收到“输入转换停止...”警告。而且 NSStringEncoding 显然也不适合这个十六进制字符串。

所以第一个问题是我猜这个十六进制字符串如何存储,然后如何转换为NSData。

有什么想法吗?

【问题讨论】:

感谢大家的回复,你们给了我很大的帮助,我学到了很多!我已经构建了上面的代码,它是一些答案的无意混合。这适用于像 "00 3c 5f 22 ef 00 00 ff" 这样的字符串,事实证明我可以得到而不是 \x 样式。再次感谢所有帮助! 【参考方案1】:

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

【讨论】:

如果您评论一些并使其更通用以便您不需要在字符串中使用 8 个十六进制字节会很好。否则很好谢谢 它对我有用。如果您有一个通用的十六进制字符串(而不是示例中的 8 个十六进制字节),请在 for 循环中使用 [command length]/2 而不是 8。 太棒了,麦克斯!谢谢!【参考方案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

【讨论】:

感谢 xyzzy,虽然这无法编译 - 它与 safeMalloc、strToChar 和 safeStrLen 有问题。我是否需要包含/导入某些内容才能使这些功能正常工作? 啊,对。您可以使用 malloc 和 strlen 替换这些调用。我会更新回复。【参考方案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 的变量中,而lengthOfInputDatainputData 的长度):


#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] 扫描字符串效率不高。 获取 UTF8 格式的 C 字符串,然后使用 *char++ 扫描它会快得多。

• 最好为NSMutableData 分配容量,以避免耗时的块大小调整。我认为 NSData 更好(见下一点)

• 不要使用 malloc 创建 NSData,然后是 [NSData dataWithBytes],最后是 free,使用 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];

【讨论】:

【参考方案5】:

如果我想对字节进行硬编码,我会这样做:

enum  numCommandBytes = 8 ;
static const unsigned char commandBytes[numCommandBytes] =  0x1c, 0x02, 'd', 0x0, 0x0, 0x0, 0xff, 0x7f ;

如果您在运行时获取这些反斜杠转义字节,请尝试the strunvis function。

显然这不适用于此十六进制数据(但适用于标准 ascii 命令):

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

首先,一些\x 十六进制代码是转义字符,我在 XCode 中编译时收到“输入转换停止...”警告。而且 NSStringEncoding 显然也不适合这个十六进制字符串。

首先,它是 Xcode,带有小写的 c。

其次,NSStringEncoding 是一种类型,而不是编码标识符。该代码根本不应该编译。

更重要的是,反斜杠转义不是一种编码;事实上,它在很大程度上独立于编码。反斜杠和“x”是字符,而不是字节,这意味着它们必须被编码为(和解码)字节,这是编码的工作。

【讨论】:

【参考方案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;

【讨论】:

这是一个非常低效的算法,同时使用subStringWithRange:rangeOfString: ...似乎没有必要。另外,我认为尝试一次读取两个字节而不是使用迷你状态机会更好。【参考方案7】:
-(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;

【讨论】:

【参考方案8】:

我知道这是一个非常古老的线程,但是 Objective C 中有一个编码方案可以轻松地将您的十六进制代码字符串转换为 ASCII 字符。

1) 从字符串中删除\x,并且在字符串中不保留空格,只需使用以下方法将字符串转换为NSData

[[NSData alloc] initWithData:[stringToBeConverted dataUsingEncoding:NSASCIIStringEncoding]];

【讨论】:

没有。 OP 不想将字符串本身转换为数据。他想要将数据的十六进制表示转换回数据本身。【参考方案9】:

十六进制数据只是内存中的字节,您可以将其视为字符串,因为这就是您看到它的方式,但它们可以代表任何东西。 试试:(在浏览器中输入,可能有错误)

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

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

等等……

【讨论】:

这看起来更像我所追求的,谢谢布鲁斯。虽然不太好用,在修复语法之后(应该是 appendbytes:length:) 我得到一个“警告:传递 'appendBytes:length:' 的参数 1 使指针从整数而不进行强制转换”有什么想法吗? appendBytes:length: 需要一个指向字节的指针以及您要附加的字节数。

以上是关于在Objective C(可可)中将十六进制数据字符串转换为NSData的主要内容,如果未能解决你的问题,请参考以下文章

尝试在 Swift 项目中使用 Objective C 可可豆荚,桥接文件编译为 Swift 代码

使用 CMake 链接到可可库(C++ 与 Objective-C 混合)

如何使用 UIImages 创建 UITableView? - 可可/Objective-C

可可objective-c:更改自定义视图的背景颜色

如何在objective-c中使用可可豆荚升级magic record sdk

如何在Objective C中将对象添加到NSMutableArray?