Is there any method in Objective-C that converts a hex string to bytes? For example @"1156FFCD3430AA22"
to an unsigned char array {0x11, 0x56, 0xFF, ...}
.
7 Answers
Fastest NSString category implementation that I could think of (cocktail of some examples):
- (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;
}
It is close to 8 times faster than wookay's solution. NSScanner is quite slow.

- 18,681
- 11
- 71
- 90
-
3+1 this seems to be better - not only faster, but this is **actually correct.** – Jul 07 '13 at 11:35
-
Good implementation but if you need error checking you need to set `errno` to 0 before calling `strtoul` and reading it afterwards. – Joe Oct 17 '17 at 01:16
-
Why do you make `byteChars` size 3 instead of size 2? – Adam Johns Aug 16 '18 at 15:55
-
1Nvm my C was rusty - forgot C strings are supposed to be null terminated. – Adam Johns Aug 16 '18 at 17:03
-
@AnandPrakash Anything we can help with? Are you using a valid hex string? The length should be dividable by 2, for example: `@"AA11"` – Yvo Apr 24 '20 at 17:38
@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]]);

- 592
- 3
- 3
-
5Correct me if I am wrong, but I think the [data appendBytes:&intValue length:1] has some trouble. Type unsigned int is at least four characters wide (holding at least 32 bit). &intValue is a pointer to the first of the four. This means that on little endian system the &intValue will rightfully be a pointer to the char you just scanned. But on big endian architectures it will always point to a zero-value, because the value is stored as the last of the four characters. Basically this means that this code will work on Mac's and break on iOS. – Trenskow Aug 22 '12 at 23:45
-
2This answer has quite a lot of flaws. Not only it treats the `int` as if it was a byte (which is impossible according to the C standard), but it only superfluously generates a lot of substrings, instead of just walking through the `const char *` one can obtain using `UTF8String`... – Jul 07 '13 at 11:34
The scanHexInt:
and similar methods of NSScanner
might be helpful in doing what you want, but you'd probably need to break the string up into smaller chunks first, in which case doing the translation manually might be simpler than using NSScanner
.

- 10,668
- 5
- 59
- 68
Modified approach,
/* 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;
}

- 15,408
- 7
- 58
- 96
Not in the way you are doing it. You'll need to write your own method to take every two characters, interpret them as an int, and store them in an array.

- 19,083
- 4
- 59
- 71
First attempt in 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
}
Or, as an extension on String:
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
}
}
This is a continuous work-in-progress, but appears to work well so far.
Further optimizations and a more in-depth discussion can be found on Code Review.
-
Isn't this just a Swift translation of [this answer](http://stackoverflow.com/a/17511588/865175)? – Iulian Onofrei Nov 23 '16 at 17:36
Several solution is returned wrong value if the string like this "DBA"
The correct data for "DBA" string is "\x0D\xBA" (int value : 3514)
if you got a data is not like this "\x0D\xBA" it mean you got a wrong byte because the value will be different, for example you got data like this "\xDB\x0A" the int value is 56074
Here is rewrite the solution:
+ (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;
}

- 799
- 7
- 11