67

URLで整数を表す最短の方法が必要です。たとえば、11234は、16進数を使用して「2be2」に短縮できます。base64の使用は64文字のエンコーディングであるため、16進数よりもさらに少ない文字を使用してbase64で整数を表すことができるはずです。問題は、Pythonを使用して整数をbase64に(そして再び)変換する最もクリーンな方法を理解できないことです。

base64モジュールには、バイト文字列を処理するためのメソッドがあります。したがって、1つの解決策は、整数をPython文字列としてのバイナリ表現に変換することです...しかし、その方法もわかりません。

4

15 に答える 15

62

この回答は、精神的にはダグラスリーダーの回答と似ていますが、次の点が異なります。

  • 実際のBase64を使用しないため、パディング文字はありません
  • 最初に数値をバイト文字列(基数256)に変換する代わりに、基数64に直接変換します。これには、符号文字を使用して負の数を表すことができるという利点があります。

    import string
    ALPHABET = string.ascii_uppercase + string.ascii_lowercase + \
               string.digits + '-_'
    ALPHABET_REVERSE = dict((c, i) for (i, c) in enumerate(ALPHABET))
    BASE = len(ALPHABET)
    SIGN_CHARACTER = '$'
    
    def num_encode(n):
        if n < 0:
            return SIGN_CHARACTER + num_encode(-n)
        s = []
        while True:
            n, r = divmod(n, BASE)
            s.append(ALPHABET[r])
            if n == 0: break
        return ''.join(reversed(s))
    
    def num_decode(s):
        if s[0] == SIGN_CHARACTER:
            return -num_decode(s[1:])
        n = 0
        for c in s:
            n = n * BASE + ALPHABET_REVERSE[c]
        return n
    

    >>> num_encode(0)
    'A'
    >>> num_encode(64)
    'BA'
    >>> num_encode(-(64**5-1))
    '$_____'

いくつかのサイドノート:

  • string.digitsをアルファベットの最初に置く(そして符号文字を'-'にする)ことで、 (わずかに)base-64番号の人間の可読性を高めることができます。Pythonのurlsafe_b64encodeに基づいて行った順序を選択しました。
  • 多くの負の数をエンコードしている場合は、符号文字の代わりに符号ビットまたは1の2の補数を使用することで効率を上げることができます。
  • アルファベットを変更して、英数字のみに制限するか、「URLセーフ」文字を追加することで、このコードをさまざまなベースに簡単に適合させることができるはずです。
  • TinyURL風のものを使用する場合を除いて、ほとんどの場合、URIで10進数以外の表現を使用しないことをお勧めします。これにより、HTTPのオーバーヘッドと比較して、複雑さが増し、デバッグが難しくなります。
于 2009-02-18T16:00:18.453 に答える
15

