Hyphenation library doesn't work with iOS 5 - iphone

I just tried the hyphenate library of Tupil.
It was mentioned here http://blog.tupil.com/adding-hyphenation-to-nsstring/.
But while it is working perfectly under iOS 4.3, I did not get it to work with iOS 5.
Are there any other frameworks I could use? I heard of CoreText, but I don't know where to start.
Thanks in advance
Martin

I realize it's been a few years, but I just found that there's a Core Foundation function that suggests hyphenation points: CFStringGetHyphenationLocationBeforeIndex. It only works for a few languages, but it looks like it might be really helpful for the narrow label problem.
Update:
Here is some example code. It's a CLI program that shows where to hyphenate a word:
#include <Cocoa/Cocoa.h>
int main(int ac, char *av[])
{
#autoreleasepool {
if(ac < 2) {
fprintf(stderr, "usage: hyph word\n");
exit(1);
}
NSString *word = [NSString stringWithUTF8String: av[1]];
unsigned char hyspots[word.length];
memset(hyspots, 0, word.length);
CFRange range = CFRangeMake(0, word.length);
CFLocaleRef locale = CFLocaleCreate(NULL, CFSTR("en_US"));
for(int i = 0; i < word.length; i++) {
int x = CFStringGetHyphenationLocationBeforeIndex(
(CFStringRef) word, i, range,
0, locale, NULL);
if(x >= 0 && x < word.length)
hyspots[x] = 1;
}
for(int i = 0; i < word.length; i++) {
if(hyspots[i]) putchar('-');
printf("%s", [[word substringWithRange: NSMakeRange(i, 1)] UTF8String]);
}
putchar('\n');
}
exit(0);
}
Here's how it looks when you build and run it:
$ cc -o hyph hyph.m -framework Cocoa
$ hyph accessibility
ac-ces-si-bil-i-ty
$ hyph hypothesis
hy-poth-e-sis
These hyphenations agree exactly with the OS X dictionary. I am using this for a narrow label problem in iOS, and it's working well for me.

I wrote a category based Jeffrey's answer for adding "soft hyphenation" to any string. These are "-" which is not visible when rendered, but instead merely queues for CoreText or UITextKit to know how to break up words.
NSString *string = #"accessibility tests and frameworks checking";
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:#"en_US"];
NSString *hyphenatedString = [string softHyphenatedStringWithLocale:locale error:nil];
NSLog(#"%#", hyphenatedString);
Outputs ac-ces-si-bil-i-ty tests and frame-works check-ing
NSString+SoftHyphenation.h
typedef enum {
NSStringSoftHyphenationErrorNotAvailableForLocale
} NSStringSoftHyphenationError;
extern NSString * const NSStringSoftHyphenationErrorDomain;
extern NSString * const NSStringSoftHyphenationToken;
#interface NSString (SoftHyphenation)
- (BOOL)canSoftHyphenateStringWithLocale:(NSLocale *)locale;
- (NSString *)softHyphenatedStringWithLocale:(NSLocale *)locale error:(out NSError **)error;
#end
NSString+SoftHyphenation.m
#import "NSString+SoftHyphenation.h"
NSString * const NSStringSoftHyphenationErrorDomain = #"NSStringSoftHyphenationErrorDomain";
NSString * const NSStringSoftHyphenationToken = #"­"; // NOTE: UTF-8 soft hyphen!
#implementation NSString (SoftHyphenation)
- (BOOL)canSoftHyphenateStringWithLocale:(NSLocale *)locale
{
CFLocaleRef localeRef = (__bridge CFLocaleRef)(locale);
return CFStringIsHyphenationAvailableForLocale(localeRef);
}
- (NSString *)softHyphenatedStringWithLocale:(NSLocale *)locale error:(out NSError **)error
{
if(![self canSoftHyphenateStringWithLocale:locale])
{
if(error != NULL)
{
*error = [self hyphen_createOnlyError];
}
return [self copy];
}
else
{
NSMutableString *string = [self mutableCopy];
unsigned char hyphenationLocations[string.length];
memset(hyphenationLocations, 0, string.length);
CFRange range = CFRangeMake(0, string.length);
CFLocaleRef localeRef = (__bridge CFLocaleRef)(locale);
for(int i = 0; i < string.length; i++)
{
CFIndex location = CFStringGetHyphenationLocationBeforeIndex((CFStringRef)string, i, range, 0, localeRef, NULL);
if(location >= 0 && location < string.length)
{
hyphenationLocations[location] = 1;
}
}
for(int i = string.length - 1; i > 0; i--)
{
if(hyphenationLocations[i])
{
[string insertString:NSStringSoftHyphenationToken atIndex:i];
}
}
if(error != NULL) { *error = nil; }
// Left here in case you want to test with visible results
// return [string stringByReplacingOccurrencesOfString:NSStringSoftHyphenationToken withString:#"-"];
return string;
}
}
- (NSError *)hyphen_createOnlyError
{
NSDictionary *userInfo = #{
NSLocalizedDescriptionKey: #"Hyphenation is not available for given locale",
NSLocalizedFailureReasonErrorKey: #"Hyphenation is not available for given locale",
NSLocalizedRecoverySuggestionErrorKey: #"You could try using a different locale even though it might not be 100% correct"
};
return [NSError errorWithDomain:NSStringSoftHyphenationErrorDomain code:NSStringSoftHyphenationErrorNotAvailableForLocale userInfo:userInfo];
}
#end
:)

