การแปลงฐาน 62


92

คุณจะแปลงจำนวนเต็มเป็นฐาน 62 ได้อย่างไร (เช่นเลขฐานสิบหก แต่ใช้ตัวเลขเหล่านี้: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')

ฉันพยายามหาไลบรารี Python ที่ดีสำหรับมัน แต่ดูเหมือนว่าทั้งหมดจะถูกครอบครองด้วยการแปลงสตริง โมดูล Python base64 ยอมรับเฉพาะสตริงและเปลี่ยนตัวเลขหลักเดียวเป็นสี่อักขระ ฉันกำลังมองหาบางอย่างที่คล้ายกับสิ่งที่ตัวย่อ URL ใช้


ฟังดูเหมือนมีคนเพิ่งค้นพบแนวคิดโครงการโอเพ่นซอร์ส :) แจ้งให้เราทราบหากคุณพบอะไรหรือตัดสินใจสร้างของคุณเอง ...
samoz

หากคุณต้องการสร้าง URL สั้น ๆ คุณอาจต้องการใช้ทั้งชุดของตัวละครที่ไม่ต้องมีการเข้ารหัส: en.wikipedia.org/wiki/Percent-encoding#Types_of_URI_characters นั่นคือ 66 อักขระ
l0b0

ฉันคิดว่าฉันจะส่งต่อจุดและเครื่องหมายทิลเดอเพื่อหลีกเลี่ยงความสับสนของผู้ใช้ แต่เส้นประและขีดล่างควรเป็นส่วนเสริมที่คุ้มค่าขอบคุณ
mikl

แล้ว Base64 ล่ะ? คุณอาจโชคดีในการหาห้องสมุดสำหรับสิ่งนั้น
Mike Cooper

คำถามนี้มีคำตอบที่เกี่ยวข้องจำนวนมาก: stackoverflow.com/questions/561486/…
ไมล์

คำตอบ:


169

ไม่มีโมดูลมาตรฐานสำหรับสิ่งนี้ แต่ฉันได้เขียนฟังก์ชันของตัวเองเพื่อให้บรรลุเป้าหมายนั้น

BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

def encode(num, alphabet):
    """Encode a positive number into Base X and return the string.

    Arguments:
    - `num`: The number to encode
    - `alphabet`: The alphabet to use for encoding
    """
    if num == 0:
        return alphabet[0]
    arr = []
    arr_append = arr.append  # Extract bound-method for faster access.
    _divmod = divmod  # Access to locals is faster.
    base = len(alphabet)
    while num:
        num, rem = _divmod(num, base)
        arr_append(alphabet[rem])
    arr.reverse()
    return ''.join(arr)

def decode(string, alphabet=BASE62):
    """Decode a Base X encoded string into the number

    Arguments:
    - `string`: The encoded string
    - `alphabet`: The alphabet to use for decoding
    """
    base = len(alphabet)
    strlen = len(string)
    num = 0

    idx = 0
    for char in string:
        power = (strlen - (idx + 1))
        num += alphabet.index(char) * (base ** power)
        idx += 1

    return num

สังเกตความจริงที่ว่าคุณสามารถให้ตัวอักษรใดก็ได้เพื่อใช้ในการเข้ารหัสและถอดรหัส หากคุณปล่อยalphabetอาร์กิวเมนต์ไว้คุณจะได้รับอักขระ 62 ตัวที่กำหนดไว้ในบรรทัดแรกของโค้ดและด้วยเหตุนี้การเข้ารหัส / ถอดรหัสเป็น / จาก 62 ฐาน

หวังว่านี่จะช่วยได้

ป.ล. - สำหรับตัวย่อ URL ฉันพบว่ามันเป็นการดีกว่าที่จะทิ้งอักขระที่สับสนเช่น 0Ol1oI เป็นต้นดังนั้นฉันจึงใช้ตัวอักษรนี้สำหรับความต้องการในการย่อ URL ของฉัน - "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"

มีความสุข.


5
+1: ดี! สิ่งนี้สามารถขยายได้ด้วยอักขระที่เป็นมิตรกับ URL มากขึ้นเพื่อให้สามารถบันทึกอักขระหนึ่งตัวที่นี่ ฉันรู้ว่าตัวละครมีความปลอดภัยคือ: $-_.+!*'(),;/?:@&= คุณอาจจะสามารถใช้ตัวอักษรอื่น ๆ บางเกินไปเช่น[]~ฯลฯ
Blixt