これには実際のbase64エンコーディングはおそらく必要ありません-パディングなどが追加され、小さな数値の16進数よりも大きな文字列になる可能性さえあります. 他のものと相互運用する必要がない場合は、独自のエンコーディングを使用してください。例えば。これは、任意の基数にエンコードする関数です (余分な reverse() 呼び出しを避けるために、数字は実際には最下位から順に格納されることに注意してください:

def make_encoder(baseString):
    size = len(baseString)
    d = dict((ch, i) for (i, ch) in enumerate(baseString)) # Map from char -> value
    if len(d) != size:
        raise Exception("Duplicate characters in encoding string")

    def encode(x):
        if x==0: return baseString[0]  # Only needed if don't want '' for 0
        l=[]
        while x>0:
            l.append(baseString[x % size])
            x //= size
        return ''.join(l)

    def decode(s):
        return sum(d[ch] * size**i for (i,ch) in enumerate(s))

    return encode, decode

# Base 64 version:
encode,decode = make_encoder("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")

assert decode(encode(435346456456)) == 435346456456

これには、エンコーダーのベース文字列に適切な文字を追加するだけで、任意のベースを使用できるという利点があります。

ただし、より大きなベースのゲインはそれほど大きくないことに注意してください. base 64 は、base 16 の 2/3 にサイズを縮小するだけです (4 ではなく 6 ビット/文字)。2 倍にするたびに、文字ごとに 1 ビットだけ追加されます。本当に圧縮する必要がない限り、16 進数を使用するのがおそらく最も簡単で最速のオプションです。

于 2009-02-18T16:40:37.543 に答える
9

エンコードするにはn:

data = ''
while n > 0:
    data = chr(n & 255) + data
    n = n >> 8
encoded = base64.urlsafe_b64encode(data).rstrip('=')

デコードするにはs:

data = base64.urlsafe_b64decode(s + '===')
decoded = 0
while len(data) > 0:
    decoded = (decoded << 8) | ord(data[0])
    data = data[1:]

いくつかの「最適な」エンコーディングの他のものと同じ精神で、RFC 1738 に従って73文字を使用できます (「+」を使用可能と見なすと、実際には 74 文字です)。

alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_`\"!$'()*,-."
encoded = ''
while n > 0:
    n, r = divmod(n, len(alphabet))
    encoded = alphabet[r] + encoded

そしてデコード:

decoded = 0
while len(s) > 0:
    decoded = decoded * len(alphabet) + alphabet.find(s[0])
    s = s[1:]
于 2009-02-18T16:22:44.343 に答える
8

base64 エンコーディングは必要なく、基数 X で基数 10 の数値を表現したいとします。

10 進数を使用可能な 26 文字で表現したい場合は、 http://en.wikipedia.org/wiki/Hexavigesimalを使用できます。(すべての有効な URL 文字を使用して、より大きなベースの例を拡張できます)

少なくとも基数 38 (26 文字、10 数字、+、_) を取得できるはずです。

于 2009-02-18T15:48:31.570 に答える
8

簡単なのは、バイト文字列を Web セーフな base64 に変換することです。

import base64
output = base64.urlsafe_b64encode(s)

トリッキーなビットは最初のステップです - 整数をバイト文字列に変換します。

整数が小さい場合は、16 進数でエンコードしたほうがよいでしょう - sauaを参照してください

それ以外の場合 (ハック再帰バージョン):

def convertIntToByteString(i):
    if i == 0:
        return ""
    else:
        return convertIntToByteString(i >> 8) + chr(i & 255)
于 2009-02-18T15:48:31.897 に答える
4

Base64 は 3 バイトをエンコードするのに 4 バイト/文字を必要とし、3 バイトの倍数しかエンコードできません (それ以外の場合はパディングを追加します)。

したがって、Base64 で 4 バイト (平均 int) を表すには 8 バイトかかります。同じ 4 バイトを 16 進数でエンコードすると、8 バイトも必要になります。したがって、単一の int では何も得られません。

于 2009-02-18T15:33:18.453 に答える
3

少しハックですが、動作します:

def b64num(num_to_encode):
  h = hex(num_to_encode)[2:]     # hex(n) returns 0xhh, strip off the 0x
  h = len(h) & 1 and '0'+h or h  # if odd number of digits, prepend '0' which hex codec requires
  return h.decode('hex').encode('base64') 

.encode('base64') への呼び出しを urlsafe_b64encode() などの base64 モジュール内のものに置き換えることができます

于 2009-02-18T16:19:56.097 に答える
3

zbase62 という名前の小さなライブラリを維持しています: http://pypi.python.org/pypi/zbase62

これを使用すると、Python 2 の str オブジェクトから base-62 でエンコードされた文字列に、またはその逆に変換できます。

Python 2.7.1+ (r271:86832, Apr 11 2011, 18:13:53) 
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> d = os.urandom(32)
>>> d
'C$\x8f\xf9\x92NV\x97\x13H\xc7F\x0c\x0f\x8d9}\xf5.u\xeeOr\xc2V\x92f\x1b=:\xc3\xbc'
>>> from zbase62 import zbase62
>>> encoded = zbase62.b2a(d)
>>> encoded
'Fv8kTvGhIrJvqQ2oTojUGlaVIxFE1b6BCLpH8JfYNRs'
>>> zbase62.a2b(encoded)
'C$\x8f\xf9\x92NV\x97\x13H\xc7F\x0c\x0f\x8d9}\xf5.u\xeeOr\xc2V\x92f\x1b=:\xc3\xbc'

ただし、整数から str に変換する必要があります。これは Python 3 に組み込まれています。

Python 3.2 (r32:88445, Mar 25 2011, 19:56:22)
[GCC 4.5.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> d = os.urandom(32)
>>> d
b'\xe4\x0b\x94|\xb6o\x08\xe9oR\x1f\xaa\xa8\xe8qS3\x86\x82\t\x15\xf2"\x1dL%?\xda\xcc3\xe3\xba'
>>> int.from_bytes(d, 'big')
103147789615402524662804907510279354159900773934860106838120923694590497907642
>>> x= _ 
>>> x.to_bytes(32, 'big')
b'\xe4\x0b\x94|\xb6o\x08\xe9oR\x1f\xaa\xa8\xe8qS3\x86\x82\t\x15\xf2"\x1dL%?\xda\xcc3\xe3\xba'

Python 2 で int からバイトに、またはその逆に変換するには、私の知る限り、便利で標準的な方法はありません。便宜上、 https ://github.com/warner/foolscap/blob/46e3a041167950fa93e48f65dcf106a576ed110e/foolscap/banana.py#L41 のような実装を zbase62 にコピーする必要があると思います。

于 2011-07-06T20:08:32.487 に答える
2

base64 を使用して整数表現を短縮する方法を探している場合は、他の場所を探す必要があると思います。base64 で何かをエンコードすると、短くなることはなく、実際には長くなります。

たとえば、base64 でエンコードされた 11234 は MTEyMzQ= を生成します。

base64 を使用する場合、数字 (0 ~ 9) だけを 64 文字エンコーディングに変換していないという事実を見落としています。3 バイトを 4 バイトに変換しているので、base64 でエンコードされた文字列が 33.33% 長くなることが保証されます。

于 2009-02-18T15:35:18.340 に答える
2

符号付き整数が必要だったので、最終的には次のようになりました。

import struct, base64

def b64encode_integer(i):
   return base64.urlsafe_b64encode(struct.pack('i', i)).rstrip('=\n')

例:

>>> b64encode_integer(1)
'AQAAAA'
>>> b64encode_integer(-1)
'_____w'
>>> b64encode_integer(256)
'AAEAAA'
于 2011-08-10T13:23:09.913 に答える
2

このための pip パッケージの作成に取り組んでいます。

bases.jsに触発された私の bases.py https://github.com/kamijoutouma/bases.pyを使用することをお勧めします

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を参照してください

あなたの場合

ベース32、58、または64のいずれかを使用することをお勧めします

Base-64 の警告: いくつかの異なる規格があることに加えて、現在パディングは追加されておらず、行の長さは追跡されていません。正式な base-64 文字列を期待する API での使用はお勧めしません!

同じことがbase 66にも当てはまります.bases.jsとbase.pyの両方で現在サポートされていませんが、将来的にはサポートされる可能性があります.

于 2015-05-27T10:37:39.643 に答える
1

'整数をバイナリ文字列としてエンコードし、次にその'メソッドをbase64でエンコードし、structを使用して実行します。

>>> import struct, base64
>>> base64.b64encode(struct.pack('l', 47))
'LwAAAA=='
>>> struct.unpack('l', base64.b64decode(_))
(47,)

もう一度編集する:32ビットの完全な精度を必要とするには小さすぎる数値の余分な0を取り除くには、次のことを試してください。

def pad(str, l=4):
    while len(str) < l:
        str = '\x00' + str
    return str

>>> base64.b64encode(struct.pack('!l', 47).replace('\x00', ''))
'Lw=='
>>> struct.unpack('!l', pad(base64.b64decode('Lw==')))
(47,)
于 2009-02-18T15:27:18.580 に答える