Related

How to get response from .clp(CLIPS) file?

I am trying to load .clp file in my iPhone application. For that I am using below code
NSString *filePath = [[NSBundle mainBundle]
pathForResource:#"autodemo" ofType:#"clp"];
environment = CreateEnvironment();
char *clipsFileChar = (char *)[filePath cStringUsingEncoding:NSASCIIStringEncoding];
Load(clipsFileChar);
Reset();
Run(-1);
NSString *evalS = [NSString stringWithFormat:#"(find-all-facts ((?f state-list)) TRUE)"];
char * evalStr = (char *)evalS;
DATA_OBJECT obj;// = {0,-1};
// obj.type = STRING;
// obj.value = evalStr;
int i = Eval(evalStr, &obj);
NSLog(#"%d",i);
now when I run this code Eval(evalStr, &obj) gives me 0 every time.
I am using autodemo.clp file from this link.
So, how to make Eval() command work and how do I get response returned by clp file?
thanks,
below code solved my problem, hope it will help to someone else.. :)
InitializeEnvironment();
Clear();
NSString *filePath = [[NSBundle mainBundle]
pathForResource:#"autodemo" ofType:#"clp"];
char *clipsFileChar = (char *)[filePath cStringUsingEncoding:NSASCIIStringEncoding];
IncrementGCLocks();
Load(clipsFileChar);
Reset();
Run(-1);
DecrementGCLocks();
[self nextUIState];
- (void)nextUIState
{
DATA_OBJECT theDO;
NSString * evalS = #"(find-all-facts ((?f state-list)) TRUE)";
char *evalStr = (char *)[evalS cStringUsingEncoding:NSASCIIStringEncoding];
int j = EnvEval(GetCurrentEnvironment(), evalStr, &theDO);
NSLog(#"j = %d",j);
if(factDict)
{
[factDict release];
factDict = nil;
factDict = [[NSMutableDictionary alloc] init];
}
id value = [self objectForDataObject:&theDO];
NSLog(#"%#",[value description]);
}
-(id) objectForDataObject: (DATA_OBJECT*) arg
{
switch(arg->type)
{
case FACT_ADDRESS:
{
DATA_OBJECT data = { 0 };
struct fact* aFact = (struct fact*) arg->value;
if(EnvGetFactSlot(GetCurrentEnvironment(),aFact,(char*)[#"current" UTF8String],&data))
{
[factDict setObject:[self objectForDataObject: &data] forKey:#"current"];
[factDict retain];
}
return factDict;
}
case SYMBOL:
{
NSString *str = [NSString stringWithUTF8String: ValueToString(arg->value)];
if ([str isEqual: #"nil"]) return nil;
if ([str hasPrefix: #"<<<"] && [str hasSuffix: #">>>"])
{
return [self dataFromSymbolString: str];
}
return str;
}
case STRING:
{
return [NSString stringWithUTF8String: ValueToString(arg->value)];
}
case INTEGER:
{
return [NSNumber numberWithInt: ValueToInteger(arg->value)];
}
case FLOAT:
{
return [NSNumber numberWithDouble: ValueToDouble(arg->value)];
}
case EXTERNAL_ADDRESS:
{
return (id) arg->value;
}
case MULTIFIELD:
{
int i, count = GetpDOLength(arg);
NSMutableArray *args = [NSMutableArray arrayWithCapacity: count];
FIELD_PTR fptr = (FIELD_PTR) GetMFPtr(GetpValue(arg),GetpDOBegin(arg));
for(i = 0; i < count; ++i, ++fptr)
{
DATA_OBJECT dobj;
dobj.type = fptr->type;
dobj.value = fptr->value;
[args addObject: [self objectForDataObject: &dobj]];
}
return args;
}
default:
return nil;
}
}
If you find out any other and better way(ofcourse there is), please let me know. :)

-[NSCFDictionary length]: unrecognized selector

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.

NSString range of string at occurrence

i'm trying to build a function that will tell me the range of a string at an occurrence.
For example if I had the string "hello, hello, hello", I want to know the range of hello at it's, lets say, third occurrence.
I've tried building this simple function, but it doesn't work.
Note - the top functions were constructed at an earlier date and work fine.
Any help appreciated.
- (NSString *)stringByTrimmingString:(NSString *)stringToTrim toChar:(NSUInteger)toCharacterIndex {
if (toCharacterIndex > [stringToTrim length]) return #"";
NSString *devString = [[[NSString alloc] init] autorelease];
for (int i = 0; i <= toCharacterIndex; i++) {
devString = [NSString stringWithFormat:#"%#%#", devString, [NSString stringWithFormat:#"%c", [stringToTrim characterAtIndex:(i-1)]]];
}
return devString;
[devString release];
}
- (NSString *)stringByTrimmingString:(NSString *)stringToTrim fromChar:(NSUInteger)fromCharacterIndex {
if (fromCharacterIndex > [stringToTrim length]) return #"";
NSString *devString = [[[NSString alloc] init] autorelease];
for (int i = (fromCharacterIndex+1); i <= [stringToTrim length]; i++) {
devString = [NSString stringWithFormat:#"%#%#", devString, [NSString stringWithFormat:#"%c", [stringToTrim characterAtIndex:(i-1)]]];
}
return devString;
[devString release];
}
- (NSRange)rangeOfString:(NSString *)substring inString:(NSString *)string atOccurence:(int)occurence {
NSString *trimmedString = [inString copy]; //We start with the whole string.
NSUInteger len, loc, oldLength;
len = 0;
loc = 0;
NSRange tempRange = [string rangeOfString:substring];
len = tempRange.length;
loc = tempRange.location;
for (int i = 0; i != occurence; i++) {
NSUInteger endOfWord = len+loc;
trimmedString = [self stringByTrimmingString:trimmedString fromChar:endOfWord];
oldLength += [[self stringByTrimmingString:trimmedString toChar:endOfWord] length];
NSRange tmp = [trimmedString rangeOfString:substring];
len = tmp.length;
loc = tmp.location + oldLength;
}
NSRange returnRange = NSMakeRange(loc, len);
return returnRange;
}
Instead of trimming the string a bunch of times (slow), just use rangeOfString:options:range:, which searches only within the range passed as its third argument. See Apple's documentation.
So try:
- (NSRange)rangeOfString:(NSString *)substring
inString:(NSString *)string
atOccurence:(int)occurence
{
int currentOccurence = 0;
NSRange rangeToSearchWithin = NSMakeRange(0, string.length);
while (YES)
{
currentOccurence++;
NSRange searchResult = [string rangeOfString: substring
options: NULL
range: rangeToSearchWithin];
if (searchResult.location == NSNotFound)
{
return searchResult;
}
if (currentOccurence == occurence)
{
return searchResult;
}
int newLocationToStartAt = searchResult.location + searchResult.length;
rangeToSearchWithin = NSMakeRange(newLocationToStartAt, string.length - newLocationToStartAt);
}
}
You need to rework the whole code. While it may seem to work, it's poor coding and plain wrong, like permanently reassigning the same variable, initializing but reassigning one line later, releasing after returning (which will never work).
For your question: Just use rangeOfString:options:range:, and do this the appropriate number of times while just incrementing the starting point.

How to convert an NSString to hex values

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.

Best way to serialize an NSData into a hexadeximal string

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.