24
การตั้งชื่อจุดบกพร่อง: ไม่ใช่ฐาน 62 เนื่องจากตัวอักษรสามารถปรับแต่งได้
คลาย

3
สำหรับการถอดรหัสเป็นนิสัยที่ดีกว่าที่จะไม่คำนวณพาวเวอร์ (ประหยัดเวลาเขียนสั้นลง แต่ที่สำคัญกว่าคือหลีกเลี่ยงข้อผิดพลาดทีละรายการ) ดังนั้น: num = 0; สำหรับ char ในสตริง: num = num * base + alphabet.index (ถ่าน)
ShreevatsaR

1
@ShreevatsaR: เหตุผลพิเศษในการใช้ str.index () แทนการค้นหาพจนานุกรม? ดูคำตอบของฉัน ...
John Machin

2
โจนาธาน - งูใหญ่สามารถจัดการกับตัวเลขของความยาวโดยพลการ - ไม่มีล้น >>> 256 * (62 ** 100) 44402652562862911414971048359760030835982580330786570771137804709455598239929932673552190201125730101070867075377228748911717860448985185350731601887476350502973424822800696272224256L
แอนโธนีบริกส์

53

ฉันเคยเขียนสคริปต์เพื่อทำสิ่งนี้เหมือนกันฉันคิดว่ามันค่อนข้างสง่างาม :)

import string
# Remove the `_@` below for base62, now it has 64 characters
BASE_LIST = string.digits + string.letters + '_@'
BASE_DICT = dict((c, i) for i, c in enumerate(BASE_LIST))

def base_decode(string, reverse_base=BASE_DICT):
    length = len(reverse_base)
    ret = 0
    for i, c in enumerate(string[::-1]):
        ret += (length ** i) * reverse_base[c]

    return ret

def base_encode(integer, base=BASE_LIST):
    if integer == 0:
        return base[0]

    length = len(base)
    ret = ''
    while integer != 0:
        ret = base[integer % length] + ret
        integer /= length

    return ret

ตัวอย่างการใช้งาน:

for i in range(100):                                    
    print i, base_decode(base_encode(i)), base_encode(i)

9
เวอร์ชันนี้เร็วกว่าโซลูชันที่ยอมรับจาก Baishampayan มาก ฉันปรับให้เหมาะสมเพิ่มเติมโดยการคำนวณความยาวนอกฟังก์ชัน ผลการทดสอบ (การทำซ้ำ 100,000 ครั้ง): version-WoLpH: .403 .399 .399 .398 .398 | รุ่น Baishampayan: 1.783 1.785 1.782 1.788 1.784 เวอร์ชันนี้เร็วประมาณ 4 เท่า
จอร์แดน

ถ้าใช้reversed(string)เร็วกว่าการแบ่งส่วนstring[::-1]ในฟังก์ชัน base_decode
ENDOH takanao

1
ฉันใช้เวลานานมากในการค้นหาคำถามนี้ ไม่เคยรู้มาก่อนว่าสิ่งนี้เรียกว่าการแปลง base62 คำตอบที่ดี

1
ฉันต้องเปลี่ยนinteger /= lengthเพื่อinteger //=lengthรับส่วนที่เหลือที่ถูกต้อง
karlgold

10

เครื่องถอดรหัสต่อไปนี้ทำงานร่วมกับฐานที่สมเหตุสมผลมีการวนซ้ำที่เป็นระเบียบมากขึ้นและให้ข้อความแสดงข้อผิดพลาดที่ชัดเจนเมื่อตรงกับอักขระที่ไม่ถูกต้อง

def base_n_decoder(alphabet):
    """Return a decoder for a base-n encoded string
    Argument:
    - `alphabet`: The alphabet used for encoding
    """
    base = len(alphabet)
    char_value = dict(((c, v) for v, c in enumerate(alphabet)))
    def f(string):
        num = 0
        try:
            for char in string:
                num = num * base + char_value[char]
        except KeyError:
            raise ValueError('Unexpected character %r' % char)
        return num
    return f

