【发布时间】:2011-01-30 20:34:36
【问题描述】:
Objective-C 中是否有任何方法可以将十六进制字符串转换为字节?例如 @"1156FFCD3430AA22" 到 unsigned char array {0x11, 0x56, 0xFF, ...}。
【问题讨论】:
标签: objective-c nsstring hex byte
Objective-C 中是否有任何方法可以将十六进制字符串转换为字节?例如 @"1156FFCD3430AA22" 到 unsigned char array {0x11, 0x56, 0xFF, ...}。
【问题讨论】:
标签: objective-c nsstring hex byte
我能想到的最快的 NSString 类别实现(一些示例的鸡尾酒):
- (NSData *)dataFromHexString {
const char *chars = [self UTF8String];
int i = 0, len = self.length;
NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
char byteChars[3] = {'\0','\0','\0'};
unsigned long wholeByte;
while (i < len) {
byteChars[0] = chars[i++];
byteChars[1] = chars[i++];
wholeByte = strtoul(byteChars, NULL, 16);
[data appendBytes:&wholeByte length:1];
}
return data;
}
它比 wookay 的解决方案快近 8 倍。 NSScanner 很慢。
【讨论】:
strtoul 之前将errno 设置为0,然后再读取它。
byteChars 设为 3 号而不是 2 号?
@"AA11"
@interface NSString (NSStringHexToBytes)
-(NSData*) hexToBytes ;
@end
@implementation NSString (NSStringHexToBytes)
-(NSData*) hexToBytes {
NSMutableData* data = [NSMutableData data];
int idx;
for (idx = 0; idx+2 <= self.length; idx+=2) {
NSRange range = NSMakeRange(idx, 2);
NSString* hexStr = [self substringWithRange:range];
NSScanner* scanner = [NSScanner scannerWithString:hexStr];
unsigned int intValue;
[scanner scanHexInt:&intValue];
[data appendBytes:&intValue length:1];
}
return data;
}
@end
/// example
unsigned char bytes[] = { 0x11, 0x56, 0xFF, 0xCD, 0x34, 0x30, 0xAA, 0x22 };
NSData* expectedData = [NSData dataWithBytes:bytes length:sizeof(bytes)];
NSLog(@"data %@", [@"1156FFCD3430AA22" hexToBytes]);
NSLog(@"expectedData isEqual:%d", [expectedData isEqual:[@"1156FFCD3430AA22" hexToBytes]]);
【讨论】:
int 视为一个字节(根据C 标准这是不可能的),而且它只是多余地生成了很多子字符串,而不是仅仅遍历const char * 可以使用@987654324 获得@...
scanHexInt: 和 NSScanner 的类似方法可能有助于执行您想要的操作,但您可能需要先将字符串分成更小的块,在这种情况下手动进行翻译可能比手动翻译更简单使用NSScanner。
【讨论】:
不是你现在做的那样。您需要编写自己的方法来获取每两个字符,将它们解释为 int,并将它们存储在数组中。
【讨论】:
修改方法,
/* Converts a hex string to bytes.
Precondition:
. The hex string can be separated by space or not.
. the string length without space or 0x, must be even. 2 symbols for one byte/char
. sample input: 23 3A F1 OR 233AF1, 0x23 0X231f 2B
*/
+ (NSData *) dataFromHexString:(NSString*)hexString
{
NSString * cleanString = [Util cleanNonHexCharsFromHexString:hexString];
if (cleanString == nil) {
return nil;
}
NSMutableData *result = [[NSMutableData alloc] init];
int i = 0;
for (i = 0; i+2 <= cleanString.length; i+=2) {
NSRange range = NSMakeRange(i, 2);
NSString* hexStr = [cleanString substringWithRange:range];
NSScanner* scanner = [NSScanner scannerWithString:hexStr];
unsigned int intValue;
[scanner scanHexInt:&intValue];
unsigned char uc = (unsigned char) intValue;
[result appendBytes:&uc length:1];
}
NSData * data = [NSData dataWithData:result];
[result release];
return data;
}
/* Clean a hex string by removing spaces and 0x chars.
. The hex string can be separated by space or not.
. sample input: 23 3A F1; 233AF1; 0x23 0x3A 0xf1
*/
+ (NSString *) cleanNonHexCharsFromHexString:(NSString *)input
{
if (input == nil) {
return nil;
}
NSString * output = [input stringByReplacingOccurrencesOfString:@"0x" withString:@""
options:NSCaseInsensitiveSearch range:NSMakeRange(0, input.length)];
NSString * hexChars = @"0123456789abcdefABCDEF";
NSCharacterSet *hexc = [NSCharacterSet characterSetWithCharactersInString:hexChars];
NSCharacterSet *invalidHexc = [hexc invertedSet];
NSString * allHex = [[output componentsSeparatedByCharactersInSet:invalidHexc] componentsJoinedByString:@""];
return allHex;
}
【讨论】:
在 Swift 2.2 中的首次尝试:
func hexStringToBytes(hexString: String) -> NSData? {
guard let chars = hexString.cStringUsingEncoding(NSUTF8StringEncoding) else { return nil}
var i = 0
let length = hexString.characters.count
let data = NSMutableData(capacity: length/2)
var byteChars: [CChar] = [0, 0, 0]
var wholeByte: CUnsignedLong = 0
while i < length {
byteChars[0] = chars[i]
i+=1
byteChars[1] = chars[i]
i+=1
wholeByte = strtoul(byteChars, nil, 16)
data?.appendBytes(&wholeByte, length: 1)
}
return data
}
或者,作为字符串的扩展:
extension String {
func dataFromHexString() -> NSData? {
guard let chars = cStringUsingEncoding(NSUTF8StringEncoding) else { return nil}
var i = 0
let length = characters.count
let data = NSMutableData(capacity: length/2)
var byteChars: [CChar] = [0, 0, 0]
var wholeByte: CUnsignedLong = 0
while i < length {
byteChars[0] = chars[i]
i+=1
byteChars[1] = chars[i]
i+=1
wholeByte = strtoul(byteChars, nil, 16)
data?.appendBytes(&wholeByte, length: 1)
}
return data
}
}
这是一项持续进行中的工作,但目前看来运行良好。
更多优化和更深入的讨论可以在Code Review找到。
【讨论】:
如果这样的字符串有几种解决方案返回错误的值 “DBA”
“DBA”字符串的正确数据是"\x0D\xBA"(int值:3514)
如果你得到的数据不是这样的"\x0D\xBA"这意味着你得到一个错误的字节,因为值会不同,例如你得到这样的数据" \xDB\x0A" int 值为 56074
这里是重写解决方案:
+ (NSData *)dataFromHexString:(NSString *) string {
if([string length] % 2 == 1){
string = [@"0"stringByAppendingString:string];
}
const char *chars = [string UTF8String];
int i = 0, len = (int)[string length];
NSMutableData *data = [NSMutableData dataWithCapacity:len / 2];
char byteChars[3] = {'\0','\0','\0'};
unsigned long wholeByte;
while (i < len) {
byteChars[0] = chars[i++];
byteChars[1] = chars[i++];
wholeByte = strtoul(byteChars, NULL, 16);
[data appendBytes:&wholeByte length:1];
}
return data;
}
【讨论】: