Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert an NSData into an NSString Hex string?

Tags:

macos

cocoa

When I call -description on an NSData object, I see a pretty Hex string of the NSData object's bytes like:

<f6e7cd28 0fc5b5d4 88f8394b af216506 bc1bba86 4d5b483d> 

I'd like to get this representation of the data (minus the lt/gt quotes) into an in-memory NSString so I can work with it.. I'd prefer not to call -[NSData description] and then just trim the lt/gt quotes (because I assume that is not a guaranteed aspect of NSData's public interface and is subject change in the future).

What's the simplest way to get this representation of an NSData object into an NSString object (other than calling -description)?

like image 375
Todd Ditchendorf Avatar asked Sep 22 '11 19:09

Todd Ditchendorf


2 Answers

Keep in mind that any String(format: ...) solution will be terribly slow (for large data)

NSData *data = ...; NSUInteger capacity = data.length * 2; NSMutableString *sbuf = [NSMutableString stringWithCapacity:capacity]; const unsigned char *buf = data.bytes; NSInteger i; for (i=0; i<data.length; ++i) {   [sbuf appendFormat:@"%02X", (NSUInteger)buf[i]]; } 

If you need something more performant try this:

static inline char itoh(int i) {     if (i > 9) return 'A' + (i - 10);     return '0' + i; }  NSString * NSDataToHex(NSData *data) {     NSUInteger i, len;     unsigned char *buf, *bytes;          len = data.length;     bytes = (unsigned char*)data.bytes;     buf = malloc(len*2);          for (i=0; i<len; i++) {         buf[i*2] = itoh((bytes[i] >> 4) & 0xF);         buf[i*2+1] = itoh(bytes[i] & 0xF);     }          return [[NSString alloc] initWithBytesNoCopy:buf                                           length:len*2                                         encoding:NSASCIIStringEncoding                                     freeWhenDone:YES]; } 

Swift version

 private extension Data {     var hexadecimalString: String {         let charA: UInt8 = 0x61         let char0: UInt8 = 0x30         func byteToChar(_ b: UInt8) -> Character {             Character(UnicodeScalar(b > 9 ? charA + b - 10 : char0 + b))         }         let hexChars = flatMap {[             byteToChar(($0 >> 4) & 0xF),             byteToChar($0 & 0xF)         ]}         return String(hexChars)     } } 
like image 186
Era Avatar answered Oct 06 '22 12:10

Era


I agree on the solution not to call description which is to be reserved for debugging, so good point and good question :)

The easiest solution is to loop thru the bytes of the NSData and construct the NSString from it. Use [yourData bytes] to access the bytes, and build the string into an NSMutableString.

Here is an example by implementing this using a category of NSData

@interface NSData(Hex) -(NSString*)hexRepresentationWithSpaces_AS:(BOOL)spaces; @end  @implementation NSData(Hex) -(NSString*)hexRepresentationWithSpaces_AS:(BOOL)spaces {     const unsigned char* bytes = (const unsigned char*)[self bytes];     NSUInteger nbBytes = [self length];     //If spaces is true, insert a space every this many input bytes (twice this many output characters).     static const NSUInteger spaceEveryThisManyBytes = 4UL;     //If spaces is true, insert a line-break instead of a space every this many spaces.     static const NSUInteger lineBreakEveryThisManySpaces = 4UL;     const NSUInteger lineBreakEveryThisManyBytes = spaceEveryThisManyBytes * lineBreakEveryThisManySpaces;     NSUInteger strLen = 2*nbBytes + (spaces ? nbBytes/spaceEveryThisManyBytes : 0);      NSMutableString* hex = [[NSMutableString alloc] initWithCapacity:strLen];     for(NSUInteger i=0; i<nbBytes; ) {         [hex appendFormat:@"%02X", bytes[i]];         //We need to increment here so that the every-n-bytes computations are right.         ++i;          if (spaces) {             if (i % lineBreakEveryThisManyBytes == 0) [hex appendString:@"\n"];             else if (i % spaceEveryThisManyBytes == 0) [hex appendString:@" "];         }     }     return [hex autorelease]; } @end 

Usage:

NSData* data = ... NSString* hex = [data hexRepresentationWithSpaces_AS:YES]; 
like image 36
AliSoftware Avatar answered Oct 06 '22 10:10

AliSoftware