if __name__ == "__main__":
    func = base_n_decoder('0123456789abcdef')
    for test in ('0', 'f', '2020', 'ffff', 'abqdef'):
        print test
        print func(test)

แม้ว่าฉันจะไม่เคยใช้สิ่งนี้ แต่ฉันก็ยกนิ้วให้กับความคิดสร้างสรรค์เช่นกัน รหัสนี้ทำให้ฉันหัวเราะ :)
Sepero

@ เซเปโร: ตลกอะไร? เป็นซอฟต์แวร์ที่มีความแข็งแกร่งทางอุตสาหกรรมอย่างจริงจัง ไม่มีการย้อนกลับของ Micky-Mouse ด้วยตัว**ดำเนินการในลูป
John Machin

ใจเย็น ๆ นะเพื่อน คุณถูก. ฉันพลาดความดีที่แท้จริงของวงในของคุณเนื่องจากมันถูกฝังอยู่ในสิ่งที่ไม่เกี่ยวข้องกับคำถาม (การห่อการตรวจสอบข้อผิดพลาดการทดสอบหน่วย)
Sepero

ดูดี แต่คุณยังไม่ลืมตัวเข้ารหัส "ความแข็งแกร่งทางอุตสาหกรรม" ซึ่งใช้ตัวอักษรจำนวนเต็มบวกในการสร้างสตริงหรือไม่?
martineau

1
q ในค่าสุดท้ายมีเจตนาที่จะแสดง ValueError ที่เพิ่มขึ้นหรือไม่
Thomas Vander Stichele

8

หากคุณกำลังมองหาประสิทธิภาพสูงสุด (เช่น django) คุณจะต้องการสิ่งต่อไปนี้ รหัสนี้เป็นการผสมผสานระหว่างวิธีการที่มีประสิทธิภาพจาก Baishampayan Ghose และ WoLpH และ John Machin

# Edit this list of characters as desired.
BASE_ALPH = tuple("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
BASE_DICT = dict((c, v) for v, c in enumerate(BASE_ALPH))
BASE_LEN = len(BASE_ALPH)

def base_decode(string):
    num = 0
    for char in string:
        num = num * BASE_LEN + BASE_DICT[char]
    return num

def base_encode(num):
    if not num:
        return BASE_ALPH[0]

    encoding = ""
    while num:
        num, rem = divmod(num, BASE_LEN)
        encoding = BASE_ALPH[rem] + encoding
    return encoding

คุณอาจต้องการคำนวณพจนานุกรมของคุณล่วงหน้า (หมายเหตุ: การเข้ารหัสด้วยสตริงจะมีประสิทธิภาพมากกว่าการแสดงรายการแม้ว่าจะมีตัวเลขที่ยาวมากก็ตาม)

>>> timeit.timeit("for i in xrange(1000000): base.base_decode(base.base_encode(i))", setup="import base", number=1)
2.3302059173583984

เข้ารหัสและถอดรหัส 1 ล้านหมายเลขภายใน 2.5 วินาที (2.2Ghz i7-2670QM)


เราไม่จำเป็นต้องมีสิ่งtuple()รอบตัวBASE_ALPHในตอนเริ่มต้น ใน Python ทุก String สามารถทำซ้ำได้ enumerate()คุณลักษณะที่จะใช้ประโยชน์ของการเรียนการสอนโดย ดังนั้นรหัสจึงยิ่งน้อยลง :)
Luis Nell

7
เฮ้ origiNell คุณคิดถูกแล้วที่ไม่จำเป็นต้องใช้ทูเปิล () แต่ในระบบของฉันมันทำให้โค้ดทำงานเร็วขึ้นประมาณ 20% ลองทดสอบโดยไม่ใช้ทูเปิล () และดูว่าอะไรดีที่สุดสำหรับคุณ ไชโย :)
Sepero

1
จุดที่น่าสนใจ. มีความหมายโดยรวมเนื่องจากสิ่งที่เพิ่มขึ้นมีน้ำหนักเบากว่าสตริง ขอบคุณสำหรับการตรัสรู้ :)!
Luis Nell

