【问题标题】:Base 62 conversion in Objective-CObjective-C 中的 Base 62 转换
【发布时间】:2013-01-24 16:07:42
【问题描述】:

我花了太多时间试图为 Objective-C 找到 base 62 转换的实现。我确信这是一个糟糕的例子,必须有一种优雅、超高效的方法来做到这一点,但这有效,请编辑或回答以改进它!但我想帮助那些寻找这个的人有一些有用的东西。似乎没有针对 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/questions/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

【问题讨论】:

  • 我不熟悉 base 62 转换。它是做什么用的?
  • 用于制作长整数的短版本。你知道那些制作短网址的网站吗?他们会使用“1ly7vl”而不是“1234567891”。我刚刚意识到我正在使用ints,这将限制输入的大小,并且会在一定数量以上给出不好的结果......会纠正(或者有人打败我)
  • 因告诉我一些我从未听说过的事情而投票赞成!
  • 当然要与人混在一起(假设您不关心与其他系统的互操作性),您只需要使用字母表的随机版本;-)
  • 我建议改用字母 @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"。我这样说是因为大多数其他 b62 实现(最重要的是 rubygems.org/gems/base62)都使用这种排序。

标签: ios objective-c cocoa-touch


【解决方案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;
    }
}

Swift 3 版本:https://gist.github.com/j4n0/056475333d0ddfe963ac5dc44fa53bf2

【讨论】:

  • 我喜欢直接转换检查!聪明的。我建议将此命名为 **format**Number:toBase:。那么更通用的版本呢?这应该只是 formatNumber:toBase:usingAlphabet: 的包装器 :)
  • 甚至不需要给出基数——从字母表的长度推导出来。
  • 感谢 Josh 和 Hot Licks,我添加了您的建议。如果您愿意,请随时编辑答案。
【解决方案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 &gt; 0) { ... } 替换为do { ... } while (num &gt; 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];
}

【讨论】:

    猜你喜欢
    • 2010-11-10
    • 2013-01-06
    • 2014-11-22
    • 1970-01-01
    • 2018-03-23
    • 1970-01-01
    • 2011-02-19
    • 2012-03-22
    相关资源
    最近更新 更多