Base 62 conversion in Objective-C
Your code is fine. If anything, make it more generic. Here is a recursive version for any base (same code):
#import <Foundation/Foundation.h>@interface BaseConversion : NSObject+(NSString*) formatNumber:(NSUInteger)n toBase:(NSUInteger)base;+(NSString*) formatNumber:(NSUInteger)n usingAlphabet:(NSString*)alphabet;@end@implementation BaseConversion// Uses the alphabet length as base.+(NSString*) formatNumber:(NSUInteger)n usingAlphabet:(NSString*)alphabet{ NSUInteger base = [alphabet length]; if (n<base){ // direct conversion NSRange range = NSMakeRange(n, 1); return [alphabet substringWithRange:range]; } else { return [NSString stringWithFormat:@"%@%@", // Get the number minus the last digit and do a recursive call. // Note that division between integer drops the decimals, eg: 769/10 = 76 [self formatNumber:n/base usingAlphabet:alphabet], // Get the last digit and perform direct conversion with the result. [alphabet substringWithRange:NSMakeRange(n%base, 1)]]; }}+(NSString*) formatNumber:(NSUInteger)n toBase:(NSUInteger)base { NSString *alphabet = @"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 62 digits NSAssert([alphabet length]>=base,@"Not enough characters. Use base %ld or lower.",(unsigned long)[alphabet length]); return [self formatNumber:n usingAlphabet:[alphabet substringWithRange:NSMakeRange (0, base)]];}@endint main(int argc, char *argv[]) { @autoreleasepool { NSLog(@"%@",[BaseConversion formatNumber:3735928559 toBase:16]); // deadbeef return EXIT_SUCCESS; }}
A Swift 3 version: https://gist.github.com/j4n0/056475333d0ddfe963ac5dc44fa53bf2
You could improve your encode
method in such a way that reversing the final string is not necessary:
+ (NSString *)encode:(NSUInteger)num{ NSString *alphabet = @"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; NSUInteger base = [alphabet length]; NSMutableString *result = [NSMutableString string]; while (num > 0) { NSString *digit = [alphabet substringWithRange:NSMakeRange(num % base, 1)]; [result insertString:digit atIndex:0]; num /= base; } return result;}
Of course, this could also be generalized for arbitrary bases or alphabets, as suggested by @Jano in his answer.
Note that this method (as well as your original encode
method) returns an empty string for num = 0
, so you might want to consider this case separately (or just replace while (num > 0) { ... }
by do { ... } while (num > 0)
.
For more efficiency, one could avoid all intermediate NSString
objects altogether, and work with plain C strings:
+ (NSString *)encode:(NSUInteger)num{ static const char *alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; NSUInteger base = 62; char result[20]; // sufficient room to encode 2^64 in Base-62 char *p = result + sizeof(result); *--p = 0; // NULL termination while (num > 0) { *--p = alphabet[num % base]; num /= base; } return [NSString stringWithUTF8String:p];}