@Sepero ฉันปรับปรุงเวอร์ชันของคุณเพิ่มเติมในแง่ของการจัดรูปแบบการตั้งชื่อการทดสอบและการทำงาน (รองรับตัวเลขเชิงลบ): pastebin.com/4uket7iu (คุณสามารถอัปเดตคำตอบของคุณด้วยสิ่งนี้)
Joschua

@Joschua - รหัสของคุณที่ URL ของคุณใช้ไม่ได้กับฉัน base_encode () ดูเหมือนจะสร้างตัวเลขที่เข้ารหัสเพียงตัวเดียวสำหรับตัวเลขที่ฉันทดสอบ
SMGreenfield

4

หากสิ่งที่คุณต้องการคือสร้าง ID แบบสั้น (เนื่องจากคุณพูดถึงตัวย่อ URL) แทนที่จะเข้ารหัส / ถอดรหัสบางสิ่งโมดูลนี้อาจช่วยได้:

https://github.com/stochastic-technologies/shortuuid/


ฉันไม่แน่ใจว่าเหมาะสำหรับ URL แบบสั้น UUID มักเป็นตัวเลขที่มีขนาดใหญ่มากดังนั้นแม้การเข้ารหัส base57 อย่างที่เขาทำจะต้องค่อนข้างยาวสำหรับ URL แบบสั้น
mikl

คุณสามารถตัดได้มากเท่าที่คุณต้องการการชนกันจะยังคงไม่น่าเกิดขึ้นเนื่องจากเป็นการสุ่มอย่างแท้จริง แต่จะไม่เป็นรหัสเฉพาะอีกต่อไป
Stavros Korokithakis

4

หากคุณใช้ django framework คุณสามารถใช้โมดูล django.utils.baseconv

>>> from django.utils import baseconv
>>> baseconv.base62.encode(1234567890)
1LY7VK

นอกจาก base62 แล้ว baseconv ยังกำหนด base2 / base16 / base36 / base56 / base64


3

คุณอาจต้องการ base64 ไม่ใช่ base62 มีเวอร์ชันที่เข้ากันได้กับ URL ที่ลอยอยู่รอบ ๆ ดังนั้นอักขระฟิลเลอร์พิเศษสองตัวจึงไม่น่าจะเป็นปัญหา

กระบวนการนี้ค่อนข้างง่าย พิจารณาว่า base64 แทน 6 บิตและไบต์ปกติแสดงถึง 8 กำหนดค่าจาก 000000 ถึง 111111 ให้กับอักขระ 64 ตัวที่เลือกและใส่ค่า 4 ค่าเข้าด้วยกันเพื่อจับคู่ชุด 3 base256 ไบต์ ทำซ้ำสำหรับชุดละ 3 ไบต์โดยเว้นส่วนท้ายด้วยอักขระช่องว่างภายในที่คุณเลือก (0 โดยทั่วไปมีประโยชน์)


5
วิธีการเข้ารหัส Python base64 มาตรฐานไม่เหมาะสำหรับ URL แบบสั้นเนื่องจากได้รับการปรับให้เหมาะสมกับการเข้ารหัสไบต์ (เช่นสตริง / ตัวอักษร) และจะให้ผลลัพธ์ที่ยาวกว่าการเปลี่ยนค่าตัวเลขเพียงฐาน
mikl

@mikl แน่นอนว่าโมดูล base64 ของ Python อาจไม่เหมาะสำหรับการสร้าง URL แบบสั้น แต่วิธีการเข้ารหัสทั้งหมดของ Python นั้นใช้งานได้กับลำดับเลขฐาน 256 ไบต์เป็น "สตริง" ที่เข้ารหัสฐาน 256 Python 2.x ถือว่าสตริงเป็นลำดับของไบต์ในขณะที่ Python 3.x (ซึ่งทำสิ่งที่ถูกต้อง) ถือว่าสตริงเป็น Unicode ดังนั้น b'foobar จึงเป็นเพียงวิธีการเขียนที่แปลกใหม่ [102, 111, 111, 98, 97, 114] หรือ [0x66,0x6f, 0x6f, 0x62,0x61,0x72] หรือ b '\ x66 \ x6f \ x6f \ x62 \ x61 \ x72 'ซึ่งไม่น่าแปลกใจเลยคือการแทนค่าฐาน 256 ไบต์ไม่ใช่สตริงหรือตัวอักษร ไบต์คือไบต์ =)
yesudeep

