Подтвердить что ты не робот

Преобразование базы 62 в Objective-C

Я потратил слишком много времени, пытаясь найти реализацию для преобразования base 62 для Objective-C. Я уверен, что это ужасный пример, и должен быть элегантный, суперэффективный способ сделать это, но это работает, пожалуйста, отредактируйте или ответьте, чтобы улучшить его! Но я хотел помочь людям найти это, чтобы иметь что-то, что сработает. Кажется, что ничего не найдено для реализации Objective-C.

@implementation Base62Converter

+(int)decode:(NSString*)string
{
    int num = 0;
    NSString * alphabet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

    for (int i = 0, len = [string length]; i < len; i++)
    {
        NSRange range = [alphabet rangeOfString:[string substringWithRange:NSMakeRange(i,1)]];
        num = num * 62 + range.location;
    }

    return num;
}

+(NSString*)encode:(int)num
{
    NSString * alphabet = @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    NSMutableString * precursor = [NSMutableString stringWithCapacity:3];

    while (num > 0)
    {
        [precursor appendString:[alphabet substringWithRange:NSMakeRange( num % 62, 1 )]];
        num /= 62;
    }

    // http://stackoverflow.com/info/6720191/reverse-nsstring-text
    NSMutableString *reversedString = [NSMutableString stringWithCapacity:[precursor length]];

    [precursor enumerateSubstringsInRange:NSMakeRange(0,[precursor length])
                             options:(NSStringEnumerationReverse |NSStringEnumerationByComposedCharacterSequences)
                          usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
                              [reversedString appendString:substring];
                          }];
    return reversedString;
}

@end
4b9b3361

Ответ 1

Ваш код в порядке. Если что-нибудь, сделайте его более общим. Вот рекурсивная версия для любой базы (тот же код):

#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)]];
}

@end

int 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

Ответ 2

Вы можете улучшить свой метод encode таким образом, чтобы не менять окончательную строку:

+ (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;
}

Конечно, это также можно было бы обобщить для произвольных оснований или алфавитов, как это было предложено @Jano в его ответе.

Обратите внимание, что этот метод (а также ваш оригинальный метод encode) возвращает пустую строку для num = 0, поэтому вам может понадобиться рассмотреть этот случай отдельно (или просто заменить while (num > 0) { ... } на do { ... } while (num > 0).


Для большей эффективности можно было избежать всех промежуточных NSString объектов в целом и работать с простыми строками C:

+ (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];
}