URLで整数を表すための最短の方法が必要です。たとえば、11234は16進数を使用して「2be2」に短縮できます。 base64は64文字のエンコーディングを使用するため、16進数よりも少ない文字を使用してbase64で整数を表すことができるはずです。問題は、Pythonを使用して整数をbase64に変換する(そして再び元に戻す)ための最もクリーンな方法を理解できないことです。
Base64モジュールには、バイト文字列を処理するためのメソッドがあります。そのため、おそらく1つの解決策は、整数をバイナリ表現にPython string ...として変換することです...どちらか。
この回答は、精神的にダグラスリーダーの回答と似ていますが、次の点が異なります。
最初に数値をバイト文字列(基数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))
'$_____'
いくつかの補足事項:
Base64に関するすべての回答は非常に合理的なソリューションです。しかし、それらは技術的に正しくありません。整数を可能な最短のURLセーフ文字列に変換するには、ベース66が必要です( 66 URLセーフ文字があります )。
そのコードは次のようになります。
from io import StringIO
import urllib
BASE66_ALPHABET = u"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~"
BASE = len(BASE66_ALPHABET)
def hexahexacontadecimal_encode_int(n):
if n == 0:
return BASE66_ALPHABET[0].encode('ascii')
r = StringIO()
while n:
n, t = divmod(n, BASE)
r.write(BASE66_ALPHABET[t])
return r.getvalue().encode('ascii')[::-1]
これはこのようなスキームの完全な実装であり、pipのインストール可能なパッケージとして準備ができています。
あなたはおそらくこれのために本当の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ビット/文字)。倍加するたびに、文字ごとに1ビットが追加されます。本当にコンパクトにする必要がない限り、16進数を使用するのがおそらく最も簡単で最速のオプションです。
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に従って7文字を使用できます(「+」を使用可能とカウントした場合、実際には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:]
簡単なビットは、バイト文字列を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)
Base64エンコードは必要ありません。10進数を数値ベースXで表します。
使用可能な26文字で表される10を基数とする数値が必要な場合は、 http://en.wikipedia.org/wiki/Hexavigesimal を使用できます。 (すべての正当なURL文字を使用することにより、この例をより大きなベースに拡張できます)
少なくとも38進数(26文字、10桁の数字、+、_)を取得できる必要があります。
Base64は3バイトをエンコードするために4バイト/文字を取り、3バイトの倍数のみをエンコードできます(それ以外の場合はパディングを追加します)。
したがって、Base64で4バイト(平均int)を表すには8バイトかかります。同じ4バイトを16進数でエンコードすると、8バイトもかかります。したがって、1つのintに対しては何も得られません。
私は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に入れます。
少しハックですが、うまくいきます:
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モジュールの何かで置き換えることができます。
shortenを表現する方法を探している場合は、base64を使用した整数表現で、他の場所を調べる必要があると思います。 base64でエンコードした場合、短くはなりませんが、実際には長くなります。
例えば。 base64でエンコードされた11234はMTEyMzQ =を生成します
Base64を使用する場合、数字(0〜9)だけを64文字エンコードに変換しないという事実を見落としました。 3バイトを4バイトに変換しているので、base64でエンコードされた文字列が33.33%長くなることが保証されます。
符号付き整数が必要だったので、次のようになりました:
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'
このための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での使用は推奨されません!
同じことがbases.jsとbases.pyの両方で現在サポートされていないbase 66にも当てはまりますが、 future
私は「整数をバイナリ文字列としてエンコードし、次にbase64エンコードする」メソッドを提案し、構造体を使用してそれを行います。
>>> 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,)
純粋なpython、依存関係なし、バイト文字列のエンコードなしなど、正しいRFC 4648文字でbase 10 intをbase 64 intに変換するだけです:
def tetrasexagesimal(number):
out=""
while number>=0:
if number == 0:
out = 'A' + out
break
digit = number % 64
out = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[digit] + out
number /= 64 # //= 64 for py3 (thank spanishgum!)
if number == 0:
break
return out
tetrasexagesimal(1)