@yesudeep: ไบต์คือไบต์…แล้วประเด็นของคุณคืออะไร?
martineau

3

ขณะนี้มีไลบรารี python สำหรับสิ่งนี้

ฉันกำลังทำแพ็คเกจ pip สำหรับสิ่งนี้

ฉันแนะนำให้คุณใช้ bases.py https://github.com/kamijoutouma/bases.pyซึ่งได้รับแรงบันดาลใจจาก bases.js

from bases import Bases
bases = Bases()

bases.toBase16(200)                // => 'c8'
bases.toBase(200, 16)              // => 'c8'
bases.toBase62(99999)              // => 'q0T'
bases.toBase(200, 62)              // => 'q0T'
bases.toAlphabet(300, 'aAbBcC')    // => 'Abba'

bases.fromBase16('c8')               // => 200
bases.fromBase('c8', 16)             // => 200
bases.fromBase62('q0T')              // => 99999
bases.fromBase('q0T', 62)            // => 99999
bases.fromAlphabet('Abba', 'aAbBcC') // => 300

อ้างถึงhttps://github.com/kamijoutouma/bases.py#known-basesalphabets สำหรับฐานที่ใช้งานได้


2

คุณสามารถดาวน์โหลดโมดูล zbase62 จากpypi

เช่น

>>> import zbase62
>>> zbase62.b2a("abcd")
'1mZPsa'

2
ใช่ฉันดูก่อนหน้านี้ แต่มันแปลงสตริงไม่ใช่ตัวเลข :)
mikl

2

ฉันได้รับประโยชน์อย่างมากจากโพสต์ของผู้อื่นที่นี่ เดิมทีฉันต้องการรหัส python สำหรับโครงการ Django แต่ตั้งแต่นั้นมาฉันก็หันไปใช้ node.js ดังนั้นนี่คือโค้ดเวอร์ชันจาวาสคริปต์ (ส่วนการเข้ารหัส) ที่ Baishampayan Ghose ให้มา

var ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

function base62_encode(n, alpha) {
  var num = n || 0;
  var alphabet = alpha || ALPHABET;

  if (num == 0) return alphabet[0];
  var arr = [];
  var base = alphabet.length;

  while(num) {
    rem = num % base;
    num = (num - rem)/base;
    arr.push(alphabet.substring(rem,rem+1));
  }

  return arr.reverse().join('');
}

console.log(base62_encode(2390687438976, "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ"));

ฉันได้อัปเดตรหัสนี้และทำให้เป็นโครงการโอเพ่นซอร์สสำหรับทุกคนที่สนใจgithub.com/sbussard/encode-the-things
Stephen

2

ฉันหวังว่าตัวอย่างต่อไปนี้จะช่วยได้

def num2sym(num, sym, join_symbol=''):
    if num == 0:
        return sym[0]
    if num < 0 or type(num) not in (int, long):
        raise ValueError('num must be positive integer')

    l = len(sym)  # target number base
    r = []
    div = num
    while div != 0: # base conversion
        div, mod = divmod(div, l)
        r.append(sym[mod])

    return join_symbol.join([x for x in reversed(r)])

การใช้งานสำหรับกรณีของคุณ:

number = 367891
alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
print num2sym(number, alphabet)  # will print '1xHJ'

เห็นได้ชัดว่าคุณสามารถระบุตัวอักษรอื่นซึ่งประกอบด้วยสัญลักษณ์จำนวนน้อยกว่าหรือมากกว่าจากนั้นมันจะแปลงตัวเลขของคุณเป็นฐานตัวเลขที่น้อยกว่าหรือมากกว่า ตัวอย่างเช่นการระบุ '01' เป็นตัวอักษรจะส่งออกสตริงที่แสดงหมายเลขอินพุตเป็นไบนารี

คุณสามารถสลับตัวอักษรในตอนแรกเพื่อให้มีการแสดงตัวเลขเฉพาะของคุณ จะมีประโยชน์หากคุณกำลังให้บริการย่อ URL


1
ไม่เลว. if num < 0 or type(num) not in (int, long):คุณอาจต้องการที่จะใช้
martineau

