I have the next problem with this code:
NSDictionary * imagen = [[NSDictionary alloc] initWithDictionary:[envio resultValue]];
NSString *imagenS = [imagen valueForKey:#"/Result"];
[Base64 initialize];
NSData * imagenDecode = [Base64 decode:imagenS];
NSLog(#"%#", [imagenS length]);
//SAVE IMAGE
NSArray *sysPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *docDirectory = [sysPaths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#david.png",docDirectory];
[imagenDecode writeToFile:filePath atomically:YES];
[envio resultValue] --> return a NSDictionary with one image in Base 64 codification.
I want decoder and save this image but in my console I have showed this message:
2011-08-23 20:15:36.539 WSStub[39226:a0f] -[NSCFDictionary length]: unrecognized selector sent to instance 0xd00ee0
The Base 64 code is:
//
// Base64.m
// CryptTest
//
// Created by Kiichi Takeuchi on 4/20/10.
// Copyright 2010 ObjectGraph LLC. All rights reserved.
//
#import "Base64.h"
#implementation Base64
#define ArrayLength(x) (sizeof(x)/sizeof(*(x)))
static char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static char decodingTable[128];
+ (void) initialize
{
if (self == [Base64 class])
{
memset(decodingTable, 0, ArrayLength(decodingTable));
for (NSInteger i = 0; i < ArrayLength(encodingTable); i++) {
decodingTable[encodingTable[i]] = i;
}
}
}
+ (NSData*) decode:(const char*) string length:(NSInteger) inputLength
{
if ((string == NULL) || (inputLength % 4 != 0))
{
return nil;
}
while (inputLength > 0 && string[inputLength - 1] == '=')
{
inputLength--;
}
NSInteger outputLength = inputLength * 3 / 4;
NSMutableData* data = [NSMutableData dataWithLength:outputLength];
uint8_t* output = data.mutableBytes;
NSInteger inputPoint = 0;
NSInteger outputPoint = 0;
while (inputPoint < inputLength)
{
char i0 = string[inputPoint++];
char i1 = string[inputPoint++];
char i2 = inputPoint < inputLength ? string[inputPoint++] : 'A'; /* 'A' will decode to \0 */
char i3 = inputPoint < inputLength ? string[inputPoint++] : 'A';
output[outputPoint++] = (decodingTable[i0] << 2) | (decodingTable[i1] >> 4);
if (outputPoint < outputLength)
{
output[outputPoint++] = ((decodingTable[i1] & 0xf) << 4) | (decodingTable[i2] >> 2);
}
if (outputPoint < outputLength)
{
output[outputPoint++] = ((decodingTable[i2] & 0x3) << 6) | decodingTable[i3];
}
}
return data;
}
+ (NSData*) decode:(NSString*) string
{
return [self decode:[string cStringUsingEncoding:NSASCIIStringEncoding] length:[string length]];
}
#end
The line
NSString *imagenS = [imagen valueForKey:#"/Result"];
is returning a dictionary, not a string. You'll have to inspect your data source to determine whether this is correct or not.
Your NSLog() call is wrong. To show a length, it should be:
NSLog(#"%lu", [imagenS length]);
But that is probably not the problem.
You seem to be invoking length on an NSDictionary. Hard to tell where you do that, since you don't show the piece of code where that happens. It could be that imagenS is not an NSString, but an NSDictionary instead.
Try to do:
NSLog(#"%#", [imagenS class]);
and see what is displayed. If probably tells you that imagenS is not a string, but an NSCFDictionary or similar instead.
FWIW, NSCFDictionary is one of the subclasses of NSDictionary that actually implement the different versions of the main class. This is called a class cluster.
Related
Im using the following method to convert text to MD5 format.
- (NSString*)MD5
{
const char *ptr = [txt_Password.text UTF8String];
unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(ptr, strlen(ptr), md5Buffer);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x",md5Buffer[i]];
return output;
}
However, it returns me the MD5 string in 16 bytes. And i need it in 64 bytes. Any help is appreciated. Thank you!!!
For md5: (Add this on a cathegory over NSString as well)
+ (NSString *)hashForString:(NSString *)aString {
NSData *data = [aString dataUsingEncoding:NSUTF8StringEncoding];
unsigned char digest[CC_MD5_DIGEST_LENGTH];
CC_MD5([data bytes], [data length], digest);
NSString *md5String = [[NSString alloc] initWithBytes:digest length:CC_MD5_DIGEST_LENGTH encoding:NSUTF8StringEncoding];
return [md5String autorelease];
}
For encoding your hashed psw to 64bit format:
- (NSString*)base64MD5HashForString:(NSString *)string {
NSString *md5Hash = [[[NSString hashForString:string] dataUsingEncoding:NSUTF8StringEncoding] encodeBase64];
return md5Hash;
}
Method below is a cathegory over NSData;
static const char kBase64Alphabet[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
- (NSString*)encodeBase64 {
NSMutableString *encodedData = [NSMutableString string];
int i = 0, j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[5];
memset(char_array_3, 0, 3*sizeof(char));
memset(char_array_4, 0, 5*sizeof(char));
int length = [self length];
char *bytes = (char*)[self bytes];
while(length--) {
char_array_3[i++] = *(bytes++);
if (i == 3) {
char_array_4[0] = kBase64Alphabet[(char_array_3[0] & 0xfc)>>2];
char_array_4[1] = kBase64Alphabet[((char_array_3[0] & 0x03) <<4) + ((char_array_3[1] & 0xf0) >>4)];
char_array_4[2] = kBase64Alphabet[((char_array_3[1] & 0x0f) <<2) + ((char_array_3[2] & 0xc0) >>6)];
char_array_4[3] = kBase64Alphabet[char_array_3[2]&0x3f];
[encodedData appendString:[NSString stringWithUTF8String:(const char*)char_array_4]];
i = 0;
}
}
if (i) {
for(j=i; j<3; j++)
char_array_3[j] = '\0';
char_array_4[0] = kBase64Alphabet[(char_array_3[0] & 0xfc)>>2];
char_array_4[1] = kBase64Alphabet[((char_array_3[0] & 0x03) <<4) + ((char_array_3[1] & 0xf0) >>4)];
char_array_4[2] = kBase64Alphabet[((char_array_3[1] & 0x0f) <<2) + ((char_array_3[2] & 0xc0) >>6)];
char_array_4[3] = kBase64Alphabet[char_array_3[2]&0x3f];
char_array_4[i+1] = 0;
[encodedData appendString:[NSString stringWithUTF8String:(const char*)char_array_4]];
while((i++<3))
[encodedData appendString:[NSString stringWithUTF8String:"="]];
}
return encodedData;
}
i am converting image from picker into nsdata(jpeg representation) and then converting it into nsstring using the following code
NSData *data=UIImageJPEGRepresentation(image,1.0);
NSString *imageString=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
[[NSUserDefaults standardUserDefaults] setObject:imageString forKey:#"image_name"];
and at the other end where i need to display the image the uiimage is formed as follows.
NSString *imageString=[[NSString alloc] init];
imageString=[[NSUserDefaults standardUserDefaults] objectForKey:#"image_name"];
UIImage *image=[UIImage imageWithData:[imageString dataUsingEncoding:NSUTF8StringEncoding]];
the image variable used in the top code is not nil ,but the image formed from data is getting nil...when i nslogged userdefaults some string is present for the key mentioned above .can anyone explain why is this so..what is the right way to do this
If it goes through a web server or the like, you could encapsulate it with base64 enc/decoding or some other plain encoder.
It removes "bad" char, ie that screw up the string during transformation, and change them to generic alphabetical chars and then back again.
if this is the reason to your issues, here is a short one i use (which I most probably stole and adapted, but do not remember from whom. Sorry! :-) )
base64helper.h
#import <Foundation/Foundation.h>
#interface NSData (MBBase64)
base64helper.m
#import "base64helper.h"
static const char encodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
#implementation NSData (MBBase64)
+ (id)dataWithBase64EncodedString:(NSString *)string;
{
if (string == nil)
[NSException raise:NSInvalidArgumentException format:nil];
if ([string length] == 0)
return [NSData data];
static char *decodingTable = NULL;
if (decodingTable == NULL)
{
decodingTable = malloc(256);
if (decodingTable == NULL)
return nil;
memset(decodingTable, CHAR_MAX, 256);
NSUInteger i;
for (i = 0; i < 64; i++)
decodingTable[(short)encodingTable[i]] = i;
}
const char *characters = [string cStringUsingEncoding:NSASCIIStringEncoding];
if (characters == NULL) // Not an ASCII string!
return nil;
char *bytes = malloc((([string length] + 3) / 4) * 3);
if (bytes == NULL)
return nil;
NSUInteger length = 0;
NSUInteger i = 0;
while (YES)
{
char buffer[4];
short bufferLength;
for (bufferLength = 0; bufferLength < 4; i++)
{
if (characters[i] == '\0')
break;
if (isspace(characters[i]) || characters[i] == '=')
continue;
buffer[bufferLength] = decodingTable[(short)characters[i]];
if (buffer[bufferLength++] == CHAR_MAX) // Illegal character!
{
free(bytes);
return nil;
}
}
if (bufferLength == 0)
break;
if (bufferLength == 1) // At least two characters are needed to produce one byte!
{
free(bytes);
return nil;
}
// Decode the characters in the buffer to bytes.
bytes[length++] = (buffer[0] << 2) | (buffer[1] >> 4);
if (bufferLength > 2)
bytes[length++] = (buffer[1] << 4) | (buffer[2] >> 2);
if (bufferLength > 3)
bytes[length++] = (buffer[2] << 6) | buffer[3];
}
realloc(bytes, length);
return [NSData dataWithBytesNoCopy:bytes length:length];
}
- (NSString *)base64Encoding;
{
if ([self length] == 0)
return #"";
char *characters = malloc((([self length] + 2) / 3) * 4);
if (characters == NULL)
return nil;
NSUInteger length = 0;
NSUInteger i = 0;
while (i < [self length])
{
char buffer[3] = {0,0,0};
short bufferLength = 0;
while (bufferLength < 3 && i < [self length])
buffer[bufferLength++] = ((char *)[self bytes])[i++];
// Encode the bytes in the buffer to four characters, including padding "=" characters if necessary.
characters[length++] = encodingTable[(buffer[0] & 0xFC) >> 2];
characters[length++] = encodingTable[((buffer[0] & 0x03) << 4) | ((buffer[1] & 0xF0) >> 4)];
if (bufferLength > 1)
characters[length++] = encodingTable[((buffer[1] & 0x0F) << 2) | ((buffer[2] & 0xC0) >> 6)];
else characters[length++] = '=';
if (bufferLength > 2)
characters[length++] = encodingTable[buffer[2] & 0x3F];
else characters[length++] = '=';
}
return [[[NSString alloc] initWithBytesNoCopy:characters length:length encoding:NSASCIIStringEncoding freeWhenDone:YES] autorelease];
}
#end
I'm trying to convert a Hex NSString to NSData (I'm using the below attached code). The following is the output:
<00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000>
which looks totally irrelevant to me. Any idea/ suggestions on where its going wrong?
NSString *strData = #"72ff63cea198b3edba8f7e0c23acc345050187a0cde5a9872cbab091ab73e553";
NSLog(#"string Data length is %d",[strData length]);
NSMutableData *commandToSend= [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[2];
int i;
for (i=0; i < [strData length]/2; i++) {
byte_chars[0] = [strData characterAtIndex:i*2];
byte_chars[1] = [strData characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, [strData length]);
[commandToSend appendBytes:&whole_byte length:1];
}
NSLog(#"%#", commandToSend);
NSString *command = #"72ff63cea198b3edba8f7e0c23acc345050187a0cde5a9872cbab091ab73e553";
command = [command stringByReplacingOccurrencesOfString:#" " withString:#""];
NSMutableData *commandToSend= [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (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);
Here is another method that also handles leading <, trailing > and embedded spaces such as
<9dc69faf a7434ba9 aef57f5c 365d571f 4c3753c4 ae13db42 57d184ca e00246c5>
Code:
+ (NSData *)dataFromHexString:(NSString *)string
{
string = [string lowercaseString];
NSMutableData *data= [NSMutableData new];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i = 0;
int length = string.length;
while (i < length-1) {
char c = [string characterAtIndex:i++];
if (c < '0' || (c > '9' && c < 'a') || c > 'f')
continue;
byte_chars[0] = c;
byte_chars[1] = [string characterAtIndex:i++];
whole_byte = strtol(byte_chars, NULL, 16);
[data appendBytes:&whole_byte length:1];
}
return data;
}
This is based on the answer by #Nikunj R. Jadav
This might be more useful, Apple has shared a NSData category.
NSData+HexString.m
The code is:
#implementation NSData (HexString)
// Not efficent
+(id)dataWithHexString:(NSString *)hex
{
char buf[3];
buf[2] = '\0';
NSAssert(0 == [hex length] % 2, #"Hex strings should have an even number of digits (%#)", hex);
unsigned char *bytes = malloc([hex length]/2);
unsigned char *bp = bytes;
for (CFIndex i = 0; i < [hex length]; i += 2) {
buf[0] = [hex characterAtIndex:i];
buf[1] = [hex characterAtIndex:i+1];
char *b2 = NULL;
*bp++ = strtol(buf, &b2, 16);
NSAssert(b2 == buf + 2, #"String should be all hex digits: %# (bad digit around %d)", hex, i);
}
return [NSData dataWithBytesNoCopy:bytes length:[hex length]/2 freeWhenDone:YES];
}
#end
I see several solution have been post only able to convert string with even length.
So here is my solution which also able return correct data if the string is odd length like this "DBA" became data like this this "\x0D\xBA"
+ (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;
}
I'd like to convert a regular NSString into an NSString with the (what I assume are) ASCII hex values and back.
I need to produce the same output that the Java methods below do, but I can't seem to find a way to do it in Objective-C. I've found some examples in C and C++ but I've had a hard time working them into my code.
Here are the Java methods I'm trying to reproduce:
/**
* Encodes the given string by using the hexadecimal representation of its UTF-8 bytes.
*
* #param s The string to encode.
* #return The encoded string.
*/
public static String utf8HexEncode(String s) {
if (s == null) {
return null;
}
byte[] utf8;
try {
utf8 = s.getBytes(ENCODING_UTF8);
} catch (UnsupportedEncodingException x) {
throw new RuntimeException(x);
}
return String.valueOf(Hex.encodeHex(utf8));
}
/**
* Decodes the given string by using the hexadecimal representation of its UTF-8 bytes.
*
* #param s The string to decode.
* #return The decoded string.
* #throws Exception If an error occurs.
*/
public static String utf8HexDecode(String s) throws Exception {
if (s == null) {
return null;
}
return new String(Hex.decodeHex(s.toCharArray()), ENCODING_UTF8);
}
Update: Thanks to drawnonward's answer here's the method I wrote to create the hex NSStrings. It gives me an "Initialization discards qualifiers from pointer target type" warning on the char declaration line, but it works.
- (NSString *)stringToHex:(NSString *)string
{
char *utf8 = [string UTF8String];
NSMutableString *hex = [NSMutableString string];
while ( *utf8 ) [hex appendFormat:#"%02X" , *utf8++ & 0x00FF];
return [NSString stringWithFormat:#"%#", hex];
}
Haven't had time to write the decoding method yet. When I do, I'll edit this to post it for anyone else interested.
Update2: So the method I posted above actually doesn't output what I'm looking for. Instead of outputting hex values in 0-f format, it was instead outputting all numbers. I finally got back to working on this problem and was able to write a category for NSString that exactly duplicates the Java methods I posted. Here it is:
//
// NSString+hex.h
// Created by Ben Baron on 10/20/10.
//
#interface NSString (hex)
+ (NSString *) stringFromHex:(NSString *)str;
+ (NSString *) stringToHex:(NSString *)str;
#end
//
// NSString+hex.m
// Created by Ben Baron on 10/20/10.
//
#import "NSString+hex.h"
#implementation NSString (hex)
+ (NSString *) stringFromHex:(NSString *)str
{
NSMutableData *stringData = [[[NSMutableData alloc] init] autorelease];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [str length] / 2; i++) {
byte_chars[0] = [str characterAtIndex:i*2];
byte_chars[1] = [str characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[stringData appendBytes:&whole_byte length:1];
}
return [[[NSString alloc] initWithData:stringData encoding:NSASCIIStringEncoding] autorelease];
}
+ (NSString *) stringToHex:(NSString *)str
{
NSUInteger len = [str length];
unichar *chars = malloc(len * sizeof(unichar));
[str getCharacters:chars];
NSMutableString *hexString = [[NSMutableString alloc] init];
for(NSUInteger i = 0; i < len; i++ )
{
[hexString appendString:[NSString stringWithFormat:#"%x", chars[i]]];
}
free(chars);
return [hexString autorelease];
}
#end
The perfect and short way to convert nsstring to hexadecimal values
NSMutableString *tempHex=[[NSMutableString alloc] init];
[tempHex appendString:#"0xD2D2D2"];
unsigned colorInt = 0;
[[NSScanner scannerWithString:tempHex] scanHexInt:&colorInt];
lblAttString.backgroundColor=UIColorFromRGB(colorInt);
The macro used for this code is----
#define UIColorFromRGB(rgbValue)
[UIColor \colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 \
green:((float)((rgbValue & 0xFF00) >> 8))/255.0 \
blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0]
For these lines of Java
utf8 = s.getBytes(ENCODING_UTF8);
new String(decodedHexString, ENCODING_UTF8);
Objective-C equivalents would be
utf8 = [s UTF8String];
[NSString initWithUTF8String:decodedHexString];
To make an NSString with the hexadecimal representation of a character string:
NSMutableString *hex = [NSMutableString string];
while ( *utf8 ) [hex appendFormat:#"%02X" , *utf8++ & 0x00FF];
You will have to make your own decodeHex function. Just pull two characters out of the string and, if they are valid, add a byte to the result.
There is a problem with your stringToHex method - it drops leading 0s, and ignores 00s. Just as a quick fix, I made the below:
+ (NSString *) stringToHex:(NSString *)str
{
NSUInteger len = [str length];
unichar *chars = malloc(len * sizeof(unichar));
[str getCharacters:chars];
NSMutableString *hexString = [[NSMutableString alloc] init];
for(NSUInteger i = 0; i < len; i++ )
{
// [hexString [NSString stringWithFormat:#"%02x", chars[i]]]; /*previous input*/
[hexString appendFormat:#"%02x", chars[i]]; /*EDITED PER COMMENT BELOW*/
}
free(chars);
return [hexString autorelease];
}
Thanks to all who contributed on this thread. It was a great help to me. Since things have moved on a little since the original post, here's my updated implementation for iOS 6. I went with the categories approach, but chose to split the load between NSData and NSString. Comments welcomed.
First, the NSString half, which handles decoding a hex encoded string into an NSData object.
#implementation NSString (StringToHexData)
//
// Decodes an NSString containing hex encoded bytes into an NSData object
//
- (NSData *) stringToHexData
{
int len = [self length] / 2; // Target length
unsigned char *buf = malloc(len)
unsigned char *whole_byte = buf;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [self length] / 2; i++) {
byte_chars[0] = [self characterAtIndex:i*2];
byte_chars[1] = [self characterAtIndex:i*2+1];
*whole_byte = strtol(byte_chars, NULL, 16);
whole_byte++;
}
NSData *data = [NSData dataWithBytes:buf length:len];
free( buf );
return data;
}
#end
The changes were mostly for efficiency's sake: some simple old-fashioned pointer arithmetic means I could allocate the whole buffer in one go, and populate it byte by byte. Then the whole thing is passed to NSData in one go.
The encoding part, in NSData, looks like this:
#implementation NSData (DataToHexString)
- (NSString *) dataToHexString
{
NSUInteger len = [self length];
char * chars = (char *)[self bytes];
NSMutableString * hexString = [[NSMutableString alloc] init];
for(NSUInteger i = 0; i < len; i++ )
[hexString appendString:[NSString stringWithFormat:#"%0.2hhx", chars[i]]];
return hexString;
}
#end
Again, some minor changes, though I suspect no efficiency gains here. The use of "%0.2hhx" solved all the problems of missing leading zero's and ensures that only a single-byte is output at a time.
Hope this helps the next person taking this on!
One possible solution:
+(NSString*)hexFromStr:(NSString*)str
{
NSData* nsData = [str dataUsingEncoding:NSUTF8StringEncoding];
const char* data = [nsData bytes];
NSUInteger len = nsData.length;
NSMutableString* hex = [NSMutableString string];
for(int i = 0; i < len; ++i)[hex appendFormat:#"%02X", data[i]];
return hex;
}
So, first off, I would like to thank drawnonward for his answer. This gave me the first function, mean and clean. In the same spirit, I wrote the other one. Hope you like it.
#synthesize unsigned char* value= _value;
- (NSString*) hexString
{
_value[CONSTANT]= '\0';
unsigned char* ptr= _value;
NSMutableString* hex = [[NSMutableString alloc] init];
while ( *ptr ) [hex appendFormat:#"%02x", *ptr++ & 0x00FF];
return [hex autorelease];
}
- (void) setHexString:(NSString*)hexString
{
_value[CONSTANT]= '\0';
unsigned char* ptr= _value;
for (const char* src= [hexString cStringUsingEncoding:NSASCIIStringEncoding];
*src;
src+=2)
{
unsigned int hexByte;
/*int res=*/ sscanf(src,"%02x",&hexByte);
*ptr++= (unsigned char)(hexByte & 0x00FF);
}
*ptr= '\0';
}
My input was an digit base10 string, and the output should be the hex representation in string format. Examples:
#"10" -> #"A"
#"1128" -> #"468"
#"1833828235" -> #"6D4DFF8B"
Implementation:
+ (NSString *) stringToHex:(NSString *)str{
NSInteger result = [str integerValue];
NSString *hexStr = (result)?#"":#"0";
while (result!=0) {
NSInteger reminder = result % 16;
if(reminder>=0 && reminder<=9){
hexStr = [[NSString stringWithFormat:#"%ld",(long)reminder] stringByAppendingString:hexStr];
}else if(reminder==10){
hexStr = [#"A" stringByAppendingString:hexStr];
}else if(reminder==11){
hexStr = [#"B" stringByAppendingString:hexStr];
}else if(reminder==12){
hexStr = [#"C" stringByAppendingString:hexStr];
}else if(reminder==13){
hexStr = [#"D" stringByAppendingString:hexStr];
}else if(reminder==14){
hexStr = [#"E" stringByAppendingString:hexStr];
}else{
hexStr = [#"F" stringByAppendingString:hexStr];
}
result /=16;
}
return hexStr;
}
Perhaps you should use NSString dataUsingEncoding: to encode and initWithData:length:encoding: to decode. Depends on where you are getting the data from.
I am looking for a nice-cocoa way to serialize an NSData object into a hexadecimal string. The idea is to serialize the deviceToken used for notification before sending it to my server.
I have the following implementation, but I am thinking there must be some shorter and nicer way to do it.
+ (NSString*) serializeDeviceToken:(NSData*) deviceToken
{
NSMutableString *str = [NSMutableString stringWithCapacity:64];
int length = [deviceToken length];
char *bytes = malloc(sizeof(char) * length);
[deviceToken getBytes:bytes length:length];
for (int i = 0; i < length; i++)
{
[str appendFormat:#"%02.2hhX", bytes[i]];
}
free(bytes);
return str;
}
This is a category applied to NSData that I wrote. It returns a hexadecimal NSString representing the NSData, where the data can be any length. Returns an empty string if NSData is empty.
NSData+Conversion.h
#import <Foundation/Foundation.h>
#interface NSData (NSData_Conversion)
#pragma mark - String Conversion
- (NSString *)hexadecimalString;
#end
NSData+Conversion.m
#import "NSData+Conversion.h"
#implementation NSData (NSData_Conversion)
#pragma mark - String Conversion
- (NSString *)hexadecimalString {
/* Returns hexadecimal string of NSData. Empty string if data is empty. */
const unsigned char *dataBuffer = (const unsigned char *)[self bytes];
if (!dataBuffer)
return [NSString string];
NSUInteger dataLength = [self length];
NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];
for (int i = 0; i < dataLength; ++i)
[hexString appendString:[NSString stringWithFormat:#"%02lx", (unsigned long)dataBuffer[i]]];
return [NSString stringWithString:hexString];
}
#end
Usage:
NSData *someData = ...;
NSString *someDataHexadecimalString = [someData hexadecimalString];
This is "probably" better than calling [someData description] and then stripping the spaces, <'s, and >'s. Stripping characters just feels too "hacky". Plus you never know if Apple will change the formatting of NSData's -description in the future.
NOTE: I have had people reach out to me about licensing for the code in this answer. I hereby dedicate my copyright in the code I posted in this answer to the public domain.
Here's a highly optimized NSData category method for generating a hex string. While #Dave Gallagher's answer is sufficient for a relatively small size, memory and cpu performance deteriorate for large amounts of data. I profiled this with a 2MB file on my iPhone 5. Time comparison was 0.05 vs 12 seconds. Memory footprint is negligible with this method while the other method grew the heap to 70MBs!
- (NSString *) hexString
{
NSUInteger bytesCount = self.length;
if (bytesCount) {
const char *hexChars = "0123456789ABCDEF";
const unsigned char *dataBuffer = self.bytes;
char *chars = malloc(sizeof(char) * (bytesCount * 2 + 1));
if (chars == NULL) {
// malloc returns null if attempting to allocate more memory than the system can provide. Thanks Cœur
[NSException raise:NSInternalInconsistencyException format:#"Failed to allocate more memory" arguments:nil];
return nil;
}
char *s = chars;
for (unsigned i = 0; i < bytesCount; ++i) {
*s++ = hexChars[((*dataBuffer & 0xF0) >> 4)];
*s++ = hexChars[(*dataBuffer & 0x0F)];
dataBuffer++;
}
*s = '\0';
NSString *hexString = [NSString stringWithUTF8String:chars];
free(chars);
return hexString;
}
return #"";
}
Using the description property of NSData should not be considered an acceptable mechanism for HEX encoding the string. That property is for description only and can change at any time. As a note, pre-iOS, the NSData description property didn't even return it's data in hex form.
Sorry for harping on the solution but it's important to take the energy to serialize it without piggy-backing off an API that is meant for something else other than data serialization.
#implementation NSData (Hex)
- (NSString*)hexString
{
NSUInteger length = self.length;
unichar* hexChars = (unichar*)malloc(sizeof(unichar) * (length*2));
unsigned char* bytes = (unsigned char*)self.bytes;
for (NSUInteger i = 0; i < length; i++) {
unichar c = bytes[i] / 16;
if (c < 10) {
c += '0';
} else {
c += 'A' - 10;
}
hexChars[i*2] = c;
c = bytes[i] % 16;
if (c < 10) {
c += '0';
} else {
c += 'A' - 10;
}
hexChars[i*2+1] = c;
}
NSString* retVal = [[NSString alloc] initWithCharactersNoCopy:hexChars length:length*2 freeWhenDone:YES];
return [retVal autorelease];
}
#end
Here is a faster way to do the conversion:
BenchMark (mean time for a 1024 bytes data conversion repeated 100 times):
Dave Gallagher : ~8.070 ms
NSProgrammer : ~0.077 ms
Peter : ~0.031 ms
This One : ~0.017 ms
#implementation NSData (BytesExtras)
static char _NSData_BytesConversionString_[512] = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
-(NSString*)bytesString
{
UInt16* mapping = (UInt16*)_NSData_BytesConversionString_;
register UInt16 len = self.length;
char* hexChars = (char*)malloc( sizeof(char) * (len*2) );
// --- Coeur's contribution - a safe way to check the allocation
if (hexChars == NULL) {
// we directly raise an exception instead of using NSAssert to make sure assertion is not disabled as this is irrecoverable
[NSException raise:#"NSInternalInconsistencyException" format:#"failed malloc" arguments:nil];
return nil;
}
// ---
register UInt16* dst = ((UInt16*)hexChars) + len-1;
register unsigned char* src = (unsigned char*)self.bytes + len-1;
while (len--) *dst-- = mapping[*src--];
NSString* retVal = [[NSString alloc] initWithBytesNoCopy:hexChars length:self.length*2 encoding:NSASCIIStringEncoding freeWhenDone:YES];
#if (!__has_feature(objc_arc))
return [retVal autorelease];
#else
return retVal;
#endif
}
#end
Functional Swift version
One liner:
let hexString = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes),
count: data.length).map { String(format: "%02x", $0) }.joinWithSeparator("")
Here's in a reusable and self documenting extension form:
extension NSData {
func base16EncodedString(uppercase uppercase: Bool = false) -> String {
let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(self.bytes),
count: self.length)
let hexFormat = uppercase ? "X" : "x"
let formatString = "%02\(hexFormat)"
let bytesAsHexStrings = buffer.map {
String(format: formatString, $0)
}
return bytesAsHexStrings.joinWithSeparator("")
}
}
Alternatively, use reduce("", combine: +) instead of joinWithSeparator("") to be seen as a functional master by your peers.
Edit: I changed String($0, radix: 16) to String(format: "%02x", $0), because one digit numbers needed to having a padding zero
Peter's answer ported to Swift
func hexString(data:NSData)->String{
if data.length > 0 {
let hexChars = Array("0123456789abcdef".utf8) as [UInt8];
let buf = UnsafeBufferPointer<UInt8>(start: UnsafePointer(data.bytes), count: data.length);
var output = [UInt8](count: data.length*2 + 1, repeatedValue: 0);
var ix:Int = 0;
for b in buf {
let hi = Int((b & 0xf0) >> 4);
let low = Int(b & 0x0f);
output[ix++] = hexChars[ hi];
output[ix++] = hexChars[low];
}
let result = String.fromCString(UnsafePointer(output))!;
return result;
}
return "";
}
swift3
func hexString()->String{
if count > 0 {
let hexChars = Array("0123456789abcdef".utf8) as [UInt8];
return withUnsafeBytes({ (bytes:UnsafePointer<UInt8>) -> String in
let buf = UnsafeBufferPointer<UInt8>(start: bytes, count: self.count);
var output = [UInt8](repeating: 0, count: self.count*2 + 1);
var ix:Int = 0;
for b in buf {
let hi = Int((b & 0xf0) >> 4);
let low = Int(b & 0x0f);
output[ix] = hexChars[ hi];
ix += 1;
output[ix] = hexChars[low];
ix += 1;
}
return String(cString: UnsafePointer(output));
})
}
return "";
}
Swift 5
func hexString()->String{
if count > 0 {
let hexChars = Array("0123456789abcdef".utf8) as [UInt8];
return withUnsafeBytes{ bytes->String in
var output = [UInt8](repeating: 0, count: bytes.count*2 + 1);
var ix:Int = 0;
for b in bytes {
let hi = Int((b & 0xf0) >> 4);
let low = Int(b & 0x0f);
output[ix] = hexChars[ hi];
ix += 1;
output[ix] = hexChars[low];
ix += 1;
}
return String(cString: UnsafePointer(output));
}
}
return "";
}
I needed to solve this problem and found the answers here very useful, but I worry about performance. Most of these answers involve copying the data in bulk out of NSData so I wrote the following to do the conversion with low overhead:
#interface NSData (HexString)
#end
#implementation NSData (HexString)
- (NSString *)hexString {
NSMutableString *string = [NSMutableString stringWithCapacity:self.length * 3];
[self enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop){
for (NSUInteger offset = 0; offset < byteRange.length; ++offset) {
uint8_t byte = ((const uint8_t *)bytes)[offset];
if (string.length == 0)
[string appendFormat:#"%02X", byte];
else
[string appendFormat:#" %02X", byte];
}
}];
return string;
}
This pre-allocates space in the string for the entire result and avoids ever copying the NSData contents out by using enumerateByteRangesUsingBlock. Changing the X to an x in the format string will use lowercase hex digits. If you don't want a separator between the bytes you can reduce the statement
if (string.length == 0)
[string appendFormat:#"%02X", byte];
else
[string appendFormat:#" %02X", byte];
down to just
[string appendFormat:#"%02X", byte];
I needed an answer that would work for variable length strings, so here's what I did:
+ (NSString *)stringWithHexFromData:(NSData *)data
{
NSString *result = [[data description] stringByReplacingOccurrencesOfString:#" " withString:#""];
result = [result substringWithRange:NSMakeRange(1, [result length] - 2)];
return result;
}
Works great as an extension for the NSString class.
You can always use [yourString uppercaseString] to capitalize letters in data description
A better way to serialize/deserialize NSData into NSString is to use the Google Toolbox for Mac Base64 encoder/decoder. Just drag into your App Project the files GTMBase64.m, GTMBase64.h e GTMDefines.h from the package Foundation and the do something like
/**
* Serialize NSData to Base64 encoded NSString
*/
-(void) serialize:(NSData*)data {
self.encodedData = [GTMBase64 stringByEncodingData:data];
}
/**
* Deserialize Base64 NSString to NSData
*/
-(NSData*) deserialize {
return [GTMBase64 decodeString:self.encodedData];
}
Here is a solution using Swift 3
extension Data {
public var hexadecimalString : String {
var str = ""
enumerateBytes { buffer, index, stop in
for byte in buffer {
str.append(String(format:"%02x",byte))
}
}
return str
}
}
extension NSData {
public var hexadecimalString : String {
return (self as Data).hexadecimalString
}
}
#implementation NSData (Extn)
- (NSString *)description
{
NSMutableString *str = [[NSMutableString alloc] init];
const char *bytes = self.bytes;
for (int i = 0; i < [self length]; i++) {
[str appendFormat:#"%02hhX ", bytes[i]];
}
return [str autorelease];
}
#end
Now you can call NSLog(#"hex value: %#", data)
Change %08x to %08X to get capital characters.
Swift + Property.
I prefer to have hex representation as property (the same as bytes and description properties):
extension NSData {
var hexString: String {
let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(self.bytes), count: self.length)
return buffer.map { String(format: "%02x", $0) }.joinWithSeparator("")
}
var heXString: String {
let buffer = UnsafeBufferPointer<UInt8>(start: UnsafePointer(self.bytes), count: self.length)
return buffer.map { String(format: "%02X", $0) }.joinWithSeparator("")
}
}
Idea is borrowed from this answer
[deviceToken description]
You'll need to remove the spaces.
Personally I base64 encode the deviceToken, but it's a matter of taste.