ดีกว่า แต่ซับซ้อนกว่าเล็กน้อยเพราะlongไม่มีอยู่ใน Py 3.x ดังนั้นอาจต้องการใช้คำตอบนี้
martineau

1
หรือใช้เวอร์ชันพกพาของฉันเอง: isinstance(x, (type(1), type(2**32))).
martineau

2

นี่คือวิธีแก้ปัญหาของฉัน:

def base62(a):
    baseit = (lambda a=a, b=62: (not a) and '0' or
        baseit(a-a%b, b*62) + '0123456789abcdefghijklmnopqrstuvwxyz'
                              'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[a%b%61 or -1*bool(a%b)])
    return baseit()

คำอธิบาย

ในฐานใด ๆ ทุกจำนวนจะเท่ากับ a1+a2*base**2+a3*base**3...ดังนั้นเป้าหมายคือการหาas ทั้งหมด

ทุกN=1,2,3...รหัสแยกaN*base**Nโดย "moduloing" โดยbสำหรับb=base**(N+1)ที่ชิ้นทั้งหมดas มีขนาดใหญ่กว่าNและหั่นทั้งหมดas เพื่อให้อนุกรมของพวกเขามีขนาดเล็กกว่าNโดยการลดทุกฟังก์ชั่นที่เรียกว่าซ้ำโดยในปัจจุบันaaN*base**N

Base%(base-1)==1ดังนั้นbase**p%(base-1)==1จึงq*base^p%(base-1)==qมีเพียงหนึ่งข้อยกเว้นเมื่อซึ่งผลตอบแทนq==base-1 0เพื่อแก้ไขกรณีนั้นจะกลับ0มา ฟังก์ชันจะตรวจสอบ0ตั้งแต่เริ่มต้น


ข้อดี

ในตัวอย่างนี้มีการคูณเพียงครั้งเดียว (แทนที่จะเป็นการหาร) และการดำเนินการโมดูลัสบางส่วนซึ่งทั้งหมดค่อนข้างเร็ว


1

โดยส่วนตัวชอบการแก้ปัญหาจาก Baishampayan เป็นส่วนใหญ่เนื่องจากการลอกตัวละครที่สับสน

เพื่อความสมบูรณ์และการแก้ปัญหาที่มีประสิทธิภาพที่ดีขึ้นโพสต์นี้จะแสดงวิธีใช้โมดูล Python base64


1
ดังที่ได้กล่าวไว้ในความคิดเห็นของฉันเกี่ยวกับ Williham Totland Pythons base64 นั้นไม่เหมาะสมสำหรับการเข้ารหัสตัวเลขเนื่องจากได้รับการปรับให้เหมาะสมกับสตริง
mikl

1

ฉันเขียนสิ่งนี้ในขณะที่ย้อนกลับไปและมันก็ใช้ได้ดี (เชิงลบและรวมทั้งหมด)

def code(number,base):
    try:
        int(number),int(base)
    except ValueError:
        raise ValueError('code(number,base): number and base must be in base10')
    else:
        number,base = int(number),int(base)
    if base < 2:
        base = 2
    if base > 62:
        base = 62
    numbers = [0,1,2,3,4,5,6,7,8,9,"a","b","c","d","e","f","g","h","i","j",
               "k","l","m","n","o","p","q","r","s","t","u","v","w","x","y",
               "z","A","B","C","D","E","F","G","H","I","J","K","L","M","N",
               "O","P","Q","R","S","T","U","V","W","X","Y","Z"]
    final = ""
    loc = 0
    if number < 0:
        final = "-"
        number = abs(number)
    while base**loc <= number:
        loc = loc + 1
    for x in range(loc-1,-1,-1):
        for y in range(base-1,-1,-1):
            if y*(base**x) <= number:
                final = "{}{}".format(final,numbers[y])
                number = number - y*(base**x)
                break
    return final

def decode(number,base):
    try:
        int(base)
    except ValueError:
        raise ValueError('decode(value,base): base must be in base10')
    else:
        base = int(base)
    number = str(number)
    if base < 2:
        base = 2
    if base > 62:
        base = 62
    numbers = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f",
               "g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v",
               "w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L",
               "M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
    final = 0
    if number.startswith("-"):
        neg = True
        number = list(number)
        del(number[0])
        temp = number
        number = ""
        for x in temp:
            number = "{}{}".format(number,x)
    else:
        neg = False
    loc = len(number)-1
    number = str(number)
    for x in number:
        if numbers.index(x) > base:
            raise ValueError('{} is out of base{} range'.format(x,str(base)))
        final = final+(numbers.index(x)*(base**loc))
        loc = loc - 1
    if neg:
        return -final
    else:
        return final

ขออภัยเกี่ยวกับความยาวทั้งหมด


1
BASE_LIST = tuple("23456789ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz")
BASE_DICT = dict((c, v) for v, c in enumerate(BASE_LIST))
BASE_LEN = len(BASE_LIST)

def nice_decode(str):
    num = 0
    for char in str[::-1]:
        num = num * BASE_LEN + BASE_DICT[char]
    return num

def nice_encode(num):
    if not num:
        return BASE_LIST[0]

    encoding = ""
    while num:
        num, rem = divmod(num, BASE_LEN)
        encoding += BASE_LIST[rem]
    return encoding

1
สิ่งนี้แก้ไขชื่อของ BASE_LIST และยังย้อนกลับสตริงในการถอดรหัสซึ่งถูกละไว้ในคำตอบที่ยอดเยี่ยมอย่างอื่นของ Spero
paulkav1

1

นี่คือวิธีการทำซ้ำและซ้ำ ๆ การทำซ้ำเร็วขึ้นเล็กน้อยขึ้นอยู่กับจำนวนการดำเนินการ

def base62_encode_r(dec):
    s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    return s[dec] if dec < 62 else base62_encode_r(dec / 62) + s[dec % 62]
print base62_encode_r(2347878234)

def base62_encode_i(dec):
    s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    ret = ''
    while dec > 0:
        ret = s[dec % 62] + ret
        dec /= 62
    return ret
print base62_encode_i(2347878234)

def base62_decode_r(b62):
    s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    if len(b62) == 1:
        return s.index(b62)
    x = base62_decode_r(b62[:-1]) * 62 + s.index(b62[-1:]) % 62
    return x
print base62_decode_r("2yTsnM")

def base62_decode_i(b62):
    s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    ret = 0
    for i in xrange(len(b62)-1,-1,-1):
        ret = ret + s.index(b62[i]) * (62**(len(b62)-i-1))
    return ret
print base62_decode_i("2yTsnM")

if __name__ == '__main__':
    import timeit
    print(timeit.timeit(stmt="base62_encode_r(2347878234)", setup="from __main__ import base62_encode_r", number=100000))
    print(timeit.timeit(stmt="base62_encode_i(2347878234)", setup="from __main__ import base62_encode_i", number=100000))
    print(timeit.timeit(stmt="base62_decode_r('2yTsnM')", setup="from __main__ import base62_decode_r", number=100000))
    print(timeit.timeit(stmt="base62_decode_i('2yTsnM')", setup="from __main__ import base62_decode_i", number=100000))

0.270266867033
0.260915645986
0.344734796766
0.311662500262

ฉันชอบวิธีการเรียกซ้ำของคุณมาก ลูกสาวของฉันที่ใช้ AP Comp Sci ได้คิดหาวิธีแก้ปัญหาเดียวกันนี้ให้ฉันใช้ "base25" (โดยใช้ 'ABCDEFHJKMNPQRTUVWXY34789') ใน C ++ ฉันไปแปลงเป็น Python และการเป็น newb ทั้งหมดด้วยภาษานั้นทำให้เกิดการสะดุดสองสามอันซึ่งคุณแก้ไขได้อย่างสวยงามในโค้ดบรรทัดเดียว! คุณยังหลีกเลี่ยงปัญหาทั่วไปที่ 0 แปลเป็นสตริงว่างในตัวอักษรที่ไม่ได้ขึ้นต้นด้วย 0-9 การทำงานที่ดี! (ฉันไม่ต้องการตัวเลขติดลบ แต่แนวทางของคุณดีมากอาจเป็นการดีที่จะเพิ่มสิ่งนั้นสำหรับเบราว์เซอร์ในอนาคต)
SMGreenfield

1

Python 3.7.x

ผมพบว่า GitHub ของปริญญาเอกสำหรับขั้นตอนวิธีการบางอย่างเมื่อมองหาสคริปต์ base62 ที่มีอยู่ มันใช้ไม่ได้กับ Python 3 เวอร์ชันสูงสุดปัจจุบันในขณะนี้ดังนั้นฉันจึงดำเนินการแก้ไขตามที่จำเป็นและทำการ refactoring เล็กน้อย ปกติฉันไม่ได้ทำงานกับ Python และมักจะใช้มันเฉพาะกิจดังนั้น YMMV เครดิตทั้งหมดไปที่ดร. Zhihua Lai ฉันเพิ่งแก้ไขข้อบกพร่องสำหรับ Python เวอร์ชันนี้

ไฟล์ base62.py

#modified from Dr. Zhihua Lai's original on GitHub
from math import floor
base = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
b = 62;
def toBase10(b62: str) -> int:
    limit = len(b62)
    res = 0
    for i in range(limit):
        res = b * res + base.find(b62[i])
    return res
def toBase62(b10: int) -> str:
    if b <= 0 or b > 62:
        return 0
    r = b10 % b
    res = base[r];
    q = floor(b10 / b)
    while q:
        r = q % b
        q = floor(q / b)
        res = base[int(r)] + res
    return res

ไฟล์ try_base62.py

import base62
print("Base10 ==> Base62")
for i in range(999):
    print(f'{i} => {base62.toBase62(i)}')
base62_samples = ["gud", "GA", "mE", "lo", "lz", "OMFGWTFLMFAOENCODING"]
print("Base62 ==> Base10")
for i in range(len(base62_samples)):
    print(f'{base62_samples[i]} => {base62.toBase10(base62_samples[i])}')

เอาต์พุตของ try_base62.py

Base10 ==> Base62
0 => 0
[...]
998 => g6
Base62 ==> Base10
gud => 63377
GA => 2640
mE => 1404
lo => 1326
lz => 1337
OMFGWTFLMFAOENCODING => 577002768656147353068189971419611424

เนื่องจากไม่มีข้อมูลการออกใบอนุญาตใน repo ฉันจึงส่งPRดังนั้นอย่างน้อยผู้เขียนต้นฉบับก็รู้ว่าคนอื่นกำลังใช้และแก้ไขรหัสของพวกเขา


0

ขอโทษค่ะฉันช่วยคุณเรื่องห้องสมุดที่นี่ไม่ได้ ฉันต้องการใช้ base64 และเพิ่มตัวละครพิเศษตามที่คุณเลือก - ถ้าเป็นไปได้!

จากนั้นคุณสามารถใช้โมดูล base64

หากเป็นเช่นนั้นจริงๆทำไม่ได้:

คุณสามารถทำได้ด้วยตัวเองด้วยวิธีนี้ (นี่คือรหัสหลอก):

base62vals = []
myBase = 62
while num > 0:
   reminder = num % myBase
   num = num / myBase
   base62vals.insert(0, reminder)

0

ด้วยการเรียกซ้ำง่าย ๆ

"""
This module contains functions to transform a number to string and vice-versa
"""
BASE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
LEN_BASE = len(BASE)


def encode(num):
    """
    This function encodes the given number into alpha numeric string
    """

    if num < LEN_BASE:
        return BASE[num]

    return BASE[num % LEN_BASE] + encode(num//LEN_BASE)


def decode_recursive(string, index):
    """
    recursive util function for decode
    """

    if not string or index >= len(string):
        return 0

    return (BASE.index(string[index]) * LEN_BASE ** index) + decode_recursive(string, index + 1)


def decode(string):
    """
    This function decodes given string to number
    """

    return decode_recursive(string, 0)


0

ง่ายที่สุดที่เคยมีมา

BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
def encode_base62(num):
    s = ""
    while num>0:
      num,r = divmod(num,62)
      s = BASE62[r]+s
    return s


def decode_base62(num):
   x,s = 1,0
   for i in range(len(num)-1,-1,-1):
      s = int(BASE62.index(num[i])) *x + s
      x*=62
   return s

print(encode_base62(123))
print(decode_base62("1Z"))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.